oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
3,400 bytes raw
1
use std::io;
2
use std::process::Command;
3
4
use serde::Deserialize;
5
use x11rb::connection::Connection;
6
use x11rb::protocol::xproto::*;
7
8
use crate::errors::X11Error;
9
10
#[derive(Debug, Copy, Clone, Deserialize)]
11
pub enum KeyAction {
12
    Spawn,
13
    KillClient,
14
    FocusStack,
15
    Quit,
16
    Restart,
17
    Recompile,
18
    ViewTag,
19
    ToggleGaps,
20
    ToggleFullScreen,
21
    ToggleFloating,
22
    ChangeLayout,
23
    CycleLayout,
24
    MoveToTag,
25
    FocusMonitor,
26
    SmartMoveWin,
27
    ExchangeClient,
28
    None,
29
}
30
31
#[derive(Debug, Clone)]
32
pub enum Arg {
33
    None,
34
    Int(i32),
35
    Str(String),
36
    Array(Vec<String>),
37
}
38
39
impl Arg {
40
    pub const fn none() -> Self {
41
        Arg::None
42
    }
43
}
44
45
#[derive(Clone)]
46
pub struct Key {
47
    pub(crate) modifiers: Vec<KeyButMask>,
48
    pub(crate) key: Keycode,
49
    pub(crate) func: KeyAction,
50
    pub(crate) arg: Arg,
51
}
52
53
impl Key {
54
    pub fn new(modifiers: Vec<KeyButMask>, key: Keycode, func: KeyAction, arg: Arg) -> Self {
55
        Self {
56
            modifiers,
57
            key,
58
            func,
59
            arg,
60
        }
61
    }
62
}
63
64
fn modifiers_to_mask(modifiers: &[KeyButMask]) -> u16 {
65
    modifiers
66
        .iter()
67
        .fold(0u16, |acc, &modifier| acc | u16::from(modifier))
68
}
69
70
pub fn setup_keybinds(
71
    connection: &impl Connection,
72
    root: Window,
73
    keybindings: &[Key],
74
) -> Result<(), X11Error> {
75
    for keybinding in keybindings {
76
        let modifier_mask = modifiers_to_mask(&keybinding.modifiers);
77
78
        connection.grab_key(
79
            false,
80
            root,
81
            modifier_mask.into(),
82
            keybinding.key,
83
            GrabMode::ASYNC,
84
            GrabMode::ASYNC,
85
        )?;
86
    }
87
    Ok(())
88
}
89
90
pub fn handle_key_press(event: KeyPressEvent, keybindings: &[Key]) -> (KeyAction, Arg) {
91
    for keybinding in keybindings {
92
        let modifier_mask = modifiers_to_mask(&keybinding.modifiers);
93
94
        if event.detail == keybinding.key && event.state == modifier_mask.into() {
95
            return (keybinding.func, keybinding.arg.clone());
96
        }
97
    }
98
99
    (KeyAction::None, Arg::None)
100
}
101
102
pub fn handle_spawn_action(action: KeyAction, arg: &Arg) -> io::Result<()> {
103
    use io::ErrorKind;
104
    if let KeyAction::Spawn = action {
105
        match arg {
106
            Arg::Str(command) => match Command::new(command.as_str()).spawn() {
107
                Err(err) if err.kind() == ErrorKind::NotFound => {
108
                    eprintln!(
109
                        "KeyAction::Spawn failed: could not spawn \"{}\", command not found",
110
                        command
111
                    );
112
                }
113
                Err(err) => Err(err)?,
114
                _ => (),
115
            },
116
            Arg::Array(command) => {
117
                let Some((cmd, args)) = command.split_first() else {
118
                    return Ok(());
119
                };
120
121
                let args_str: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
122
                match Command::new(cmd.as_str()).args(&args_str).spawn() {
123
                    Err(err) if err.kind() == ErrorKind::NotFound => {
124
                        eprintln!(
125
                            "KeyAction::Spawn failed: could not spawn \"{}\", command not found",
126
                            cmd
127
                        );
128
                    }
129
                    Err(err) => Err(err)?,
130
                    _ => (),
131
                }
132
            }
133
            _ => {}
134
        }
135
    }
136
137
    Ok(())
138
}