oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
8,673 bytes raw
1
use std::collections::HashMap;
2
use std::io::{ErrorKind, Result};
3
use std::process::Command;
4
5
use serde::Deserialize;
6
use x11rb::connection::Connection;
7
use x11rb::protocol::xproto::*;
8
9
use crate::errors::X11Error;
10
use crate::keyboard::keysyms::{self, Keysym};
11
12
#[derive(Debug, Copy, Clone, Deserialize, PartialEq)]
13
pub enum KeyAction {
14
    Spawn,
15
    SpawnTerminal,
16
    KillClient,
17
    FocusStack,
18
    FocusDirection,
19
    SwapDirection,
20
    Quit,
21
    Restart,
22
    Recompile,
23
    ViewTag,
24
    ToggleGaps,
25
    ToggleFullScreen,
26
    ToggleFloating,
27
    ChangeLayout,
28
    CycleLayout,
29
    MoveToTag,
30
    FocusMonitor,
31
    SmartMoveWin,
32
    ExchangeClient,
33
    ShowKeybindOverlay,
34
    None,
35
}
36
37
#[derive(Debug, Clone)]
38
pub enum Arg {
39
    None,
40
    Int(i32),
41
    Str(String),
42
    Array(Vec<String>),
43
}
44
45
impl Arg {
46
    pub const fn none() -> Self {
47
        Arg::None
48
    }
49
}
50
51
#[derive(Clone, Debug)]
52
pub struct KeyPress {
53
    pub(crate) modifiers: Vec<KeyButMask>,
54
    pub(crate) keysym: Keysym,
55
}
56
57
#[derive(Clone)]
58
pub struct KeyBinding {
59
    pub(crate) keys: Vec<KeyPress>,
60
    pub(crate) func: KeyAction,
61
    pub(crate) arg: Arg,
62
}
63
64
impl KeyBinding {
65
    pub fn new(keys: Vec<KeyPress>, func: KeyAction, arg: Arg) -> Self {
66
        Self { keys, func, arg }
67
    }
68
69
    pub fn single_key(
70
        modifiers: Vec<KeyButMask>,
71
        keysym: Keysym,
72
        func: KeyAction,
73
        arg: Arg,
74
    ) -> Self {
75
        Self {
76
            keys: vec![KeyPress { modifiers, keysym }],
77
            func,
78
            arg,
79
        }
80
    }
81
}
82
83
pub type Key = KeyBinding;
84
85
#[derive(Debug, Clone)]
86
pub enum KeychordState {
87
    Idle,
88
    InProgress {
89
        candidates: Vec<usize>,
90
        keys_pressed: usize,
91
    },
92
}
93
94
pub enum KeychordResult {
95
    Completed(KeyAction, Arg),
96
    InProgress(Vec<usize>),
97
    None,
98
    Cancelled,
99
}
100
101
pub fn modifiers_to_mask(modifiers: &[KeyButMask]) -> u16 {
102
    modifiers
103
        .iter()
104
        .fold(0u16, |acc, &modifier| acc | u16::from(modifier))
105
}
106
107
fn build_keysym_maps(
108
    connection: &impl Connection,
109
) -> std::result::Result<(HashMap<Keysym, Vec<Keycode>>, HashMap<Keycode, Keysym>), X11Error> {
110
    let setup = connection.setup();
111
    let min_keycode = setup.min_keycode;
112
    let max_keycode = setup.max_keycode;
113
114
    let keyboard_mapping = connection
115
        .get_keyboard_mapping(min_keycode, max_keycode - min_keycode + 1)?
116
        .reply()?;
117
118
    let mut keysym_to_keycode: HashMap<Keysym, Vec<Keycode>> = HashMap::new();
119
    let mut keycode_to_keysym: HashMap<Keycode, Keysym> = HashMap::new();
120
    let keysyms_per_keycode = keyboard_mapping.keysyms_per_keycode;
121
122
    for keycode in min_keycode..=max_keycode {
123
        let index = (keycode - min_keycode) as usize * keysyms_per_keycode as usize;
124
125
        for i in 0..keysyms_per_keycode as usize {
126
            if let Some(&keysym) = keyboard_mapping.keysyms.get(index + i) {
127
                if keysym != 0 {
128
                    keysym_to_keycode
129
                        .entry(keysym)
130
                        .or_insert_with(Vec::new)
131
                        .push(keycode);
132
                    keycode_to_keysym.entry(keycode).or_insert(keysym);
133
                }
134
            }
135
        }
136
    }
137
138
    Ok((keysym_to_keycode, keycode_to_keysym))
139
}
140
141
pub fn setup_keybinds(
142
    connection: &impl Connection,
143
    root: Window,
144
    keybindings: &[KeyBinding],
145
) -> std::result::Result<(), X11Error> {
146
    use std::collections::HashSet;
147
148
    let (keysym_to_keycode, _) = build_keysym_maps(connection)?;
149
    let mut grabbed_keys: HashSet<(u16, Keycode)> = HashSet::new();
150
151
    for keybinding in keybindings {
152
        if keybinding.keys.is_empty() {
153
            continue;
154
        }
155
156
        let first_key = &keybinding.keys[0];
157
        let modifier_mask = modifiers_to_mask(&first_key.modifiers);
158
159
        if let Some(keycodes) = keysym_to_keycode.get(&first_key.keysym) {
160
            if let Some(&keycode) = keycodes.first() {
161
                let key_tuple = (modifier_mask, keycode);
162
163
                if grabbed_keys.insert(key_tuple) {
164
                    connection.grab_key(
165
                        false,
166
                        root,
167
                        modifier_mask.into(),
168
                        keycode,
169
                        GrabMode::ASYNC,
170
                        GrabMode::ASYNC,
171
                    )?;
172
                }
173
            }
174
        }
175
    }
176
177
    Ok(())
178
}
179
180
pub fn handle_key_press(
181
    event: KeyPressEvent,
182
    keybindings: &[KeyBinding],
183
    keychord_state: &KeychordState,
184
    connection: &impl Connection,
185
) -> std::result::Result<KeychordResult, X11Error> {
186
    let (_, keycode_to_keysym) = build_keysym_maps(connection)?;
187
    let event_keysym = keycode_to_keysym.get(&event.detail).copied().unwrap_or(0);
188
189
    if event_keysym == keysyms::XK_ESCAPE {
190
        return Ok(match keychord_state {
191
            KeychordState::InProgress { .. } => KeychordResult::Cancelled,
192
            KeychordState::Idle => KeychordResult::None,
193
        });
194
    }
195
196
    Ok(match keychord_state {
197
        KeychordState::Idle => handle_first_key(event, event_keysym, keybindings),
198
        KeychordState::InProgress {
199
            candidates,
200
            keys_pressed,
201
        } => handle_next_key(event, event_keysym, keybindings, candidates, *keys_pressed),
202
    })
203
}
204
205
fn handle_first_key(
206
    event: KeyPressEvent,
207
    event_keysym: Keysym,
208
    keybindings: &[KeyBinding],
209
) -> KeychordResult {
210
    let mut candidates = Vec::new();
211
212
    for (keybinding_index, keybinding) in keybindings.iter().enumerate() {
213
        if keybinding.keys.is_empty() {
214
            continue;
215
        }
216
217
        let first_key = &keybinding.keys[0];
218
        let modifier_mask = modifiers_to_mask(&first_key.modifiers);
219
220
        if event_keysym == first_key.keysym && event.state == modifier_mask.into() {
221
            if keybinding.keys.len() == 1 {
222
                return KeychordResult::Completed(keybinding.func, keybinding.arg.clone());
223
            } else {
224
                candidates.push(keybinding_index);
225
            }
226
        }
227
    }
228
229
    if candidates.is_empty() {
230
        KeychordResult::None
231
    } else {
232
        KeychordResult::InProgress(candidates)
233
    }
234
}
235
236
fn handle_next_key(
237
    event: KeyPressEvent,
238
    event_keysym: Keysym,
239
    keybindings: &[KeyBinding],
240
    candidates: &[usize],
241
    keys_pressed: usize,
242
) -> KeychordResult {
243
    let mut new_candidates = Vec::new();
244
245
    for &candidate_index in candidates {
246
        let keybinding = &keybindings[candidate_index];
247
248
        if keys_pressed >= keybinding.keys.len() {
249
            continue;
250
        }
251
252
        let next_key = &keybinding.keys[keys_pressed];
253
        let required_mask = modifiers_to_mask(&next_key.modifiers);
254
        let event_state: u16 = event.state.into();
255
256
        let modifiers_match = if next_key.modifiers.is_empty() {
257
            true
258
        } else {
259
            (event_state & required_mask) == required_mask
260
        };
261
262
        if event_keysym == next_key.keysym && modifiers_match {
263
            if keys_pressed + 1 == keybinding.keys.len() {
264
                return KeychordResult::Completed(keybinding.func, keybinding.arg.clone());
265
            } else {
266
                new_candidates.push(candidate_index);
267
            }
268
        }
269
    }
270
271
    if new_candidates.is_empty() {
272
        KeychordResult::Cancelled
273
    } else {
274
        KeychordResult::InProgress(new_candidates)
275
    }
276
}
277
278
pub fn handle_spawn_action(action: KeyAction, arg: &Arg) -> Result<()> {
279
    if let KeyAction::Spawn = action {
280
        match arg {
281
            Arg::Str(command) => match Command::new(command.as_str()).spawn() {
282
                Err(error) if error.kind() == ErrorKind::NotFound => {
283
                    eprintln!(
284
                        "KeyAction::Spawn failed: could not spawn \"{}\", command not found",
285
                        command
286
                    );
287
                }
288
                Err(error) => Err(error)?,
289
                _ => (),
290
            },
291
            Arg::Array(command) => {
292
                let Some((cmd, args)) = command.split_first() else {
293
                    return Ok(());
294
                };
295
296
                let args_str: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
297
                match Command::new(cmd.as_str()).args(&args_str).spawn() {
298
                    Err(error) if error.kind() == ErrorKind::NotFound => {
299
                        eprintln!(
300
                            "KeyAction::Spawn failed: could not spawn \"{}\", command not found",
301
                            cmd
302
                        );
303
                    }
304
                    Err(error) => Err(error)?,
305
                    _ => (),
306
                }
307
            }
308
            _ => {}
309
        }
310
    }
311
312
    Ok(())
313
}