summaryrefslogtreecommitdiff
path: root/src/buttons.rs
diff options
context:
space:
mode:
authortcmal <me@aria.rip>2024-08-06 14:39:55 +0100
committertcmal <me@aria.rip>2024-08-13 17:56:36 +0100
commitd973959f7f34e02eeb01766b01aff680639a8c4c (patch)
tree155aab05ad70915d0ebdf509e828765c0bac2822 /src/buttons.rs
parent89b5f3f87c8022f2d32c422c3516fca2f1538d69 (diff)
Add support for mouse button binds
Diffstat (limited to 'src/buttons.rs')
-rw-r--r--src/buttons.rs111
1 files changed, 111 insertions, 0 deletions
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<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()
+ }
+}