Skip to content

Commit

Permalink
fix(linux/globalShortcut): disable on wayland (#543)
Browse files Browse the repository at this point in the history
Don't start the global shortcut thread on wayland, as its highly x11
specific and otherwise results in a segfault in libX11.
  • Loading branch information
ralf1307 committed Aug 26, 2022
1 parent 816ca49 commit 27ab6f4
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 100 deletions.
5 changes: 5 additions & 0 deletions .changes/linux-wayland.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tao": "patch"
---

Disables the global shortcut manager on wayland as its X11-specific.
204 changes: 104 additions & 100 deletions src/platform_impl/linux/global_shortcut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,126 +40,130 @@ impl ShortcutManager {
let (method_sender, thread_receiver) = channel::unbounded();
let (thread_sender, method_receiver) = channel::unbounded();

std::thread::spawn(move || {
let event_loop_channel = event_loop_channel.clone();
let xlib = xlib::Xlib::open().unwrap();
unsafe {
let display = (xlib.XOpenDisplay)(ptr::null());
let root = (xlib.XDefaultRootWindow)(display);
if !_window_target.p.is_wayland() {
std::thread::spawn(move || {
let event_loop_channel = event_loop_channel.clone();
let xlib = xlib::Xlib::open().unwrap();
unsafe {
let display = (xlib.XOpenDisplay)(ptr::null());
let root = (xlib.XDefaultRootWindow)(display);

// Only trigger key release at end of repeated keys
#[allow(clippy::uninit_assumed_init)]
let mut supported_rtrn: i32 = std::mem::MaybeUninit::uninit().assume_init();
(xlib.XkbSetDetectableAutoRepeat)(display, 1, &mut supported_rtrn);
// Only trigger key release at end of repeated keys
#[allow(clippy::uninit_assumed_init)]
let mut supported_rtrn: i32 = std::mem::MaybeUninit::uninit().assume_init();
(xlib.XkbSetDetectableAutoRepeat)(display, 1, &mut supported_rtrn);

(xlib.XSelectInput)(display, root, xlib::KeyReleaseMask);
#[allow(clippy::uninit_assumed_init)]
let mut event: xlib::XEvent = std::mem::MaybeUninit::uninit().assume_init();
(xlib.XSelectInput)(display, root, xlib::KeyReleaseMask);
#[allow(clippy::uninit_assumed_init)]
let mut event: xlib::XEvent = std::mem::MaybeUninit::uninit().assume_init();

loop {
let event_loop_channel = event_loop_channel.clone();
if (xlib.XPending)(display) > 0 {
(xlib.XNextEvent)(display, &mut event);
if let xlib::KeyRelease = event.get_type() {
let keycode = event.key.keycode;
// X11 sends masks for Lock keys also and we only care about the 4 below
let modifiers = event.key.state
& (xlib::ControlMask | xlib::ShiftMask | xlib::Mod4Mask | xlib::Mod1Mask);
if let Some(hotkey_id) = hotkey_map.lock().unwrap().get(&(keycode as i32, modifiers))
{
event_loop_channel
.send((window_id, WindowRequest::GlobalHotKey(*hotkey_id as u16)))
.unwrap();
loop {
let event_loop_channel = event_loop_channel.clone();
if (xlib.XPending)(display) > 0 {
(xlib.XNextEvent)(display, &mut event);
if let xlib::KeyRelease = event.get_type() {
let keycode = event.key.keycode;
// X11 sends masks for Lock keys also and we only care about the 4 below
let modifiers = event.key.state
& (xlib::ControlMask | xlib::ShiftMask | xlib::Mod4Mask | xlib::Mod1Mask);
if let Some(hotkey_id) =
hotkey_map.lock().unwrap().get(&(keycode as i32, modifiers))
{
event_loop_channel
.send((window_id, WindowRequest::GlobalHotKey(*hotkey_id as u16)))
.unwrap();
}
}
}
}

// XGrabKey works only with the exact state (modifiers)
// and since X11 considers NumLock, ScrollLock and CapsLock a modifier when it is ON,
// we also need to register our shortcut combined with these extra modifiers as well
const IGNORED_MODS: [u32; 4] = [
0, // modifier only
xlib::Mod2Mask, // NumLock
xlib::LockMask, // CapsLock
xlib::Mod2Mask | xlib::LockMask,
];
// XGrabKey works only with the exact state (modifiers)
// and since X11 considers NumLock, ScrollLock and CapsLock a modifier when it is ON,
// we also need to register our shortcut combined with these extra modifiers as well
const IGNORED_MODS: [u32; 4] = [
0, // modifier only
xlib::Mod2Mask, // NumLock
xlib::LockMask, // CapsLock
xlib::Mod2Mask | xlib::LockMask,
];

match thread_receiver.try_recv() {
Ok(HotkeyMessage::RegisterHotkey(_, modifiers, key)) => {
let keycode = (xlib.XKeysymToKeycode)(display, key.into()) as i32;
match thread_receiver.try_recv() {
Ok(HotkeyMessage::RegisterHotkey(_, modifiers, key)) => {
let keycode = (xlib.XKeysymToKeycode)(display, key.into()) as i32;

let mut result = 0;
for m in IGNORED_MODS {
result = (xlib.XGrabKey)(
display,
keycode,
modifiers | m,
root,
0,
xlib::GrabModeAsync,
xlib::GrabModeAsync,
);
}
if result == 0 {
if let Err(err) = thread_sender
.clone()
.send(HotkeyMessage::RegisterHotkeyResult(Err(
ShortcutManagerError::InvalidAccelerator(
"Unable to register accelerator".into(),
),
)))
{
let mut result = 0;
for m in IGNORED_MODS {
result = (xlib.XGrabKey)(
display,
keycode,
modifiers | m,
root,
0,
xlib::GrabModeAsync,
xlib::GrabModeAsync,
);
}
if result == 0 {
if let Err(err) = thread_sender
.clone()
.send(HotkeyMessage::RegisterHotkeyResult(Err(
ShortcutManagerError::InvalidAccelerator(
"Unable to register accelerator".into(),
),
)))
{
#[cfg(debug_assertions)]
eprintln!("hotkey: thread_sender.send error {}", err);
}
} else if let Err(err) = thread_sender.send(HotkeyMessage::RegisterHotkeyResult(
Ok((keycode, modifiers)),
)) {
#[cfg(debug_assertions)]
eprintln!("hotkey: thread_sender.send error {}", err);
}
} else if let Err(err) = thread_sender.send(HotkeyMessage::RegisterHotkeyResult(Ok(
(keycode, modifiers),
))) {
#[cfg(debug_assertions)]
eprintln!("hotkey: thread_sender.send error {}", err);
}
}
Ok(HotkeyMessage::UnregisterHotkey(id)) => {
let mut result = 0;
for m in IGNORED_MODS {
result = (xlib.XUngrabKey)(display, id.0, id.1 | m, root);
}
if result == 0 {
if let Err(err) = thread_sender
.clone()
.send(HotkeyMessage::UnregisterHotkeyResult(Err(
ShortcutManagerError::InvalidAccelerator(
"Unable to unregister accelerator".into(),
),
)))
Ok(HotkeyMessage::UnregisterHotkey(id)) => {
let mut result = 0;
for m in IGNORED_MODS {
result = (xlib.XUngrabKey)(display, id.0, id.1 | m, root);
}
if result == 0 {
if let Err(err) =
thread_sender
.clone()
.send(HotkeyMessage::UnregisterHotkeyResult(Err(
ShortcutManagerError::InvalidAccelerator(
"Unable to unregister accelerator".into(),
),
)))
{
#[cfg(debug_assertions)]
eprintln!("hotkey: thread_sender.send error {}", err);
}
} else if let Err(err) =
thread_sender.send(HotkeyMessage::UnregisterHotkeyResult(Ok(())))
{
#[cfg(debug_assertions)]
eprintln!("hotkey: thread_sender.send error {}", err);
}
} else if let Err(err) =
thread_sender.send(HotkeyMessage::UnregisterHotkeyResult(Ok(())))
{
#[cfg(debug_assertions)]
eprintln!("hotkey: thread_sender.send error {}", err);
}
}
Ok(HotkeyMessage::DropThread) => {
(xlib.XCloseDisplay)(display);
return;
}
Err(err) => {
if let TryRecvError::Disconnected = err {
#[cfg(debug_assertions)]
eprintln!("hotkey: try_recv error {}", err);
Ok(HotkeyMessage::DropThread) => {
(xlib.XCloseDisplay)(display);
return;
}
}
_ => unreachable!("other message should not arrive"),
};
Err(err) => {
if let TryRecvError::Disconnected = err {
#[cfg(debug_assertions)]
eprintln!("hotkey: try_recv error {}", err);
}
}
_ => unreachable!("other message should not arrive"),
};

std::thread::sleep(std::time::Duration::from_millis(50));
std::thread::sleep(std::time::Duration::from_millis(50));
}
}
}
});
});
}

ShortcutManager {
shortcuts: hotkeys,
Expand Down

0 comments on commit 27ab6f4

Please sign in to comment.