oxwm

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