summaryrefslogtreecommitdiff
path: root/src/buttons.rs
blob: a7d8a343c72f24c3fc61589ded5442bc2fbb5027 (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
//! Mouse button related code

use crate::{config::BUTTON_BINDS, conn_info::Connection, debug, WM};
use xcb::{
    x::{
        ButtonIndex, ButtonPressEvent, Cursor, EventMask, GrabButton, GrabMode, ModMask,
        UngrabButton, Window,
    },
    Xid,
};

impl WM<'_> {
    /// Dispatch the given button press event according to [`self::BUTTON_BINDS`]
    pub fn handle_button_press(&mut self, e: &ButtonPressEvent) {
        let button = match e.detail() {
            1 => ButtonIndex::N1,
            2 => ButtonIndex::N2,
            3 => ButtonIndex::N3,
            4 => ButtonIndex::N4,
            5 => ButtonIndex::N5,
            _ => return,
        };
        BUTTON_BINDS.dispatch(
            self,
            button,
            ModMask::from_bits_truncate(e.state().bits())
                .difference(self.conn.keyboard_state.numlock_mask() | ModMask::LOCK),
        );
    }
}

pub fn grab(conn: &Connection<'_>, window: Window, focused: bool) {
    conn.send_request(&UngrabButton {
        button: ButtonIndex::Any,
        grab_window: window,
        modifiers: ModMask::ANY,
    });

    if !focused {
        conn.send_request(&GrabButton {
            button: ButtonIndex::Any,
            modifiers: ModMask::ANY,
            grab_window: window,
            owner_events: false,
            event_mask: EventMask::BUTTON_PRESS | EventMask::BUTTON_RELEASE,
            pointer_mode: GrabMode::Sync,
            keyboard_mode: GrabMode::Sync,
            confine_to: Window::none(),
            cursor: Cursor::none(),
        });
    }

    for btn in BUTTON_BINDS.binds() {
        for modifiers in [
            ModMask::empty(),
            ModMask::LOCK,
            conn.keyboard_state.numlock_mask(),
            conn.keyboard_state.numlock_mask() | ModMask::LOCK,
        ] {
            conn.send_request(&GrabButton {
                button: btn.button,
                modifiers: btn.modifiers | modifiers,
                grab_window: window,
                owner_events: false,
                event_mask: EventMask::BUTTON_PRESS | EventMask::BUTTON_RELEASE,
                pointer_mode: GrabMode::Async,
                keyboard_mode: GrabMode::Sync,
                confine_to: Window::none(),
                cursor: Cursor::none(),
            });
        }
    }
}

/// A button bound to some action
pub struct ButtonBind {
    pub modifiers: ModMask,
    pub button: ButtonIndex,
    pub action: &'static dyn Fn(&mut WM<'_>),
}

/// A set of button binds. Currently, there is only one instance of this defined statically: [`crate::config::BUTTONBINDS`].
pub struct ButtonBinds(pub &'static [ButtonBind]);

impl ButtonBinds {
    /// Get an iterator over all bound buttons
    pub fn binds(&self) -> impl Iterator<Item = &ButtonBind> {
        self.0.iter()
    }

    /// Attempt to run the action for the matching buttonbind, if it exists.
    pub fn dispatch(&self, wm: &mut WM<'_>, button: ButtonIndex, modifiers: ModMask) {
        debug!("received {modifiers:?} {button:?}");
        if let Some(bind) = self
            .binds()
            .find(|b| b.button == button && modifiers == b.modifiers)
        {
            debug!("found action");
            (bind.action)(wm);
        }
    }
}

impl std::fmt::Debug for ButtonBind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("ButtonBind")
            .field("modifiers", &self.modifiers)
            .field("button", &self.button)
            .finish_non_exhaustive()
    }
}