From d973959f7f34e02eeb01766b01aff680639a8c4c Mon Sep 17 00:00:00 2001 From: tcmal Date: Tue, 6 Aug 2024 14:39:55 +0100 Subject: Add support for mouse button binds --- src/buttons.rs | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 src/buttons.rs (limited to 'src/buttons.rs') diff --git a/src/buttons.rs b/src/buttons.rs new file mode 100644 index 0000000..a7d8a34 --- /dev/null +++ b/src/buttons.rs @@ -0,0 +1,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 { + 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() + } +} -- cgit v1.2.3