Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(linux/globalShortcut): disable on wayland #543

Merged
merged 1 commit into from
Aug 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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