wzrd

An ICCCM & EWMH compliant X11 reparenting, dynamic window manager, written in Rust
git clone git://git.deurzen.net/wzrd
Log | Files | Refs | LICENSE

commit e88e3c9cebb17b196a5df74cc3c2355ecb316023
parent 33718f2901632bc44bdf650ebfe45349476f7159
Author: deurzen <m.deurzen@tum.de>
Date:   Tue, 16 Feb 2021 05:07:57 +0100

mouse binding backend refactoring

Diffstat:
Msrc/core/binding.rs | 3+--
Msrc/core/macros.rs | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/core/main.rs | 354++++++++++++++++++-------------------------------------------------------------
Msrc/core/model.rs | 10++++++----
Msrc/core/util.rs | 58+++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/winsys/input.rs | 2+-
Msrc/winsys/xdata/input.rs | 4++--
7 files changed, 220 insertions(+), 286 deletions(-)

diff --git a/src/core/binding.rs b/src/core/binding.rs @@ -2,14 +2,13 @@ use crate::model::Model; use winsys::common::Window; use winsys::input::KeyCode; -use winsys::input::MouseEvent; use winsys::input::MouseEventKey; use winsys::input::MouseShortcut; use std::collections::HashMap; pub type Action = Box<dyn FnMut(&mut Model)>; -pub type MouseEvents = Box<dyn FnMut(&mut Model, &MouseEvent, Option<Window>)>; +pub type MouseEvents = Box<dyn FnMut(&mut Model, Option<Window>)>; pub type KeyEvents = Box<dyn FnMut(&mut Model)>; pub type KeyBindings = HashMap<KeyCode, KeyEvents>; pub type MouseBindings = diff --git a/src/core/macros.rs b/src/core/macros.rs @@ -23,6 +23,37 @@ macro_rules! do_internal_block( ); #[macro_export] +macro_rules! do_nothing( + () => { + Box::new(|_: &mut $crate::model::Model, _| {}) as $crate::binding::MouseEvents + }; +); + +#[macro_export] +macro_rules! do_internal_mouse( + ($func:ident) => { + Box::new(|model: &mut $crate::model::Model, _| { + model.$func(); + }) as $crate::binding::MouseEvents + }; + + ($func:ident, $($arg:expr),+) => { + Box::new(|model: &mut $crate::model::Model, _| { + model.$func($($arg),+); + }) as $crate::binding::MouseEvents + }; +); + +#[macro_export] +macro_rules! do_internal_mouse_block( + ($model:ident, $window:ident, $body:block) => { + Box::new(|$model: &mut $crate::model::Model, $window: Option<winsys::common::Window>| { + $body + }) as $crate::binding::MouseEvents + }; +); + +#[macro_export] macro_rules! spawn_external( ($cmd:expr) => { { @@ -76,3 +107,47 @@ macro_rules! build_key_bindings( } }; ); + +#[macro_export] +macro_rules! build_mouse_bindings( + { @start $mouse_bindings:expr, + $( ( $kind:ident, $target:ident, $focus:expr ) : $binding:expr ),+ => $action:expr, + $($tail:tt)* + } => { + $( + match $crate::util::Util::parse_mouse_binding($binding) { + None => panic!("could not parse mouse binding: {}", $binding), + Some(shortcut) => $mouse_bindings.insert( + ( + winsys::input::MouseEventKey { + kind: winsys::input::MouseEventKind::$kind, + target: winsys::input::EventTarget::$target, + }, + shortcut + ), + ( + $action, + $focus + ) + ), + }; + )+ + build_mouse_bindings!(@start $mouse_bindings, $($tail)*); + }; + + { @start $mouse_bindings:expr, + $($tail:tt)* + } => { + $(compile_error!( + stringify!(incorrect syntax in build_mouse_bindings: $tail) + );)* + }; + + { $($tokens:tt)+ } => { + { + let mut mouse_bindings = std::collections::HashMap::new(); + build_mouse_bindings!(@start mouse_bindings, $($tokens)+); + mouse_bindings + } + }; +); diff --git a/src/core/main.rs b/src/core/main.rs @@ -9,16 +9,9 @@ use simplelog::LevelFilter; #[allow(unused_imports)] use simplelog::SimpleLogger; -pub use winsys::Result; - use winsys::common::Edge; -use winsys::input::Button; -use winsys::input::EventTarget; -use winsys::input::Modifier; -use winsys::input::MouseEventKey; -use winsys::input::MouseEventKind; -use winsys::input::MouseShortcut; use winsys::xdata::xconnection::XConnection; +pub use winsys::Result; #[macro_use] mod macros; @@ -65,285 +58,93 @@ pub fn main() -> Result<()> { } fn init_bindings() -> (MouseBindings, KeyBindings) { - let mut mouse_bindings = MouseBindings::new(); - - let modkey = if cfg!(debug_assertions) { - Modifier::Alt - } else { - Modifier::Meta - }; - - mouse_bindings.insert( - ( - MouseEventKey { - kind: MouseEventKind::Press, - target: EventTarget::Client, - }, - MouseShortcut::new(Button::Left, vec![modkey]), - ), - ( - Box::new(|m, _, w| { - if let Some(w) = w { - m.start_moving(w); - } - }), - true, - ), - ); - - mouse_bindings.insert( - ( - MouseEventKey { - kind: MouseEventKind::Press, - target: EventTarget::Client, - }, - MouseShortcut::new(Button::Right, vec![modkey]), - ), - ( - Box::new(|m, _, w| { - if let Some(w) = w { - m.start_resizing(w); - } - }), - true, - ), - ); - - mouse_bindings.insert( - ( - MouseEventKey { - kind: MouseEventKind::Press, - target: EventTarget::Client, - }, - MouseShortcut::new(Button::Middle, vec![modkey]), - ), - ( - Box::new(|m, _, w| { - if let Some(w) = w { - m.center_client(w); - } - }), - true, - ), - ); - - mouse_bindings.insert( - ( - MouseEventKey { - kind: MouseEventKind::Press, - target: EventTarget::Client, - }, - MouseShortcut::new(Button::Right, vec![modkey, Modifier::Ctrl]), - ), - ( - Box::new(|m, _, w| { - if let Some(w) = w { - m.toggle_float_client(w); - } - }), - true, - ), - ); - - mouse_bindings.insert( - ( - MouseEventKey { - kind: MouseEventKind::Press, - target: EventTarget::Client, - }, - MouseShortcut::new(Button::Middle, vec![ - modkey, - Modifier::Ctrl, - Modifier::Shift, - ]), - ), - ( - Box::new(|m, _, w| { - if let Some(w) = w { - m.toggle_fullscreen_client(w); - } - }), - true, - ), - ); - - mouse_bindings.insert( - ( - MouseEventKey { - kind: MouseEventKind::Press, - target: EventTarget::Client, - }, - MouseShortcut::new(Button::ScrollDown, vec![ - modkey, - Modifier::Ctrl, - Modifier::Shift, - ]), - ), - ( - Box::new(|m, _, w| { - if let Some(w) = w { - m.grow_ratio_client(w, -15); - } - }), - false, - ), - ); - - mouse_bindings.insert( - ( - MouseEventKey { - kind: MouseEventKind::Press, - target: EventTarget::Client, - }, - MouseShortcut::new(Button::ScrollUp, vec![ - modkey, - Modifier::Ctrl, - Modifier::Shift, - ]), - ), - ( - Box::new(|m, _, w| { - if let Some(w) = w { - m.grow_ratio_client(w, 15); - } - }), - false, - ), - ); - - mouse_bindings.insert( - ( - MouseEventKey { - kind: MouseEventKind::Press, - target: EventTarget::Client, - }, - MouseShortcut::new(Button::Forward, vec![modkey]), - ), - ( - Box::new(|m, _, w| { - if let Some(w) = w { - m.move_client_to_next_workspace(w); - } - }), - false, - ), - ); - - mouse_bindings.insert( - ( - MouseEventKey { - kind: MouseEventKind::Press, - target: EventTarget::Client, - }, - MouseShortcut::new(Button::Backward, vec![modkey]), - ), - ( - Box::new(|m, _, w| { - if let Some(w) = w { - m.move_client_to_prev_workspace(w); - } - }), - false, - ), - ); - - mouse_bindings.insert( - ( - MouseEventKey { - kind: MouseEventKind::Press, - target: EventTarget::Global, - }, - MouseShortcut::new(Button::ScrollDown, vec![modkey]), - ), - ( - Box::new(|m, _, _| { - m.cycle_focus(Direction::Forward); - }), - false, - ), - ); + // (kind, target, focus): "[modifiers]-button" => action + let mouse_bindings = build_mouse_bindings!( + // client state modifiers + (Press, Client, true): + "1-C-Right" => do_internal_mouse_block!(model, window, { + if let Some(window) = window { + model.toggle_float_client(window); + } + }), + (Press, Client, true): + "1-C-S-Middle" => do_internal_mouse_block!(model, window, { + if let Some(window) = window { + model.toggle_fullscreen_client(window); + } + }), - mouse_bindings.insert( - ( - MouseEventKey { - kind: MouseEventKind::Press, - target: EventTarget::Global, - }, - MouseShortcut::new(Button::ScrollUp, vec![modkey]), - ), - ( - Box::new(|m, _, _| { - m.cycle_focus(Direction::Backward); - }), - false, - ), - ); + // free client arrangers + (Press, Client, true): + "1-Middle" => do_internal_mouse_block!(model, window, { + if let Some(window) = window { + model.center_client(window); + } + }), + (Press, Client, true): + "1-Left" => do_internal_mouse_block!(model, window, { + if let Some(window) = window { + model.start_moving(window); + } + }), + (Press, Client, true): + "1-Right" => do_internal_mouse_block!(model, window, { + if let Some(window) = window { + model.start_resizing(window); + } + }), + (Press, Client, false): + "1-C-S-ScrollDown" => do_internal_mouse_block!(model, window, { + if let Some(window) = window { + model.grow_ratio_client(window, -15); + } + }), + (Press, Client, false): + "1-C-S-ScrollUp" => do_internal_mouse_block!(model, window, { + if let Some(window) = window { + model.grow_ratio_client(window, 15); + } + }), - mouse_bindings.insert( - ( - MouseEventKey { - kind: MouseEventKind::Press, - target: EventTarget::Global, - }, - MouseShortcut::new(Button::ScrollDown, vec![ - modkey, - Modifier::Shift, - ]), - ), - ( - Box::new(|m, _, _| { - m.activate_next_workspace(); - }), - false, - ), - ); + // client order modifiers + (Press, Global, false): + "1-ScrollDown" => do_internal_mouse!(cycle_focus, Direction::Forward), + (Press, Global, false): + "1-ScrollUp" => do_internal_mouse!(cycle_focus, Direction::Backward), - mouse_bindings.insert( - ( - MouseEventKey { - kind: MouseEventKind::Press, - target: EventTarget::Global, - }, - MouseShortcut::new(Button::ScrollUp, vec![modkey, Modifier::Shift]), - ), - ( - Box::new(|m, _, _| { - m.activate_prev_workspace(); - }), - false, - ), - ); + // workspace activators + (Press, Global, false): + "1-S-ScrollDown" => do_internal_mouse!(activate_next_workspace), + (Press, Global, false): + "1-S-ScrollUp" => do_internal_mouse!(activate_prev_workspace), - mouse_bindings.insert( - ( - MouseEventKey { - kind: MouseEventKind::Release, - target: EventTarget::Global, - }, - MouseShortcut::new(Button::ScrollDown, vec![modkey]), - ), - (Box::new(|_, _, _| {}), false), - ); + // workspace client movement + (Press, Client, false): + "1-Forward" => do_internal_mouse_block!(model, window, { + if let Some(window) = window { + model.move_client_to_next_workspace(window); + } + }), + (Press, Client, false): + "1-Backward" => do_internal_mouse_block!(model, window, { + if let Some(window) = window { + model.move_client_to_prev_workspace(window); + } + }), - mouse_bindings.insert( - ( - MouseEventKey { - kind: MouseEventKind::Release, - target: EventTarget::Global, - }, - MouseShortcut::new(Button::ScrollUp, vec![modkey]), - ), - (Box::new(|_, _, _| {}), false), + // NOPs + (Release, Global, false): + "1-ScrollDown" => do_nothing!(), + (Release, Global, false): + "1-ScrollUp" => do_nothing!(), ); + // "[modifiers]-key" => action let key_bindings = build_key_bindings!( "1-C-S-q" => do_internal!(exit), - // client manipulators - "1-c" => do_internal!(kill_focus), - "1-C-space" => do_internal!(center_focus), - // client state modifiers + "1-c" => do_internal!(kill_focus), "1-S-space" => do_internal!(toggle_float_focus), "1-f" => do_internal!(toggle_fullscreen_focus), "1-x" => do_internal!(toggle_stick_focus), @@ -358,6 +159,7 @@ fn init_bindings() -> (MouseBindings, KeyBindings) { }), // free client arrangers + "1-C-space" => do_internal!(center_focus), "1-C-h" => do_internal!(nudge_focus, Edge::Left, 15), "1-C-j" => do_internal!(nudge_focus, Edge::Bottom, 15), "1-C-k" => do_internal!(nudge_focus, Edge::Top, 15), diff --git a/src/core/model.rs b/src/core/model.rs @@ -575,9 +575,11 @@ impl<'a> Model<'a> { instance: &str, ) -> Rules { const PREFIX: &str = &concat!(WM_NAME!(), ":"); + const PREFIX_LEN: usize = PREFIX.len(); + let mut rules: Rules = Default::default(); - match (instance.get(..4), instance.get(4..)) { + match (instance.get(..PREFIX_LEN), instance.get(PREFIX_LEN..)) { (Some(PREFIX), flags) => { if let Some(flags) = flags { let mut invert = false; @@ -2669,7 +2671,7 @@ impl<'a> Model<'a> { )); if let Some((action, moves_focus)) = binding { - action(self, &event, None); + action(self, None); if *moves_focus { // TODO: config.focus_follows_mouse @@ -2698,7 +2700,7 @@ impl<'a> Model<'a> { )); if let Some((action, _)) = binding { - action(self, &event, None); + action(self, None); } return; @@ -2717,7 +2719,7 @@ impl<'a> Model<'a> { if let Some(window) = self.window(window) { if let Some((action, moves_focus)) = binding { - action(self, &event, Some(window)); + action(self, Some(window)); if *moves_focus { // TODO: config.focus_follows_mouse diff --git a/src/core/util.rs b/src/core/util.rs @@ -1,8 +1,11 @@ use crate::common::Change; use crate::common::Index; +use winsys::input::Button; use winsys::input::CodeMap; use winsys::input::KeyCode; +use winsys::input::Modifier; +use winsys::input::MouseShortcut; use std::cmp::Ord; use std::hash::BuildHasherDefault; @@ -155,7 +158,7 @@ impl Util { "A" | "Alt" | "Meta" => u16::from(ModMask::M1), "M" | "Super" => u16::from(ModMask::M4), "S" | "Shift" => u16::from(ModMask::SHIFT), - "C" | "Control" => u16::from(ModMask::CONTROL), + "C" | "Ctrl" | "Control" => u16::from(ModMask::CONTROL), "1" | "Mod" => u16::from(if cfg!(debug_assertions) { ModMask::M1 } else { @@ -178,4 +181,57 @@ impl Util { None => None, } } + + pub fn parse_mouse_binding( + mouse_binding: impl Into<String> + ) -> Option<MouseShortcut> { + let s = mouse_binding.into(); + let mut constituents: Vec<&str> = s.split('-').collect(); + + let button = match constituents.remove(constituents.len() - 1) { + "1" | "Left" => Button::Left, + "2" | "Middle" => Button::Middle, + "3" | "Right" => Button::Right, + "4" | "ScrollUp" => Button::ScrollUp, + "5" | "ScrollDown" => Button::ScrollDown, + "8" | "Backward" => Button::Backward, + "9" | "Forward" => Button::Forward, + s => panic!("invalid button: {}", s), + }; + + let mut modifiers = constituents + .iter() + .map(|&modifier| match modifier { + "A" | "Alt" | "Meta" => Modifier::Alt, + "AGr" | "AltGr" => Modifier::AltGr, + "M" | "Super" => Modifier::Super, + "S" | "Shift" => Modifier::Shift, + "C" | "Ctrl" | "Control" => Modifier::Ctrl, + "N" | "NumLock" => Modifier::NumLock, + "L" | "ScrollLock" => Modifier::ScrollLock, + "1" | "Mod" => { + if cfg!(debug_assertions) { + Modifier::Alt + } else { + Modifier::Super + } + }, + "2" | "Sec" => { + if cfg!(debug_assertions) { + Modifier::Super + } else { + Modifier::Alt + } + }, + _ => panic!("invalid modifier: {}", s), + }) + .collect::<Vec<Modifier>>(); + + modifiers.sort(); + + Some(MouseShortcut { + button, + modifiers, + }) + } } diff --git a/src/winsys/input.rs b/src/winsys/input.rs @@ -35,7 +35,7 @@ pub enum Modifier { Shift, Alt, AltGr, - Meta, + Super, NumLock, ScrollLock, } diff --git a/src/winsys/xdata/input.rs b/src/winsys/xdata/input.rs @@ -84,7 +84,7 @@ impl From<Modifier> for u16 { Modifier::Ctrl => ModMask::CONTROL, Modifier::Shift => ModMask::SHIFT, Modifier::Alt => ModMask::M1, - Modifier::Meta => ModMask::M4, + Modifier::Super => ModMask::M4, Modifier::AltGr => ModMask::M3, Modifier::NumLock => ModMask::M2, Modifier::ScrollLock => ModMask::M5, @@ -100,7 +100,7 @@ impl TryFrom<&str> for Modifier { "C" => Ok(Self::Ctrl), "A" => Ok(Self::Alt), "S" => Ok(Self::Shift), - "M" => Ok(Self::Meta), + "M" => Ok(Self::Super), "AltGr" => Ok(Self::Alt), "Num" => Ok(Self::NumLock), "Scroll" => Ok(Self::ScrollLock),