yazgoo / umberwm

:ram: a minimalistic X window manager based on tinywm, inspired by qtile.
57 stars 3 forks source link

Allowing more complicated key bindings #24

Open mfdorst opened 3 years ago

mfdorst commented 3 years ago

I spent a lot of yesterday trying to figure out how to add more flexible keybindings. For instance, I want to make the keybinding for quitting Shift+Alt+Q, because I'm much less likely to hit that by accident.

I'm writing this issue because it's proving a little more complicated than I thought it would be, so I don't have it working yet. This issue will help me track my research efforts, and if anyone wants to chime in with their expertise, that would be most welcome.

Currently umberwm does keybindings quite differently than other window managers. It reads in xmodmap -pke to generate a map of strings to keycodes. LeftWM uses x11_dl::keysym, and a really long match statement (LeftWM src: xkeysym_lookup.rs) to convert strings to keysyms. Here is the X documentation for keyboard utility functions, which let you convert between keysyms, keycodes and strings. Presumably these functions are available through xcb. I wonder if LeftWM could make use of XStringToKeysym() to avoid that long match statement. I'll try to do that here to avoid adding over 1300 lines worth of match statement to this repo lol.

In dwm, each keybinding has a mod mask (mod), a keysym (keysym), and an action (func and arg) (src).

An XKeyEvent has members state and keycode (among other things). To my understanding, keycode will be the non-mod key, while state will be the mod mask. Possibly keycode events get generated where keycode is a mod key, but those get ignored by the key handler because there won't be any keybindings set where the keycode is a mod key. I could be totally wrong about this.

In dwm, the keypress handler just loops over the keybindings and finds one where the event keycode (converted using XKeycodeToKeysym() matches the keybinding's keysym, and event's state matches the keybinding's mod (mod mask). Both the event state and the keybinding's mod mask are run through the CLEANMASK() macro, which appears to strip out any set bits other than the ones we care about, including numlock and capslock. While I've found documentation for caps lock, I can't seem to find any documentation on numlock in X. There is this baffling function updatenumlockmask() which I can't make heads nor tails of.

I think my missing knowledge of numlock will probably be my biggest obstacle right now. Other than that I think I have the knowledge to get this done. I'll try to implement it ignoring numlock, and if it becomes a problem I'll do more research.

mfdorst commented 3 years ago

Came across this while reading the LeftWM source: https://github.com/leftwm/leftwm/blob/master/src/utils/xkeysym_lookup.rs#L34

pub fn into_mod(key: &str) -> ModMask {
    match key {
        "None" => xlib::AnyModifier,
        "Shift" => xlib::ShiftMask,
        "Control" => xlib::ControlMask,
        "Mod1" | "Alt" => xlib::Mod1Mask,
        //"Mod2" => xlib::Mod2Mask,     // NOTE: we are ignoring the state of Numlock
        //"NumLock" => xlib::Mod2Mask,  // this is left here as a reminder
        "Mod3" => xlib::Mod3Mask,
        "Mod4" | "Super" => xlib::Mod4Mask,
        "Mod5" => xlib::Mod5Mask,
        _ => 0,
    }
}

which seems to indicate that Mod2 is numlock.

mfdorst commented 3 years ago

Turns out you don't need to change the way keycodes are looked up to make this work. I was spending all that time trying to do it the way LeftWM does it, but their way is fundamentally incompatible because they use xlib where we use xcb, and xcb does not have the ability to convert between keysyms and keycodes.

In the end I kept it simple and it works. See #26.

mfdorst commented 3 years ago

I guess this is also related to #2.