Skip to content

Commit

Permalink
Fix Hotkey Modifiers on X11 (#762)
Browse files Browse the repository at this point in the history
Turns out that X11 is very strict about the hotkey modifiers. They need
to match exactly, otherwise the hotkey is not recognized. The problem is
that there's various "lock" modifiers, such as the caps lock, num lock,
and scroll lock, that are always considered for this, but are not really
keys that you explicitly press and instead are active or inactive until
you toggle them. And they need to match exactly as well. Whoever thought
that's a good design should stop doing so immediately.
  • Loading branch information
CryZe authored Jan 22, 2024
1 parent 9bafdb0 commit f803c68
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 13 deletions.
4 changes: 2 additions & 2 deletions capi/bind_gen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub struct Function {

impl Function {
fn is_static(&self) -> bool {
if let Some((name, _)) = self.inputs.get(0) {
if let Some((name, _)) = self.inputs.first() {
name != "this"
} else {
true
Expand Down Expand Up @@ -277,7 +277,7 @@ fn fns_to_classes(functions: Vec<Function>) -> BTreeMap<String, Class> {

class.comments = function.class_comments.clone();

match function.inputs.get(0) {
match function.inputs.first() {
Some((name, ty)) if name == "this" => match ty.kind {
TypeKind::Value => class.own_fns.push(function),
TypeKind::Ref => class.shared_fns.push(function),
Expand Down
52 changes: 41 additions & 11 deletions crates/livesplit-hotkey/src/linux/x11_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use std::{

use mio::{unix::SourceFd, Events, Interest, Poll, Token, Waker};
use x11_dl::xlib::{
AnyKey, AnyModifier, ControlMask, Display, GrabModeAsync, KeyPress, Mod1Mask, Mod4Mask,
ShiftMask, XErrorEvent, XKeyEvent, Xlib, _XDisplay,
AnyKey, AnyModifier, ControlMask, Display, GrabModeAsync, KeyPress, LockMask, Mod1Mask,
Mod2Mask, Mod3Mask, Mod4Mask, ShiftMask, XErrorEvent, XKeyEvent, Xlib, _XDisplay,
};

use super::{Error, Hook, Message};
Expand All @@ -26,6 +26,33 @@ unsafe fn ungrab_all(xlib: &Xlib, display: *mut Display) {
}
}

const fn mask(x: c_uint) -> u8 {
if x < 256 {
x as u8
} else {
panic!()
}
}

const CAPS_LOCK: u8 = mask(LockMask);
const NUM_LOCK: u8 = mask(Mod2Mask);
const SCROLL_LOCK: u8 = mask(Mod3Mask);

// X11 has no way of ignoring additional modifiers, so we have to register every
// single combination of them. We are mostly only interested in the lock keys,
// because they may be in the wrong state without you actively interacting with
// them.
const IGNORE_MASKS: [u8; 8] = [
0,
CAPS_LOCK,
NUM_LOCK,
NUM_LOCK | CAPS_LOCK,
SCROLL_LOCK,
SCROLL_LOCK | CAPS_LOCK,
SCROLL_LOCK | NUM_LOCK,
SCROLL_LOCK | NUM_LOCK | CAPS_LOCK,
];

unsafe fn grab_all(xlib: &Xlib, display: *mut Display, keylist: &[(c_uint, Modifiers)]) {
ungrab_all(xlib, display);
let screencount = (xlib.XScreenCount)(display);
Expand All @@ -45,15 +72,18 @@ unsafe fn grab_all(xlib: &Xlib, display: *mut Display, keylist: &[(c_uint, Modif
if modifiers.contains(Modifiers::META) {
mod_mask |= Mod4Mask;
}
(xlib.XGrabKey)(
display,
code as c_int,
mod_mask,
rootwindow,
false as _,
GrabModeAsync,
GrabModeAsync,
);

for ignore_mask in IGNORE_MASKS {
(xlib.XGrabKey)(
display,
code as c_int,
mod_mask | (ignore_mask as c_uint),
rootwindow,
false as _,
GrabModeAsync,
GrabModeAsync,
);
}
}
}
}
Expand Down

0 comments on commit f803c68

Please sign in to comment.