summaryrefslogtreecommitdiff
path: root/src/keys.rs
blob: 08dd0566fad71c81647f8c43e5167fe0c5c61870 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use crate::{error::*, WM};
use xcb::x::{
    GetKeyboardMapping, GetModifierMapping, GrabKey, GrabMode, KeyPressEvent, Keycode, Keysym,
    Mapping, MappingNotifyEvent, ModMask, UngrabKey, GRAB_ANY,
};

// https://github.com/D-Programming-Deimos/libX11/blob/master/c/X11/keysymdef.h
const KEYSYM_NUMLOCK: Keysym = 0xff7f;

impl WM<'_> {
    pub(crate) fn handle_key_press(&mut self, e: KeyPressEvent) -> Result<()> {
        dbg!(e);

        Ok(())
    }

    pub(crate) fn handle_mapping_notify(&mut self, e: MappingNotifyEvent) -> Result<()> {
        if e.request() == Mapping::Keyboard {
            self.grabkeys()?;
        }

        Ok(())
    }

    pub(crate) fn grabkeys(&self) -> Result<()> {
        let numlock_mask = self.get_numlock_mask()?;

        // ungrab all keys
        self.conn.send_request(&UngrabKey {
            key: GRAB_ANY,
            grab_window: self.root,
            modifiers: ModMask::ANY,
        });

        // go through keymap and grab what we need
        let (min_keycode, max_keycode) = (
            self.conn.get_setup().min_keycode(),
            self.conn.get_setup().max_keycode(),
        );
        let mapping = self
            .conn
            .wait_for_reply(self.conn.send_request(&GetKeyboardMapping {
                first_keycode: min_keycode,
                count: max_keycode - min_keycode + 1,
            }))?;

        for shift in 0..mapping.keysyms_per_keycode() {
            for keycode in min_keycode..max_keycode {
                let keysym = mapping.keysyms()
                    [(shift as usize * mapping.keysyms_per_keycode() as usize + keycode as usize)];
                if true {
                    // todo: is bound
                    // grab key with any combination of modifiers
                    for modmask in [
                        ModMask::empty(),
                        ModMask::LOCK,
                        numlock_mask,
                        numlock_mask | ModMask::LOCK,
                    ] {
                        self.conn.send_request(&GrabKey {
                            grab_window: self.root,
                            key: keycode,
                            modifiers: modmask,
                            owner_events: true,
                            pointer_mode: GrabMode::Async,
                            keyboard_mode: GrabMode::Async,
                        });
                    }
                }
            }
        }

        // ensure all requests succeeded
        self.conn.flush()?;

        Ok(())
    }

    pub(crate) fn get_numlock_mask(&self) -> Result<ModMask> {
        let map = self
            .conn
            .wait_for_reply(self.conn.send_request(&GetModifierMapping {}))?;
        let numlock_keycode = self.keysym_to_keycode(KEYSYM_NUMLOCK)?;

        let keypermod = map.keycodes().len() / 8;
        for i in 0..8 {
            for j in 0..keypermod {
                if map.keycodes()[(i * keypermod + j) as usize] == numlock_keycode {
                    return Ok(ModMask::from_bits(1 << i).expect("x11 has unrecognised modifier"));
                }
            }
        }

        // todo: is this ok?
        return Ok(ModMask::empty());
    }

    pub(crate) fn keysym_to_keycode(&self, keysym: Keysym) -> Result<Keycode> {
        let (min_keycode, max_keycode) = (
            self.conn.get_setup().min_keycode(),
            self.conn.get_setup().max_keycode(),
        );
        let mapping = self
            .conn
            .wait_for_reply(self.conn.send_request(&GetKeyboardMapping {
                first_keycode: min_keycode,
                count: max_keycode - min_keycode + 1,
            }))?;

        // todo: apparently there's an optimisation here https://github.com/ArcherPergande/xcbkeys/blob/main/xcbkeys.h
        for shift in 0..mapping.keysyms_per_keycode() {
            for keycode in min_keycode..max_keycode {
                if mapping.keysyms()
                    [keycode as usize * mapping.keysyms_per_keycode() as usize + shift as usize]
                    == keysym
                {
                    return Ok(keycode);
                }
            }
        }

        // todo: is this right?
        return Ok(0);
    }
}