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 e7081b91130adb07f42687288b1d8fdeb8cae5f6
parent 8b7bfcc67fc84ec5aa4e86a859741a74a4e65456
Author: deurzen <m.deurzen@tum.de>
Date:   Sun, 20 Jun 2021 04:43:41 +0200

input handling overhaul

Diffstat:
Msrc/core/binding.rs | 13++++++-------
Msrc/core/macros.rs | 9+++++++++
Msrc/core/main.rs | 654++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/core/model.rs | 139++++++++++++++++++++++++++++++++++---------------------------------------------
Msrc/core/util.rs | 199+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/winsys/connection.rs | 7++++---
Msrc/winsys/event.rs | 5+++--
Msrc/winsys/geometry.rs | 2+-
Msrc/winsys/input.rs | 330+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/winsys/xdata/event.rs | 1-
Msrc/winsys/xdata/input.rs | 169++++++++++++-------------------------------------------------------------------
Msrc/winsys/xdata/xconnection.rs | 628+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
12 files changed, 1461 insertions(+), 695 deletions(-)

diff --git a/src/core/binding.rs b/src/core/binding.rs @@ -1,13 +1,12 @@ use crate::model::Model; -use winsys::input::KeyCode; -use winsys::input::MouseEventKey; -use winsys::input::MouseShortcut; +use winsys::input::KeyInput; +use winsys::input::MouseInput; use winsys::window::Window; use std::collections::HashMap; -pub type KeyAction = Box<dyn FnMut(&mut Model<'_>)>; -pub type MouseAction = Box<dyn FnMut(&mut Model<'_>, Option<Window>)>; -pub type KeyBindings = HashMap<KeyCode, KeyAction>; -pub type MouseBindings = HashMap<(MouseEventKey, MouseShortcut), (MouseAction, bool)>; +pub type KeyAction = fn(&mut Model<'_>); +pub type MouseAction = fn(&mut Model<'_>, Option<Window>) -> bool; +pub type KeyBindings = HashMap<KeyInput, KeyAction>; +pub type MouseBindings = HashMap<MouseInput, MouseAction>; diff --git a/src/core/macros.rs b/src/core/macros.rs @@ -1,4 +1,13 @@ #[macro_export] +macro_rules! hashset { + ($( $val: expr ),*) => {{ + let mut set = ::std::collections::HashSet::new(); + $( set.insert($val); )* + set + }} +} + +#[macro_export] macro_rules! call( ($($method:tt)+) => { |arg| $($method)+(arg) diff --git a/src/core/main.rs b/src/core/main.rs @@ -24,6 +24,9 @@ use winsys::geometry::Edge; use winsys::xdata::xconnection::XConnection; pub use winsys::Result; +use std::collections::HashMap; +use std::collections::HashSet; + #[macro_use] mod macros; @@ -59,6 +62,14 @@ use compare::MatchMethod; use jump::JumpCriterium; use layout::LayoutKind; use model::Model; +use winsys::input::Button; +use winsys::input::Key; +use winsys::input::KeyInput; +use winsys::input::Modifier; +use winsys::input::MouseEventKind; +use winsys::input::MouseInput; +use winsys::input::MouseInputTarget; +use winsys::window::Window; use workspace::ClientSelector; pub fn main() -> Result<()> { @@ -79,288 +90,427 @@ pub fn main() -> Result<()> { } fn init_bindings() -> (MouseBindings, KeyBindings) { - // (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, { + let mut mouse_bindings = MouseBindings::new(); + let mut key_bindings = KeyBindings::new(); + + mouse_bindings.insert( + MouseInput { + target: MouseInputTarget::Client, + button: Button::Right, + modifiers: hashset!(Modifier::Alt, Modifier::Ctrl), + }, + |model: &mut Model<'_>, window: Option<Window>| -> bool { if let Some(window) = window { model.set_floating_window(window, Toggle::Reverse); } - }), - (Press, Client, true): - "1-C-S-Middle" => do_internal_mouse_block!(model, window, { + + true + }, + ); + + mouse_bindings.insert( + MouseInput { + target: MouseInputTarget::Client, + button: Button::Middle, + modifiers: hashset!(Modifier::Alt, Modifier::Ctrl, Modifier::Shift), + }, + |model: &mut Model<'_>, window: Option<Window>| -> bool { if let Some(window) = window { model.set_fullscreen_window(window, Toggle::Reverse); } - }), - // free client arrangers - (Press, Client, true): - "1-Middle" => do_internal_mouse_block!(model, window, { + true + }, + ); + + mouse_bindings.insert( + MouseInput { + target: MouseInputTarget::Client, + button: Button::Middle, + modifiers: hashset!(Modifier::Alt), + }, + |model: &mut Model<'_>, window: Option<Window>| -> bool { if let Some(window) = window { model.center_window(window); } - }), - (Press, Client, true): - "1-Left" => do_internal_mouse_block!(model, window, { + + true + }, + ); + + mouse_bindings.insert( + MouseInput { + target: MouseInputTarget::Client, + button: Button::Left, + modifiers: hashset!(Modifier::Alt), + }, + |model: &mut Model<'_>, window: Option<Window>| -> bool { if let Some(window) = window { model.start_moving(window); } - }), - (Press, Client, true): - "1-Right" => do_internal_mouse_block!(model, window, { + + true + }, + ); + + mouse_bindings.insert( + MouseInput { + target: MouseInputTarget::Client, + button: Button::Right, + modifiers: hashset!(Modifier::Alt), + }, + |model: &mut Model<'_>, window: Option<Window>| -> bool { if let Some(window) = window { model.start_resizing(window); } - }), - (Press, Client, false): - "1-C-S-ScrollDown" => do_internal_mouse_block!(model, window, { + + true + }, + ); + + mouse_bindings.insert( + MouseInput { + target: MouseInputTarget::Client, + button: Button::ScrollDown, + modifiers: hashset!(Modifier::Alt, Modifier::Ctrl, Modifier::Shift), + }, + |model: &mut Model<'_>, window: Option<Window>| -> bool { if let Some(window) = window { model.grow_ratio_window(window, -15); } - }), - (Press, Client, false): - "1-C-S-ScrollUp" => do_internal_mouse_block!(model, window, { + + true + }, + ); + + mouse_bindings.insert( + MouseInput { + target: MouseInputTarget::Client, + button: Button::ScrollUp, + modifiers: hashset!(Modifier::Alt, Modifier::Ctrl, Modifier::Shift), + }, + |model: &mut Model<'_>, window: Option<Window>| -> bool { if let Some(window) = window { model.grow_ratio_window(window, 15); } - }), - - // 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), - - // workspace activators - (Press, Global, false): - "1-S-ScrollUp" => do_internal_mouse!(activate_next_workspace, Direction::Backward), - (Press, Global, false): - "1-S-ScrollDown" => do_internal_mouse!(activate_next_workspace, Direction::Forward), - - // workspace client movement - (Press, Client, false): - "1-Forward" => do_internal_mouse_block!(model, window, { + + true + }, + ); + + mouse_bindings.insert( + MouseInput { + target: MouseInputTarget::Client, + button: Button::ScrollUp, + modifiers: hashset!(Modifier::Alt), + }, + |model: &mut Model<'_>, _: Option<Window>| -> bool { + model.cycle_focus(Direction::Backward); + false + } + ); + + mouse_bindings.insert( + MouseInput { + target: MouseInputTarget::Client, + button: Button::ScrollDown, + modifiers: hashset!(Modifier::Alt), + }, + |model: &mut Model<'_>, _: Option<Window>| -> bool { + model.cycle_focus(Direction::Forward); + false + } + ); + + mouse_bindings.insert( + MouseInput { + target: MouseInputTarget::Global, + button: Button::ScrollUp, + modifiers: hashset!(Modifier::Alt, Modifier::Shift), + }, + |model: &mut Model<'_>, _: Option<Window>| -> bool { + model.activate_next_workspace(Direction::Backward); + false + } + ); + + mouse_bindings.insert( + MouseInput { + target: MouseInputTarget::Global, + button: Button::ScrollDown, + modifiers: hashset!(Modifier::Alt, Modifier::Shift), + }, + |model: &mut Model<'_>, _: Option<Window>| -> bool { + model.activate_next_workspace(Direction::Forward); + false + } + ); + + mouse_bindings.insert( + MouseInput { + target: MouseInputTarget::Client, + button: Button::Backward, + modifiers: hashset!(Modifier::Alt), + }, + |model: &mut Model<'_>, window: Option<Window>| -> bool { if let Some(window) = window { - model.move_window_to_next_workspace(window, Direction::Forward); + model.move_window_to_next_workspace(window, Direction::Backward); } - }), - (Press, Client, false): - "1-Backward" => do_internal_mouse_block!(model, window, { + + false + } + ); + + mouse_bindings.insert( + MouseInput { + target: MouseInputTarget::Client, + button: Button::Forward, + modifiers: hashset!(Modifier::Alt), + }, + |model: &mut Model<'_>, window: Option<Window>| -> bool { if let Some(window) = window { - model.move_window_to_next_workspace(window, Direction::Backward); + model.move_window_to_next_workspace(window, Direction::Forward); } - }), - // NOPs - (Release, Global, false): - "1-ScrollDown" => do_nothing!(), - (Release, Global, false): - "1-ScrollUp" => do_nothing!(), + false + } ); - // "[modifiers]-key" => action - let key_bindings = build_key_bindings!( - "1-C-S-q" => do_internal!(exit), - - // client state modifiers - "1-c" => do_internal!(kill_focus), - "1-S-space" => do_internal!(set_floating_focus, Toggle::Reverse), - "1-f" => do_internal!(set_fullscreen_focus, Toggle::Reverse), - "1-x" => do_internal!(set_stick_focus, Toggle::Reverse), - "1-2-C-f" => do_internal!(set_contained_focus, Toggle::Reverse), - "1-2-C-i" => do_internal!(set_invincible_focus, Toggle::Reverse), - "1-2-C-p" => do_internal!(set_producing_focus, Toggle::Reverse), - "1-2-C-y" => do_internal!(set_iconifyable_focus, Toggle::Reverse), - "1-y" => do_internal!(set_iconify_focus, Toggle::On), - "1-u" => do_internal!(pop_deiconify), - "1-2-u" => do_internal_block!(model, { - model.deiconify_all(model.active_workspace()); - }), - - // 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), - "1-C-l" => do_internal!(nudge_focus, Edge::Right, 15), - "1-C-S-h" => do_internal!(stretch_focus, Edge::Left, 15), - "1-C-S-j" => do_internal!(stretch_focus, Edge::Bottom, 15), - "1-C-S-k" => do_internal!(stretch_focus, Edge::Top, 15), - "1-C-S-l" => do_internal!(stretch_focus, Edge::Right, 15), - "1-C-S-y" => do_internal!(stretch_focus, Edge::Left, -15), - "1-C-S-u" => do_internal!(stretch_focus, Edge::Bottom, -15), - "1-C-S-i" => do_internal!(stretch_focus, Edge::Top, -15), - "1-C-S-o" => do_internal!(stretch_focus, Edge::Right, -15), - "1-C-Left" => do_internal!(snap_focus, Edge::Left), - "1-C-Down" => do_internal!(snap_focus, Edge::Bottom), - "1-C-Up" => do_internal!(snap_focus, Edge::Top), - "1-C-Right" => do_internal!(snap_focus, Edge::Right), - - // client order modifiers - "1-j" => do_internal!(cycle_focus, Direction::Forward), - "1-k" => do_internal!(cycle_focus, Direction::Backward), - "1-S-j" => do_internal!(drag_focus, Direction::Forward), - "1-S-k" => do_internal!(drag_focus, Direction::Backward), - "1-S-semicolon" => do_internal!(rotate_clients, Direction::Forward), - "1-S-comma" => do_internal!(rotate_clients, Direction::Backward), - - // zone creators - "1-C-Return" => do_internal!(create_layout_zone), - "1-C-S-Return" => do_internal!(create_tab_zone), - "1-C-C" => do_internal!(delete_zone), - - // zone order modifiers - // "1-C-j" => do_internal!(cycle_zones, Direction::Forward), - // "1-C-k" => do_internal!(cycle_zones, Direction::Backward), - - // active workspace layout modifiers - "1-S-f" => do_internal!(set_layout, LayoutKind::Float), - "1-S-l" => do_internal!(set_layout, LayoutKind::BLFloat), - "1-z" => do_internal!(set_layout, LayoutKind::SingleFloat), - "1-S-z" => do_internal!(set_layout, LayoutKind::BLSingleFloat), - "1-m" => do_internal!(set_layout, LayoutKind::Monocle), - "1-g" => do_internal!(set_layout, LayoutKind::Center), - "1-t" => do_internal!(set_layout, LayoutKind::Stack), - "1-S-t" => do_internal!(set_layout, LayoutKind::SStack), - "1-C-S-p" => do_internal!(set_layout, LayoutKind::Paper), - "1-2-C-S-p" => do_internal!(set_layout, LayoutKind::SPaper), - "1-C-S-b" => do_internal!(set_layout, LayoutKind::BStack), - "1-2-C-S-b" => do_internal!(set_layout, LayoutKind::SBStack), - "1-S-y" => do_internal!(set_layout, LayoutKind::Horz), - "1-C-y" => do_internal!(set_layout, LayoutKind::SHorz), - "1-S-v" => do_internal!(set_layout, LayoutKind::Vert), - "1-C-v" => do_internal!(set_layout, LayoutKind::SVert), - "1-C-S-f" => do_internal!(apply_float_retain_region), - "1-space" => do_internal!(toggle_layout), - - // active workspace layout data modifiers - "1-plus" => do_internal!(change_gap_size, Change::Inc(5u32)), - "1-minus" => do_internal!(change_gap_size, Change::Dec(5u32)), - "1-S-equal" => do_internal!(reset_gap_size), - "1-i" => do_internal!(change_main_count, Change::Inc(1u32)), - "1-d" => do_internal!(change_main_count, Change::Dec(1u32)), - "1-l" => do_internal!(change_main_factor, Change::Inc(0.05f32)), - "1-h" => do_internal!(change_main_factor, Change::Dec(0.05f32)), - "1-S-Left" => do_internal!(change_margin, Edge::Left, Change::Inc(5i32)), - "1-C-S-Left" => do_internal!(change_margin, Edge::Left, Change::Dec(5i32)), - "1-S-Up" => do_internal!(change_margin, Edge::Top, Change::Inc(5i32)), - "1-C-S-Up" => do_internal!(change_margin, Edge::Top, Change::Dec(5i32)), - "1-S-Down" => do_internal!(change_margin, Edge::Bottom, Change::Inc(5i32)), - "1-C-S-Down" => do_internal!(change_margin, Edge::Bottom, Change::Dec(5i32)), - "1-S-Right" => do_internal!(change_margin, Edge::Right, Change::Inc(5i32)), - "1-C-S-Right" => do_internal!(change_margin, Edge::Right, Change::Dec(5i32)), - "1-C-S-equal" => do_internal!(reset_margin), - "1-2-C-S-l" => do_internal!(copy_prev_layout_data), - "1-2-C-S-equal" => do_internal!(reset_layout_data), - - // workspace activators - "1-Escape" => do_internal!(toggle_workspace), - "1-bracketleft" => do_internal!(activate_next_workspace, Direction::Backward), - "1-bracketright" => do_internal!(activate_next_workspace, Direction::Forward), - "1-1" => do_internal!(activate_workspace, 0), - "1-2" => do_internal!(activate_workspace, 1), - "1-3" => do_internal!(activate_workspace, 2), - "1-4" => do_internal!(activate_workspace, 3), - "1-5" => do_internal!(activate_workspace, 4), - "1-6" => do_internal!(activate_workspace, 5), - "1-7" => do_internal!(activate_workspace, 6), - "1-8" => do_internal!(activate_workspace, 7), - "1-9" => do_internal!(activate_workspace, 8), - "1-0" => do_internal!(activate_workspace, 9), - - // workspace client movers - "1-S-bracketleft" => do_internal!(move_focus_to_next_workspace, Direction::Backward), - "1-S-bracketright" => do_internal!(move_focus_to_next_workspace, Direction::Forward), - "1-S-1" => do_internal!(move_focus_to_workspace, 0), - "1-S-2" => do_internal!(move_focus_to_workspace, 1), - "1-S-3" => do_internal!(move_focus_to_workspace, 2), - "1-S-4" => do_internal!(move_focus_to_workspace, 3), - "1-S-5" => do_internal!(move_focus_to_workspace, 4), - "1-S-6" => do_internal!(move_focus_to_workspace, 5), - "1-S-7" => do_internal!(move_focus_to_workspace, 6), - "1-S-8" => do_internal!(move_focus_to_workspace, 7), - "1-S-9" => do_internal!(move_focus_to_workspace, 8), - "1-S-0" => do_internal!(move_focus_to_workspace, 9), - - // placeable region modifiers - "1-v" => do_internal!(toggle_screen_struts), - - // client jump criteria - "1-b" => do_internal!(jump_client, - JumpCriterium::ByClass(MatchMethod::Equals("qutebrowser")) - ), - "1-S-b" => do_internal!(jump_client, - JumpCriterium::ByClass(MatchMethod::Equals("Firefox")) - ), - "1-C-b" => do_internal!(jump_client, - JumpCriterium::ByClass(MatchMethod::Equals("Chromium")) - ), - "1-2-space" => do_internal!(jump_client, - JumpCriterium::ByClass(MatchMethod::Equals("Spotify")) - ), - "1-e" => do_internal_block!(model, { - model.jump_client(JumpCriterium::ByName( - MatchMethod::Contains("[vim]"), - )); - }), - "1-slash" => do_internal_block!(model, { - model.jump_client(JumpCriterium::OnWorkspaceBySelector( - model.active_workspace(), - &ClientSelector::Last, - )); - }), - "1-period" => do_internal_block!(model, { - model.jump_client(JumpCriterium::OnWorkspaceBySelector( - model.active_workspace(), - &ClientSelector::AtMaster, - )); - }), - "1-comma" => do_internal_block!(model, { - model.jump_client(JumpCriterium::OnWorkspaceBySelector( - model.active_workspace(), - &ClientSelector::First, - )); - }), - - // external spawn commands - "XF86AudioPlay", "1-2-p" => spawn_external!("playerctl play-pause"), - "XF86AudioPrev", "1-2-k" => spawn_external!("playerctl previous"), - "XF86AudioNext", "1-2-j" => spawn_external!("playerctl next"), - "1-2-BackSpace" => spawn_external!("playerctl stop"), - "XF86AudioMute" => spawn_external!("amixer -D pulse sset Master toggle"), - "XF86AudioLowerVolume" => spawn_external!("amixer -D pulse sset Master 5%-"), - "XF86AudioRaiseVolume" => spawn_external!("amixer -D pulse sset Master 5%+"), - - "1-Return" => spawn_external!("st"), - "1-S-Return" => spawn_external!(concat!("st -n ", WM_NAME!(), ":cf")), - - "1-p" => spawn_external!("dmenu_run"), - "1-q" => spawn_external!("qutebrowser"), - "1-S-q" => spawn_external!("firefox"), - "1-C-q" => spawn_external!("chromium"), - - "1-C-e" => spawn_external!("st -g 140x42 -e zsh -i -c neomutt"), - "1-C-s" => spawn_external!("st -g 80x42 -e zsh -i -c sncli"), - "1-C-i" => spawn_external!("st -g 80x42 -e zsh -i -c irssi"), - - "S-XF86AudioMute" => spawn_external!("amixer -D pulse sset Capture toggle"), - "XF86AudioMicMute" => spawn_external!("amixer -D pulse sset Capture toggle"), - - // external shell commands - "1-S-p" => spawn_from_shell!("$HOME/bin/dmenupass"), - "1-C-p" => spawn_from_shell!("$HOME/bin/dmenupass --copy"), - "1-S-o" => spawn_from_shell!("$HOME/bin/dmenunotify"), - "Print", "1-2-slash" => spawn_from_shell!( - "maim -u -m 1 -s \ - $(date +$HOME/screenshots/scrots/SS_%Y-%h-%d_%H-%M-%S.png)" - ), - "S-Print", "1-2-S-slash" => spawn_from_shell!( - "maim -u -m 1 \ - $(date +$HOME/screenshots/scrots/SS_%Y-%h-%d_%H-%M-%S.png)" - ), + key_bindings.insert( + KeyInput { + key: Key::Escape, + modifiers: hashset!(Modifier::Alt, Modifier::Ctrl, Modifier::Shift), + }, + |model: &mut Model<'_>| { + model.exit(); + } ); + key_bindings.insert( + KeyInput { + key: Key::J, + modifiers: hashset!(Modifier::Alt), + }, + |model: &mut Model<'_>| { + model.cycle_focus(Direction::Forward); + } + ); + + key_bindings.insert( + KeyInput { + key: Key::K, + modifiers: hashset!(Modifier::Alt), + }, + |model: &mut Model<'_>| { + model.cycle_focus(Direction::Backward); + } + ); + + // // (kind, target, focus): "[modifiers]-button" => action + // let mouse_bindings = build_mouse_bindings!( + // }), + + // // "[modifiers]-key" => action + // let key_bindings = build_key_bindings!( + // "1-C-S-q" => do_internal!(exit), + + // // client state modifiers + // "1-c" => do_internal!(kill_focus), + // "1-S-space" => do_internal!(set_floating_focus, Toggle::Reverse), + // "1-f" => do_internal!(set_fullscreen_focus, Toggle::Reverse), + // "1-x" => do_internal!(set_stick_focus, Toggle::Reverse), + // "1-2-C-f" => do_internal!(set_contained_focus, Toggle::Reverse), + // "1-2-C-i" => do_internal!(set_invincible_focus, Toggle::Reverse), + // "1-2-C-p" => do_internal!(set_producing_focus, Toggle::Reverse), + // "1-2-C-y" => do_internal!(set_iconifyable_focus, Toggle::Reverse), + // "1-y" => do_internal!(set_iconify_focus, Toggle::On), + // "1-u" => do_internal!(pop_deiconify), + // "1-2-u" => do_internal_block!(model, { + // model.deiconify_all(model.active_workspace()); + // }), + + // // 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), + // "1-C-l" => do_internal!(nudge_focus, Edge::Right, 15), + // "1-C-S-h" => do_internal!(stretch_focus, Edge::Left, 15), + // "1-C-S-j" => do_internal!(stretch_focus, Edge::Bottom, 15), + // "1-C-S-k" => do_internal!(stretch_focus, Edge::Top, 15), + // "1-C-S-l" => do_internal!(stretch_focus, Edge::Right, 15), + // "1-C-S-y" => do_internal!(stretch_focus, Edge::Left, -15), + // "1-C-S-u" => do_internal!(stretch_focus, Edge::Bottom, -15), + // "1-C-S-i" => do_internal!(stretch_focus, Edge::Top, -15), + // "1-C-S-o" => do_internal!(stretch_focus, Edge::Right, -15), + // "1-C-Left" => do_internal!(snap_focus, Edge::Left), + // "1-C-Down" => do_internal!(snap_focus, Edge::Bottom), + // "1-C-Up" => do_internal!(snap_focus, Edge::Top), + // "1-C-Right" => do_internal!(snap_focus, Edge::Right), + + // // client order modifiers + // "1-j" => do_internal!(cycle_focus, Direction::Forward), + // "1-k" => do_internal!(cycle_focus, Direction::Backward), + // "1-S-j" => do_internal!(drag_focus, Direction::Forward), + // "1-S-k" => do_internal!(drag_focus, Direction::Backward), + // "1-S-semicolon" => do_internal!(rotate_clients, Direction::Forward), + // "1-S-comma" => do_internal!(rotate_clients, Direction::Backward), + + // // zone creators + // "1-C-Return" => do_internal!(create_layout_zone), + // "1-C-S-Return" => do_internal!(create_tab_zone), + // "1-C-C" => do_internal!(delete_zone), + + // // zone order modifiers + // // "1-C-j" => do_internal!(cycle_zones, Direction::Forward), + // // "1-C-k" => do_internal!(cycle_zones, Direction::Backward), + + // // active workspace layout modifiers + // "1-S-f" => do_internal!(set_layout, LayoutKind::Float), + // "1-S-l" => do_internal!(set_layout, LayoutKind::BLFloat), + // "1-z" => do_internal!(set_layout, LayoutKind::SingleFloat), + // "1-S-z" => do_internal!(set_layout, LayoutKind::BLSingleFloat), + // "1-m" => do_internal!(set_layout, LayoutKind::Monocle), + // "1-g" => do_internal!(set_layout, LayoutKind::Center), + // "1-t" => do_internal!(set_layout, LayoutKind::Stack), + // "1-S-t" => do_internal!(set_layout, LayoutKind::SStack), + // "1-C-S-p" => do_internal!(set_layout, LayoutKind::Paper), + // "1-2-C-S-p" => do_internal!(set_layout, LayoutKind::SPaper), + // "1-C-S-b" => do_internal!(set_layout, LayoutKind::BStack), + // "1-2-C-S-b" => do_internal!(set_layout, LayoutKind::SBStack), + // "1-S-y" => do_internal!(set_layout, LayoutKind::Horz), + // "1-C-y" => do_internal!(set_layout, LayoutKind::SHorz), + // "1-S-v" => do_internal!(set_layout, LayoutKind::Vert), + // "1-C-v" => do_internal!(set_layout, LayoutKind::SVert), + // "1-C-S-f" => do_internal!(apply_float_retain_region), + // "1-space" => do_internal!(toggle_layout), + + // // active workspace layout data modifiers + // "1-plus" => do_internal!(change_gap_size, Change::Inc(5u32)), + // "1-minus" => do_internal!(change_gap_size, Change::Dec(5u32)), + // "1-S-equal" => do_internal!(reset_gap_size), + // "1-i" => do_internal!(change_main_count, Change::Inc(1u32)), + // "1-d" => do_internal!(change_main_count, Change::Dec(1u32)), + // "1-l" => do_internal!(change_main_factor, Change::Inc(0.05f32)), + // "1-h" => do_internal!(change_main_factor, Change::Dec(0.05f32)), + // "1-S-Left" => do_internal!(change_margin, Edge::Left, Change::Inc(5i32)), + // "1-C-S-Left" => do_internal!(change_margin, Edge::Left, Change::Dec(5i32)), + // "1-S-Up" => do_internal!(change_margin, Edge::Top, Change::Inc(5i32)), + // "1-C-S-Up" => do_internal!(change_margin, Edge::Top, Change::Dec(5i32)), + // "1-S-Down" => do_internal!(change_margin, Edge::Bottom, Change::Inc(5i32)), + // "1-C-S-Down" => do_internal!(change_margin, Edge::Bottom, Change::Dec(5i32)), + // "1-S-Right" => do_internal!(change_margin, Edge::Right, Change::Inc(5i32)), + // "1-C-S-Right" => do_internal!(change_margin, Edge::Right, Change::Dec(5i32)), + // "1-C-S-equal" => do_internal!(reset_margin), + // "1-2-C-S-l" => do_internal!(copy_prev_layout_data), + // "1-2-C-S-equal" => do_internal!(reset_layout_data), + + // // workspace activators + // "1-Escape" => do_internal!(toggle_workspace), + // "1-bracketleft" => do_internal!(activate_next_workspace, Direction::Backward), + // "1-bracketright" => do_internal!(activate_next_workspace, Direction::Forward), + // "1-1" => do_internal!(activate_workspace, 0), + // "1-2" => do_internal!(activate_workspace, 1), + // "1-3" => do_internal!(activate_workspace, 2), + // "1-4" => do_internal!(activate_workspace, 3), + // "1-5" => do_internal!(activate_workspace, 4), + // "1-6" => do_internal!(activate_workspace, 5), + // "1-7" => do_internal!(activate_workspace, 6), + // "1-8" => do_internal!(activate_workspace, 7), + // "1-9" => do_internal!(activate_workspace, 8), + // "1-0" => do_internal!(activate_workspace, 9), + + // // workspace client movers + // "1-S-bracketleft" => do_internal!(move_focus_to_next_workspace, Direction::Backward), + // "1-S-bracketright" => do_internal!(move_focus_to_next_workspace, Direction::Forward), + // "1-S-1" => do_internal!(move_focus_to_workspace, 0), + // "1-S-2" => do_internal!(move_focus_to_workspace, 1), + // "1-S-3" => do_internal!(move_focus_to_workspace, 2), + // "1-S-4" => do_internal!(move_focus_to_workspace, 3), + // "1-S-5" => do_internal!(move_focus_to_workspace, 4), + // "1-S-6" => do_internal!(move_focus_to_workspace, 5), + // "1-S-7" => do_internal!(move_focus_to_workspace, 6), + // "1-S-8" => do_internal!(move_focus_to_workspace, 7), + // "1-S-9" => do_internal!(move_focus_to_workspace, 8), + // "1-S-0" => do_internal!(move_focus_to_workspace, 9), + + // // placeable region modifiers + // "1-v" => do_internal!(toggle_screen_struts), + + // // client jump criteria + // "1-b" => do_internal!(jump_client, + // JumpCriterium::ByClass(MatchMethod::Equals("qutebrowser")) + // ), + // "1-S-b" => do_internal!(jump_client, + // JumpCriterium::ByClass(MatchMethod::Equals("Firefox")) + // ), + // "1-C-b" => do_internal!(jump_client, + // JumpCriterium::ByClass(MatchMethod::Equals("Chromium")) + // ), + // "1-2-space" => do_internal!(jump_client, + // JumpCriterium::ByClass(MatchMethod::Equals("Spotify")) + // ), + // "1-e" => do_internal_block!(model, { + // model.jump_client(JumpCriterium::ByName( + // MatchMethod::Contains("[vim]"), + // )); + // }), + // "1-slash" => do_internal_block!(model, { + // model.jump_client(JumpCriterium::OnWorkspaceBySelector( + // model.active_workspace(), + // &ClientSelector::Last, + // )); + // }), + // "1-period" => do_internal_block!(model, { + // model.jump_client(JumpCriterium::OnWorkspaceBySelector( + // model.active_workspace(), + // &ClientSelector::AtMaster, + // )); + // }), + // "1-comma" => do_internal_block!(model, { + // model.jump_client(JumpCriterium::OnWorkspaceBySelector( + // model.active_workspace(), + // &ClientSelector::First, + // )); + // }), + + // // external spawn commands + // "XF86AudioPlay", "1-2-p" => spawn_external!("playerctl play-pause"), + // "XF86AudioPrev", "1-2-k" => spawn_external!("playerctl previous"), + // "XF86AudioNext", "1-2-j" => spawn_external!("playerctl next"), + // "1-2-BackSpace" => spawn_external!("playerctl stop"), + // "XF86AudioMute" => spawn_external!("amixer -D pulse sset Master toggle"), + // "XF86AudioLowerVolume" => spawn_external!("amixer -D pulse sset Master 5%-"), + // "XF86AudioRaiseVolume" => spawn_external!("amixer -D pulse sset Master 5%+"), + + // "1-Return" => spawn_external!("st"), + // "1-S-Return" => spawn_external!(concat!("st -n ", WM_NAME!(), ":cf")), + + // "1-p" => spawn_external!("dmenu_run"), + // "1-q" => spawn_external!("qutebrowser"), + // "1-S-q" => spawn_external!("firefox"), + // "1-C-q" => spawn_external!("chromium"), + + // "1-C-e" => spawn_external!("st -g 140x42 -e zsh -i -c neomutt"), + // "1-C-s" => spawn_external!("st -g 80x42 -e zsh -i -c sncli"), + // "1-C-i" => spawn_external!("st -g 80x42 -e zsh -i -c irssi"), + + // "S-XF86AudioMute" => spawn_external!("amixer -D pulse sset Capture toggle"), + // "XF86AudioMicMute" => spawn_external!("amixer -D pulse sset Capture toggle"), + + // // external shell commands + // "1-S-p" => spawn_from_shell!("$HOME/bin/dmenupass"), + // "1-C-p" => spawn_from_shell!("$HOME/bin/dmenupass --copy"), + // "1-S-o" => spawn_from_shell!("$HOME/bin/dmenunotify"), + // "Print", "1-2-slash" => spawn_from_shell!( + // "maim -u -m 1 -s \ + // $(date +$HOME/screenshots/scrots/SS_%Y-%h-%d_%H-%M-%S.png)" + // ), + // "S-Print", "1-2-S-slash" => spawn_from_shell!( + // "maim -u -m 1 \ + // $(date +$HOME/screenshots/scrots/SS_%Y-%h-%d_%H-%M-%S.png)" + // ), + // ); + (mouse_bindings, key_bindings) } diff --git a/src/core/model.rs b/src/core/model.rs @@ -47,13 +47,13 @@ use winsys::geometry::Edge; use winsys::geometry::Pos; use winsys::geometry::Region; use winsys::hints::Hints; -use winsys::input::EventTarget; use winsys::input::Grip; -use winsys::input::KeyCode; +use winsys::input::KeyEvent; +use winsys::input::KeyInput; use winsys::input::MouseEvent; -use winsys::input::MouseEventKey; use winsys::input::MouseEventKind; -use winsys::input::MouseShortcut; +use winsys::input::MouseInput; +use winsys::input::MouseInputTarget; use winsys::screen::Screen; use winsys::window::IcccmWindowState; use winsys::window::Window; @@ -163,11 +163,11 @@ impl<'model> Model<'model> { .init_wm_properties(WM_NAME!(), &defaults::WORKSPACE_NAMES); model.conn.grab_bindings( - &key_bindings.keys().into_iter().collect::<Vec<&KeyCode>>(), + &key_bindings.keys().into_iter().collect::<Vec<&KeyInput>>(), &mouse_bindings .keys() .into_iter() - .collect::<Vec<&(MouseEventKey, MouseShortcut)>>(), + .collect::<Vec<&MouseInput>>(), ); model @@ -2512,6 +2512,10 @@ impl<'model> Model<'model> { &self, pos: &Pos, ) { + if !self.move_buffer.is_occupied() { + return; + } + let client = self .move_buffer .window() @@ -2574,6 +2578,10 @@ impl<'model> Model<'model> { &self, pos: &Pos, ) { + if !self.resize_buffer.is_occupied() { + return; + } + let client = self .resize_buffer .window() @@ -2650,10 +2658,11 @@ impl<'model> Model<'model> { match event { Event::Mouse { event, - } => self.handle_mouse(event, &mut mouse_bindings), + on_root, + } => self.handle_mouse(event, on_root, &mut mouse_bindings), Event::Key { - key_code, - } => self.handle_key(key_code, &mut key_bindings), + event, + } => self.handle_key(event, &mut key_bindings), Event::MapRequest { window, ignore, @@ -2745,27 +2754,22 @@ impl<'model> Model<'model> { fn handle_mouse( &mut self, event: MouseEvent, + on_root: bool, mouse_bindings: &mut MouseBindings, ) { - let mut window = event.window; - let subwindow = event.subwindow; + let mut input = event.input; + let window = event.window; match event.kind { MouseEventKind::Release => { - if self.move_buffer.is_occupied() { - self.stop_moving(); - return; - } else if self.resize_buffer.is_occupied() { - self.stop_resizing(); - return; - } + self.stop_moving(); + self.stop_resizing(); + + return; }, MouseEventKind::Motion => { - if self.move_buffer.is_occupied() { - self.handle_move(&event.root_rpos); - } else if self.resize_buffer.is_occupied() { - self.handle_resize(&event.root_rpos); - } + self.handle_move(&event.root_rpos); + self.handle_resize(&event.root_rpos); return; }, @@ -2774,22 +2778,15 @@ impl<'model> Model<'model> { { // handle global mouse bindings - let binding = mouse_bindings.get_mut(&( - MouseEventKey { - kind: event.kind, - target: EventTarget::Global, - }, - event.shortcut.clone(), - )); + input.target = MouseInputTarget::Global; + let binding = mouse_bindings.get_mut(&input); - if let Some((action, moves_focus)) = binding { - action(self, None); - - if *moves_focus { + if let Some(action) = binding { + if action(self, None) { // TODO: config.focus_follows_mouse if let Some(focus) = self.focus.get() { - if window != focus { - self.focus_window(window); + if window.is_some() && window != Some(focus) { + self.focus_window(window.unwrap()); } } } @@ -2798,55 +2795,39 @@ impl<'model> Model<'model> { } } - if event.on_root { - if let Some(subwindow) = subwindow { - window = subwindow; - } else { - // handle root-targeted mouse bindings - let binding = mouse_bindings.get_mut(&( - MouseEventKey { - kind: event.kind, - target: EventTarget::Root, - }, - event.shortcut, - )); - - if let Some((action, _)) = binding { - action(self, None); - } + if on_root { + // handle root-targeted mouse bindings + input.target = MouseInputTarget::Root; + let binding = mouse_bindings.get_mut(&input); + if let Some(action) = binding { + action(self, None); return; } } { // handle client-targeted mouse bindings - let binding = mouse_bindings.get_mut(&( - MouseEventKey { - kind: event.kind, - target: EventTarget::Client, - }, - event.shortcut.clone(), - )); - - if let Some(window) = self.window(window) { - if let Some((action, moves_focus)) = binding { - action(self, Some(window)); - - if *moves_focus { - // TODO: config.focus_follows_mouse - if let Some(focus) = self.focus.get() { - if window != focus { - self.focus_window(window); + input.target = MouseInputTarget::Client; + let binding = mouse_bindings.get_mut(&input); + + if let Some(window) = event.window { + if let Some(window) = self.window(window) { + if let Some(action) = binding { + if action(self, Some(window)) { + // TODO: config.focus_follows_mouse + if let Some(focus) = self.focus.get() { + if window != focus { + self.focus_window(window); + } } } - } - } else { - // TODO: config.focus_follows_mouse - if event.kind != MouseEventKind::Release { - if let Some(focus) = self.focus.get() { - if window != focus { - self.focus_window(window); + } else { + if event.kind != MouseEventKind::Release { + if let Some(focus) = self.focus.get() { + if window != focus { + self.focus_window(window); + } } } } @@ -2858,11 +2839,11 @@ impl<'model> Model<'model> { #[inline(always)] fn handle_key( &mut self, - key_code: KeyCode, + event: KeyEvent, key_bindings: &mut KeyBindings, ) { - if let Some(action) = key_bindings.get_mut(&key_code.clone()) { - debug!("processing key binding: {:?}", key_code); + if let Some(action) = key_bindings.get_mut(&event.input) { + debug!("processing key binding: {:?}", event.input); action(self); } } diff --git a/src/core/util.rs b/src/core/util.rs @@ -3,10 +3,7 @@ use crate::change::Direction; use crate::identify::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::BuildHasher; @@ -156,111 +153,111 @@ impl Util { .ok(); } - pub fn system_keycodes() -> CodeMap { - match Command::new("xmodmap").arg("-pke").output() { - Err(e) => panic!("unable to fetch keycodes via xmodmap: {}", e), - Ok(o) => match String::from_utf8(o.stdout) { - Err(e) => panic!("invalid utf8 from xmodmap: {}", e), - Ok(s) => s - .lines() - .flat_map(|l| { - let mut words = l.split_whitespace(); - let key_code: u8 = words.nth(1).unwrap().parse().unwrap(); + // pub fn system_keycodes() -> CodeMap { + // match Command::new("xmodmap").arg("-pke").output() { + // Err(e) => panic!("unable to fetch keycodes via xmodmap: {}", e), + // Ok(o) => match String::from_utf8(o.stdout) { + // Err(e) => panic!("invalid utf8 from xmodmap: {}", e), + // Ok(s) => s + // .lines() + // .flat_map(|l| { + // let mut words = l.split_whitespace(); + // let key_code: u8 = words.nth(1).unwrap().parse().unwrap(); - words.skip(1).map(move |name| (name.into(), key_code)) - }) - .collect::<CodeMap>(), - }, - } - } + // words.skip(1).map(move |name| (name.into(), key_code)) + // }) + // .collect::<CodeMap>(), + // }, + // } + // } - pub fn parse_key_binding( - key_binding: impl Into<String>, - keycodes: &CodeMap, - ) -> Option<KeyCode> { - let s = key_binding.into(); - let mut constituents: Vec<&str> = s.split('-').collect(); + // pub fn parse_key_binding( + // key_binding: impl Into<String>, + // keycodes: &CodeMap, + // ) -> Option<KeyCode> { + // let s = key_binding.into(); + // let mut constituents: Vec<&str> = s.split('-').collect(); - match keycodes.get(constituents.remove(constituents.len() - 1)) { - Some(&code) => { - let mask = constituents - .iter() - .map(|&modifier| match modifier { - "A" | "Alt" | "Meta" => u16::from(ModMask::M1), - "M" | "Super" => u16::from(ModMask::M4), - "S" | "Shift" => u16::from(ModMask::SHIFT), - "C" | "Ctrl" | "Control" => u16::from(ModMask::CONTROL), - "1" | "Mod" => u16::from(if cfg!(debug_assertions) { - ModMask::M1 - } else { - ModMask::M4 - }), - "2" | "Sec" => u16::from(if cfg!(debug_assertions) { - ModMask::M4 - } else { - ModMask::M1 - }), - _ => panic!("invalid modifier: {}", s), - }) - .fold(0, |acc, modifier| acc | modifier); + // match keycodes.get(constituents.remove(constituents.len() - 1)) { + // Some(&code) => { + // let mask = constituents + // .iter() + // .map(|&modifier| match modifier { + // "A" | "Alt" | "Meta" => u16::from(ModMask::M1), + // "M" | "Super" => u16::from(ModMask::M4), + // "S" | "Shift" => u16::from(ModMask::SHIFT), + // "C" | "Ctrl" | "Control" => u16::from(ModMask::CONTROL), + // "1" | "Mod" => u16::from(if cfg!(debug_assertions) { + // ModMask::M1 + // } else { + // ModMask::M4 + // }), + // "2" | "Sec" => u16::from(if cfg!(debug_assertions) { + // ModMask::M4 + // } else { + // ModMask::M1 + // }), + // _ => panic!("invalid modifier: {}", s), + // }) + // .fold(0, |acc, modifier| acc | modifier); - Some(KeyCode { - mask, - code, - }) - }, - None => None, - } - } + // Some(KeyCode { + // mask, + // code, + // }) + // }, + // 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(); + // 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 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>>(); + // 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(); + // modifiers.sort(); - Some(MouseShortcut { - button, - modifiers, - }) - } + // Some(MouseShortcut { + // button, + // modifiers, + // }) + // } } diff --git a/src/winsys/connection.rs b/src/winsys/connection.rs @@ -6,7 +6,8 @@ use crate::geometry::Region; use crate::geometry::Strut; use crate::hints::Hints; use crate::hints::SizeHints; -use crate::input::*; +use crate::input::KeyInput; +use crate::input::MouseInput; use crate::screen::Screen; use crate::window::IcccmWindowState; use crate::window::Window; @@ -135,8 +136,8 @@ pub trait Connection { ); fn grab_bindings( &self, - key_codes: &[&KeyCode], - mouse_bindings: &[&(MouseEventKey, MouseShortcut)], + key_codes: &[&KeyInput], + mouse_bindings: &[&MouseInput], ); fn regrab_buttons( &self, diff --git a/src/winsys/event.rs b/src/winsys/event.rs @@ -4,7 +4,7 @@ use crate::geometry::Dim; use crate::geometry::Pos; use crate::geometry::Region; use crate::input::Grip; -use crate::input::KeyCode; +use crate::input::KeyEvent; use crate::input::MouseEvent; use crate::screen::Screen; use crate::window::Window; @@ -15,9 +15,10 @@ use crate::window::WindowType; pub enum Event { Mouse { event: MouseEvent, + on_root: bool, }, Key { - key_code: KeyCode, + event: KeyEvent, }, MapRequest { window: Window, diff --git a/src/winsys/geometry.rs b/src/winsys/geometry.rs @@ -24,7 +24,7 @@ pub enum Corner { BottomRight, } -#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] pub struct Pos { pub x: i32, pub y: i32, diff --git a/src/winsys/input.rs b/src/winsys/input.rs @@ -6,9 +6,13 @@ use crate::geometry::Pos; use crate::window::Window; use std::collections::HashMap; +use std::collections::HashSet; use std::convert::TryFrom; +use std::hash::Hash; +use std::hash::Hasher; use std::vec::Vec; +use anyhow::anyhow; use strum::EnumIter; use strum::IntoEnumIterator; @@ -32,12 +36,33 @@ impl Grip { } } -pub type CodeMap = HashMap<String, u8>; +#[repr(u8)] +#[derive(Debug, PartialEq, EnumIter, Eq, Hash, Clone, Copy, PartialOrd, Ord)] +pub enum Modifier { + Ctrl = 1 << 0, + Shift = 1 << 1, + Alt = 1 << 2, + AltGr = 1 << 3, + Super = 1 << 4, + NumLock = 1 << 5, + ScrollLock = 1 << 6, +} -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] -pub struct KeyCode { - pub mask: u16, - pub code: u8, +impl TryFrom<&str> for Modifier { + type Error = anyhow::Error; + + fn try_from(val: &str) -> Result<Self> { + match val { + "C" => Ok(Self::Ctrl), + "A" => Ok(Self::Alt), + "S" => Ok(Self::Shift), + "M" => Ok(Self::Super), + "AltGr" => Ok(Self::Alt), + "Num" => Ok(Self::NumLock), + "Scroll" => Ok(Self::ScrollLock), + _ => Err(anyhow!("unable to resolve \"{}\" to modifier", val)), + } + } } #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] @@ -51,36 +76,6 @@ pub enum Button { Forward, } -#[derive(Debug, PartialEq, EnumIter, Eq, Hash, Clone, Copy, PartialOrd, Ord)] -pub enum Modifier { - Ctrl, - Shift, - Alt, - AltGr, - Super, - NumLock, - ScrollLock, -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone)] -pub struct MouseShortcut { - pub button: Button, - pub modifiers: Vec<Modifier>, -} - -impl MouseShortcut { - pub fn new( - button: Button, - mut modifiers: Vec<Modifier>, - ) -> Self { - modifiers.sort(); - Self { - button, - modifiers, - } - } -} - #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] pub enum MouseEventKind { Press, @@ -89,55 +84,244 @@ pub enum MouseEventKind { } #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] -pub enum EventTarget { +pub enum MouseInputTarget { Global, Root, Client, } -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] -pub struct MouseEventKey { - pub kind: MouseEventKind, - pub target: EventTarget, +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MouseInput { + pub target: MouseInputTarget, + pub button: Button, + pub modifiers: HashSet<Modifier>, } -#[derive(Debug, Clone)] +impl Hash for MouseInput { + fn hash<H: Hasher>( + &self, + state: &mut H, + ) { + self.button.hash(state); + self.modifiers + .iter() + .fold(0u8, |acc, &m| acc | m as u8) + .hash(state); + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct MouseEvent { pub kind: MouseEventKind, - pub window: Window, - pub subwindow: Option<Window>, - pub on_root: bool, + pub input: MouseInput, + pub window: Option<Window>, pub root_rpos: Pos, - pub window_rpos: Pos, - pub shortcut: MouseShortcut, } -impl MouseEvent { - pub fn new( - kind: MouseEventKind, - window: Window, - subwindow: Option<Window>, - root: Window, - root_rx: i16, - root_ry: i16, - window_rx: i16, - window_ry: i16, - shortcut: MouseShortcut, - ) -> Self { - Self { - kind, - window, - subwindow, - on_root: window == root, - root_rpos: Pos { - x: root_rx as i32, - y: root_ry as i32, - }, - window_rpos: Pos { - x: window_rx as i32, - y: window_ry as i32, - }, - shortcut, - } +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +pub enum Key { + Any, + Backspace, + Tab, + Clear, + Return, + Shift, + Control, + Alt, + Super, + Menu, + Pause, + CapsLock, + Escape, + Space, + ExclamationMark, + QuotationMark, + QuestionMark, + NumberSign, + DollarSign, + PercentSign, + AtSign, + Ampersand, + Apostrophe, + LeftParenthesis, + RightParenthesis, + LeftBracket, + RightBracket, + LeftBrace, + RightBrace, + Underscore, + Grave, + Bar, + Tilde, + QuoteLeft, + Asterisk, + Plus, + Comma, + Minus, + Period, + Slash, + BackSlash, + Colon, + SemiColon, + Less, + Equal, + Greater, + PageUp, + PageDown, + End, + Home, + Left, + Up, + Right, + Down, + Select, + Print, + Execute, + PrintScreen, + Insert, + Delete, + Help, + Zero, + One, + Two, + Three, + Four, + Five, + Six, + Seven, + Eight, + Nine, + A, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, + M, + N, + O, + P, + Q, + R, + S, + T, + U, + V, + W, + X, + Y, + Z, + NumPad0, + NumPad1, + NumPad2, + NumPad3, + NumPad4, + NumPad5, + NumPad6, + NumPad7, + NumPad8, + NumPad9, + Multiply, + Add, + Seperator, + Subtract, + Decimal, + Divide, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + F13, + F14, + F15, + F16, + F17, + F18, + F19, + F20, + F21, + F22, + F23, + F24, + Numlock, + ScrollLock, + LeftShift, + RightShift, + LeftControl, + RightContol, + LeftAlt, + RightAlt, + LeftSuper, + RightSuper, + BrowserBack, + BrowserForward, + BrowserRefresh, + BrowserStop, + BrowserSearch, + BrowserFavorites, + BrowserHome, + VolumeMute, + VolumeDown, + VolumeUp, + NextTrack, + PreviousTrack, + StopMedia, + PlayPause, + LaunchMail, + SelectMedia, + LaunchAppA, + LaunchAppB, + LaunchAppC, + LaunchAppD, + LaunchAppE, + LaunchAppF, + LaunchApp0, + LaunchApp1, + LaunchApp2, + LaunchApp3, + LaunchApp4, + LaunchApp5, + LaunchApp6, + LaunchApp7, + LaunchApp8, + LaunchApp9, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct KeyInput { + pub key: Key, + pub modifiers: HashSet<Modifier>, +} + +impl Hash for KeyInput { + fn hash<H: Hasher>( + &self, + state: &mut H, + ) { + self.key.hash(state); + self.modifiers + .iter() + .fold(0u8, |acc, &m| acc | m as u8) + .hash(state); } } + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct KeyEvent { + pub input: KeyInput, + pub window: Option<Window>, +} diff --git a/src/winsys/xdata/event.rs b/src/winsys/xdata/event.rs @@ -3,7 +3,6 @@ pub use super::super::event::*; use crate::geometry::Pos; use crate::geometry::Region; -use crate::input::KeyCode; use crate::screen::Screen; use crate::window::Window; use crate::window::WindowState; diff --git a/src/winsys/xdata/input.rs b/src/winsys/xdata/input.rs @@ -18,21 +18,33 @@ use x11rb::protocol::xproto::KeyPressEvent; use x11rb::protocol::xproto::ModMask; use x11rb::protocol::xproto::MotionNotifyEvent; -impl KeyCode { - pub fn from_press_event(event: &KeyPressEvent) -> Self { - Self { - mask: event.state, - code: event.detail, - } +impl From<Modifier> for u16 { + fn from(modifier: Modifier) -> u16 { + u16::from(match modifier { + Modifier::Ctrl => ModMask::CONTROL, + Modifier::Shift => ModMask::SHIFT, + Modifier::Alt => ModMask::M1, + Modifier::Super => ModMask::M4, + Modifier::AltGr => ModMask::M3, + Modifier::NumLock => ModMask::M2, + Modifier::ScrollLock => ModMask::M5, + }) } +} + +impl TryFrom<ModMask> for Modifier { + type Error = anyhow::Error; - pub fn without_mask( - &self, - mask: ModMask, - ) -> Self { - Self { - mask: self.mask & !(u16::from(mask)), - code: self.code, + fn try_from(val: ModMask) -> Result<Self> { + match val { + ModMask::CONTROL => Ok(Modifier::Ctrl), + ModMask::SHIFT => Ok(Modifier::Shift), + ModMask::M1 => Ok(Modifier::Alt), + ModMask::M4 => Ok(Modifier::Super), + ModMask::M3 => Ok(Modifier::AltGr), + ModMask::M2 => Ok(Modifier::NumLock), + ModMask::M5 => Ok(Modifier::ScrollLock), + _ => Err(anyhow!("no matching modifier for value {}", u16::from(val))), } } } @@ -67,134 +79,3 @@ impl TryFrom<u8> for Button { } } } - -impl Modifier { - pub fn was_held( - &self, - mask: u16, - ) -> bool { - mask & u16::from(*self) > 0 - } -} - -impl From<Modifier> for u16 { - fn from(modifier: Modifier) -> u16 { - u16::from(match modifier { - Modifier::Ctrl => ModMask::CONTROL, - Modifier::Shift => ModMask::SHIFT, - Modifier::Alt => ModMask::M1, - Modifier::Super => ModMask::M4, - Modifier::AltGr => ModMask::M3, - Modifier::NumLock => ModMask::M2, - Modifier::ScrollLock => ModMask::M5, - }) - } -} - -impl TryFrom<&str> for Modifier { - type Error = anyhow::Error; - - fn try_from(val: &str) -> Result<Self> { - match val { - "C" => Ok(Self::Ctrl), - "A" => Ok(Self::Alt), - "S" => Ok(Self::Shift), - "M" => Ok(Self::Super), - "AltGr" => Ok(Self::Alt), - "Num" => Ok(Self::NumLock), - "Scroll" => Ok(Self::ScrollLock), - _ => Err(anyhow!("unable to resolve \"{}\" to modifier", val)), - } - } -} - -impl MouseShortcut { - pub fn from_event( - detail: u8, - state: u16, - ) -> Result<Self> { - Ok(Self { - button: Button::try_from(detail)?, - modifiers: Modifier::iter() - .filter(|&m| { - m.was_held(state) && m != Modifier::NumLock && m != Modifier::ScrollLock - }) - .collect(), - }) - } - - pub fn mask(&self) -> u16 { - self.modifiers - .iter() - .fold(0, |acc, &val| acc | u16::from(val)) - } - - pub fn button(&self) -> u8 { - self.button.into() - } -} - -impl MouseEvent { - pub fn from_press_event( - event: &ButtonPressEvent, - root: Window, - ) -> Result<Self> { - Ok(Self::new( - MouseEventKind::Press, - event.event, - if event.child != x11rb::NONE { - Some(event.child) - } else { - None - }, - root, - event.root_x, - event.root_y, - event.event_x, - event.event_y, - MouseShortcut::from_event(event.detail, event.state)?, - )) - } - - pub fn from_release_event( - event: &ButtonReleaseEvent, - root: Window, - ) -> Result<Self> { - Ok(Self::new( - MouseEventKind::Release, - event.event, - if event.child != x11rb::NONE { - Some(event.child) - } else { - None - }, - root, - event.root_x, - event.root_y, - event.event_x, - event.event_y, - MouseShortcut::from_event(event.detail, event.state)?, - )) - } - - pub fn from_motion_event( - event: &MotionNotifyEvent, - root: Window, - ) -> Result<Self> { - Ok(Self::new( - MouseEventKind::Motion, - event.event, - if event.child != x11rb::NONE { - Some(event.child) - } else { - None - }, - root, - event.root_x, - event.root_y, - event.event_x, - event.event_y, - MouseShortcut::from_event(1, event.state)?, - )) - } -} diff --git a/src/winsys/xdata/xconnection.rs b/src/winsys/xdata/xconnection.rs @@ -15,11 +15,15 @@ use crate::geometry::Strut; use crate::hints::Hints; use crate::hints::SizeHints; use crate::input::Button; +use crate::input::Modifier; use crate::input::Grip; -use crate::input::KeyCode; +use crate::input::Key; +use crate::input::KeyEvent; +use crate::input::MouseEventKind; +use crate::input::MouseInputTarget; +use crate::input::KeyInput; use crate::input::MouseEvent; -use crate::input::MouseEventKey; -use crate::input::MouseShortcut; +use crate::input::MouseInput; use crate::screen::Screen; use crate::window::IcccmWindowState; use crate::window::Window; @@ -28,7 +32,9 @@ use crate::window::WindowType; use crate::Result; use std::cell::Cell; +use std::cell::RefCell; use std::collections::HashMap; +use std::collections::HashSet; use std::convert::TryFrom; use std::str::FromStr; @@ -177,7 +183,8 @@ pub struct XConnection<'conn, Conn: connection::Connection> { background_gc: xproto::Gcontext, database: Option<Database>, confined_to: Cell<Option<Window>>, - + keys: RefCell<HashMap<u8, Key>>, + keycodes: RefCell<HashMap<Key, u8>>, root_event_mask: EventMask, window_event_mask: EventMask, frame_event_mask: EventMask, @@ -285,6 +292,9 @@ impl<'conn, Conn: connection::Connection> XConnection<'conn, Conn> { ); } + let keys = RefCell::new(HashMap::new()); + let keycodes = RefCell::new(HashMap::new()); + let root_event_mask: EventMask = EventMask::PROPERTY_CHANGE | EventMask::SUBSTRUCTURE_REDIRECT | EventMask::STRUCTURE_NOTIFY @@ -317,7 +327,8 @@ impl<'conn, Conn: connection::Connection> XConnection<'conn, Conn> { background_gc, database, confined_to: Cell::new(None), - + keys, + keycodes, root_event_mask, window_event_mask, frame_event_mask, @@ -493,6 +504,331 @@ impl<'conn, Conn: connection::Connection> XConnection<'conn, Conn> { } } + fn get_key( + &self, + keycode: u8, + ) -> Key { + if let Some(&key) = self.keys.borrow().get(&keycode) { + return key; + } + + let key = match keycode { + 9 => Key::Escape, + 10 => Key::One, + 11 => Key::Two, + 12 => Key::Three, + 13 => Key::Four, + 14 => Key::Five, + 15 => Key::Six, + 16 => Key::Seven, + 17 => Key::Eight, + 18 => Key::Nine, + 19 => Key::Zero, + 20 => Key::Minus, + 21 => Key::Equal, + 22 => Key::Backspace, + 23 => Key::Tab, + 24 => Key::Q, + 25 => Key::W, + 26 => Key::E, + 27 => Key::R, + 28 => Key::T, + 29 => Key::Y, + 30 => Key::U, + 31 => Key::I, + 32 => Key::O, + 33 => Key::P, + 34 => Key::LeftBracket, + 35 => Key::RightBracket, + 36 => Key::Return, + 37 => Key::Control, + 38 => Key::A, + 39 => Key::S, + 40 => Key::D, + 41 => Key::F, + 42 => Key::G, + 43 => Key::H, + 44 => Key::J, + 45 => Key::K, + 46 => Key::L, + 47 => Key::SemiColon, + 48 => Key::Apostrophe, + 49 => Key::Tilde, + 50 => Key::Shift, + 51 => Key::BackSlash, + 52 => Key::Z, + 53 => Key::X, + 54 => Key::C, + 55 => Key::V, + 56 => Key::B, + 57 => Key::N, + 58 => Key::M, + 59 => Key::Comma, + 60 => Key::Period, + 61 => Key::Slash, + 62 => Key::RightShift, + 63 => Key::Multiply, + 64 => Key::Alt, + 65 => Key::Space, + 66 => Key::CapsLock, + 67 => Key::F1, + 68 => Key::F2, + 69 => Key::F3, + 70 => Key::F4, + 71 => Key::F5, + 72 => Key::F6, + 73 => Key::F7, + 74 => Key::F8, + 75 => Key::F9, + 76 => Key::F10, + 77 => Key::Numlock, + 78 => Key::ScrollLock, + 79 => Key::NumPad7, + 80 => Key::NumPad8, + 81 => Key::NumPad9, + 82 => Key::Subtract, + 83 => Key::NumPad4, + 84 => Key::NumPad5, + 85 => Key::NumPad6, + 86 => Key::Add, + 87 => Key::NumPad1, + 88 => Key::NumPad2, + 89 => Key::NumPad3, + 90 => Key::NumPad0, + 94 => Key::Less, + 95 => Key::F11, + 96 => Key::F12, + 105 => Key::RightContol, + 106 => Key::Divide, + 107 => Key::PrintScreen, + 108 => Key::RightAlt, + 110 => Key::Home, + 111 => Key::Up, + 112 => Key::PageUp, + 113 => Key::Left, + 114 => Key::Right, + 115 => Key::End, + 116 => Key::Down, + 117 => Key::PageDown, + 118 => Key::Insert, + 119 => Key::Delete, + 121 => Key::VolumeMute, + 122 => Key::VolumeDown, + 123 => Key::VolumeUp, + 127 => Key::Pause, + 128 => Key::LaunchAppA, + 129 => Key::Decimal, + 133 => Key::Super, + 134 => Key::RightSuper, + 135 => Key::Menu, + 146 => Key::Help, + 156 => Key::LaunchApp1, + 157 => Key::LaunchApp2, + 163 => Key::LaunchMail, + 164 => Key::BrowserFavorites, + 166 => Key::BrowserBack, + 167 => Key::BrowserForward, + 171 => Key::NextTrack, + 172 => Key::PlayPause, + 173 => Key::PreviousTrack, + 174 => Key::StopMedia, + 180 => Key::BrowserHome, + 182 => Key::BrowserStop, + 187 => Key::LeftParenthesis, + 188 => Key::RightParenthesis, + 192 => Key::LaunchApp5, + 193 => Key::LaunchApp6, + 194 => Key::LaunchApp7, + 195 => Key::LaunchApp8, + 196 => Key::LaunchApp9, + 210 => Key::LaunchApp3, + 211 => Key::LaunchApp4, + 212 => Key::LaunchAppB, + 225 => Key::BrowserSearch, + 234 => Key::SelectMedia, + _ => return Key::Any, + }; + + self.keys.borrow_mut().insert(keycode, key); + self.keycodes.borrow_mut().insert(key, keycode); + + key + } + + fn get_keycode( + &self, + key: Key, + ) -> u8 { + if let Some(&keycode) = self.keycodes.borrow().get(&key) { + return keycode; + } + + let keycode = match key { + Key::Backspace => 22, + Key::Tab => 23, + Key::Return => 36, + Key::Shift => 50, + Key::Control => 37, + Key::Alt => 64, + Key::Super => 133, + Key::Menu => 135, + Key::Pause => 127, + Key::CapsLock => 66, + Key::Escape => 9, + Key::Space => 65, + Key::ExclamationMark => 10, + Key::QuotationMark => 48, + Key::QuestionMark => 61, + Key::NumberSign => 12, + Key::DollarSign => 13, + Key::PercentSign => 14, + Key::AtSign => 11, + Key::Ampersand => 16, + Key::Apostrophe => 48, + Key::LeftParenthesis => 187, + Key::RightParenthesis => 188, + Key::LeftBracket => 34, + Key::RightBracket => 35, + Key::LeftBrace => 34, + Key::RightBrace => 35, + Key::Underscore => 20, + Key::Grave => 49, + Key::Bar => 51, + Key::Tilde => 49, + Key::QuoteLeft => 49, + Key::Asterisk => 17, + Key::Plus => 21, + Key::Comma => 59, + Key::Minus => 20, + Key::Period => 60, + Key::Slash => 61, + Key::BackSlash => 51, + Key::Colon => 47, + Key::SemiColon => 47, + Key::Less => 94, + Key::Equal => 21, + Key::Greater => 60, + Key::PageUp => 112, + Key::PageDown => 117, + Key::End => 115, + Key::Home => 110, + Key::Left => 113, + Key::Up => 111, + Key::Right => 114, + Key::Down => 116, + Key::Print => 107, + Key::PrintScreen => 107, + Key::Insert => 118, + Key::Delete => 119, + Key::Help => 146, + Key::Zero => 19, + Key::One => 10, + Key::Two => 11, + Key::Three => 12, + Key::Four => 13, + Key::Five => 14, + Key::Six => 15, + Key::Seven => 16, + Key::Eight => 17, + Key::Nine => 18, + Key::A => 38, + Key::B => 56, + Key::C => 54, + Key::D => 40, + Key::E => 26, + Key::F => 41, + Key::G => 42, + Key::H => 43, + Key::I => 31, + Key::J => 44, + Key::K => 45, + Key::L => 46, + Key::M => 58, + Key::N => 57, + Key::O => 32, + Key::P => 33, + Key::Q => 24, + Key::R => 27, + Key::S => 39, + Key::T => 28, + Key::U => 30, + Key::V => 55, + Key::W => 25, + Key::X => 53, + Key::Y => 29, + Key::Z => 52, + Key::NumPad0 => 90, + Key::NumPad1 => 87, + Key::NumPad2 => 88, + Key::NumPad3 => 89, + Key::NumPad4 => 83, + Key::NumPad5 => 84, + Key::NumPad6 => 85, + Key::NumPad7 => 79, + Key::NumPad8 => 80, + Key::NumPad9 => 81, + Key::Multiply => 63, + Key::Add => 86, + Key::Subtract => 82, + Key::Decimal => 129, + Key::Divide => 106, + Key::F1 => 67, + Key::F2 => 68, + Key::F3 => 69, + Key::F4 => 70, + Key::F5 => 71, + Key::F6 => 72, + Key::F7 => 73, + Key::F8 => 74, + Key::F9 => 75, + Key::F10 => 76, + Key::F11 => 95, + Key::F12 => 96, + Key::Numlock => 77, + Key::ScrollLock => 78, + Key::LeftShift => 50, + Key::RightShift => 62, + Key::LeftControl => 37, + Key::RightContol => 105, + Key::LeftAlt => 64, + Key::RightAlt => 108, + Key::LeftSuper => 133, + Key::RightSuper => 134, + Key::BrowserBack => 166, + Key::BrowserForward => 167, + Key::BrowserStop => 182, + Key::BrowserSearch => 225, + Key::BrowserFavorites => 164, + Key::BrowserHome => 180, + Key::VolumeMute => 121, + Key::VolumeDown => 122, + Key::VolumeUp => 123, + Key::NextTrack => 171, + Key::PreviousTrack => 173, + Key::StopMedia => 174, + Key::PlayPause => 172, + Key::LaunchMail => 163, + Key::SelectMedia => 234, + Key::LaunchAppA => 128, + Key::LaunchAppB => 212, + Key::LaunchApp1 => 156, + Key::LaunchApp2 => 157, + Key::LaunchApp3 => 210, + Key::LaunchApp4 => 211, + Key::LaunchApp5 => 192, + Key::LaunchApp6 => 193, + Key::LaunchApp7 => 194, + Key::LaunchApp8 => 195, + Key::LaunchApp9 => 196, + _ => return 0, + }; + + self.keys.borrow_mut().insert(keycode, key); + self.keycodes.borrow_mut().insert(key, keycode); + + keycode + } + fn set_window_state_atom( &self, window: Window, @@ -549,8 +885,64 @@ impl<'conn, Conn: connection::Connection> XConnection<'conn, Conn> { &self, event: &xproto::ButtonPressEvent, ) -> Option<Event> { + let window = event.event; + let window = if window == self.screen.root || window == x11rb::NONE { + if event.child == x11rb::NONE { + None + } else { + Some(event.child) + } + } else { + Some(window) + }; + Some(Event::Mouse { - event: MouseEvent::from_press_event(&event, self.screen.root).ok()?, + event: MouseEvent { + kind: MouseEventKind::Press, + input: MouseInput { + target: MouseInputTarget::Global, + button: { + if let Ok(button) = Button::try_from(event.detail) { + button + } else { + return None; + } + }, + modifiers: { + let mut modifiers = HashSet::new(); + + if event.state & u16::from(xproto::ModMask::CONTROL) > 0 { + modifiers.insert(Modifier::Ctrl); + } + + if event.state & u16::from(xproto::ModMask::SHIFT) > 0 { + modifiers.insert(Modifier::Shift); + } + + if event.state & u16::from(xproto::ModMask::M1) > 0 { + modifiers.insert(Modifier::Alt); + } + + if event.state & u16::from(xproto::ModMask::M4) > 0 { + modifiers.insert(Modifier::Super); + } + + modifiers + }, + }, + window: { + if window == Some(self.screen.root) { + None + } else { + window + } + }, + root_rpos: Pos { + x: event.root_x as i32, + y: event.root_y as i32, + }, + }, + on_root: window == Some(self.screen.root), }) } @@ -559,8 +951,64 @@ impl<'conn, Conn: connection::Connection> XConnection<'conn, Conn> { &self, event: &xproto::ButtonReleaseEvent, ) -> Option<Event> { + let window = event.event; + let window = if window == self.screen.root || window == x11rb::NONE { + if event.child == x11rb::NONE { + None + } else { + Some(event.child) + } + } else { + None + }; + Some(Event::Mouse { - event: MouseEvent::from_release_event(&event, self.screen.root).ok()?, + event: MouseEvent { + kind: MouseEventKind::Release, + input: MouseInput { + target: MouseInputTarget::Global, + button: { + if let Ok(button) = Button::try_from(event.detail) { + button + } else { + return None; + } + }, + modifiers: { + let mut modifiers = HashSet::new(); + + if event.state & u16::from(xproto::ModMask::CONTROL) > 0 { + modifiers.insert(Modifier::Ctrl); + } + + if event.state & u16::from(xproto::ModMask::SHIFT) > 0 { + modifiers.insert(Modifier::Shift); + } + + if event.state & u16::from(xproto::ModMask::M1) > 0 { + modifiers.insert(Modifier::Alt); + } + + if event.state & u16::from(xproto::ModMask::M4) > 0 { + modifiers.insert(Modifier::Super); + } + + modifiers + }, + }, + window: { + if window == Some(self.screen.root) { + None + } else { + window + } + }, + root_rpos: Pos { + x: event.root_x as i32, + y: event.root_y as i32, + }, + }, + on_root: window == Some(self.screen.root), }) } @@ -569,8 +1017,58 @@ impl<'conn, Conn: connection::Connection> XConnection<'conn, Conn> { &self, event: &xproto::MotionNotifyEvent, ) -> Option<Event> { + let window = event.event; + let window = if window == self.screen.root || window == x11rb::NONE { + if event.child == x11rb::NONE { + None + } else { + Some(event.child) + } + } else { + None + }; + Some(Event::Mouse { - event: MouseEvent::from_motion_event(&event, self.screen.root).ok()?, + event: MouseEvent { + kind: MouseEventKind::Motion, + input: MouseInput { + target: MouseInputTarget::Global, + button: Button::Left, + modifiers: { + let mut modifiers = HashSet::new(); + + if event.state & u16::from(xproto::ModMask::CONTROL) > 0 { + modifiers.insert(Modifier::Ctrl); + } + + if event.state & u16::from(xproto::ModMask::SHIFT) > 0 { + modifiers.insert(Modifier::Shift); + } + + if event.state & u16::from(xproto::ModMask::M1) > 0 { + modifiers.insert(Modifier::Alt); + } + + if event.state & u16::from(xproto::ModMask::M4) > 0 { + modifiers.insert(Modifier::Super); + } + + modifiers + }, + }, + window: { + if window == Some(self.screen.root) { + None + } else { + window + } + }, + root_rpos: Pos { + x: event.root_x as i32, + y: event.root_y as i32, + }, + }, + on_root: window == Some(self.screen.root), }) } @@ -580,7 +1078,41 @@ impl<'conn, Conn: connection::Connection> XConnection<'conn, Conn> { event: &xproto::KeyPressEvent, ) -> Option<Event> { Some(Event::Key { - key_code: KeyCode::from_press_event(&event).without_mask(ModMask::M2), + event: KeyEvent { + input: KeyInput { + key: self.get_key(event.detail), + modifiers: { + let mut modifiers = HashSet::new(); + + if event.state & u16::from(xproto::ModMask::CONTROL) > 0 { + modifiers.insert(Modifier::Ctrl); + } + + if event.state & u16::from(xproto::ModMask::SHIFT) > 0 { + modifiers.insert(Modifier::Shift); + } + + if event.state & u16::from(xproto::ModMask::M1) > 0 { + modifiers.insert(Modifier::Alt); + } + + if event.state & u16::from(xproto::ModMask::M4) > 0 { + modifiers.insert(Modifier::Super); + } + + modifiers + }, + }, + window: { + let window = event.event; + + if window == self.screen.root || window == x11rb::NONE { + Some(event.child) + } else { + None + } + }, + } }) } @@ -1504,33 +2036,65 @@ impl<'conn, Conn: connection::Connection> Connection for XConnection<'conn, Conn fn grab_bindings( &self, - key_codes: &[&KeyCode], - mouse_bindings: &[&(MouseEventKey, MouseShortcut)], + key_inputs: &[&KeyInput], + mouse_inputs: &[&MouseInput], ) { for &m in &[0, u16::from(ModMask::M2), u16::from(ModMask::M5)] { - for k in key_codes { - drop(self.conn.grab_key( - false, - self.screen.root, - k.mask | m, - k.code, - xproto::GrabMode::ASYNC, - xproto::GrabMode::ASYNC, - )); + for key_input in key_inputs { + drop( + self.conn.grab_key( + false, + self.screen.root, + key_input + .modifiers + .iter() + .fold(0u16, |acc, &m| acc | { + u16::from(match m { + Modifier::Ctrl => xproto::ModMask::CONTROL, + Modifier::Shift => xproto::ModMask::SHIFT, + Modifier::Alt => xproto::ModMask::M1, + Modifier::Super => xproto::ModMask::M4, + Modifier::NumLock => xproto::ModMask::M2, + Modifier::ScrollLock => xproto::ModMask::M5, + _ => xproto::ModMask::ANY, + }) + }) + | u16::from(m), + self.get_keycode(key_input.key), + xproto::GrabMode::ASYNC, + xproto::GrabMode::ASYNC, + ), + ); } - for (_, state) in mouse_bindings { - drop(self.conn.grab_button( - false, - self.screen.root, - u32::from(self.mouse_event_mask) as u16 | m, - xproto::GrabMode::ASYNC, - xproto::GrabMode::ASYNC, - x11rb::NONE, - x11rb::NONE, - xproto::ButtonIndex::try_from(state.button()).unwrap(), - state.mask() | m, - )); + for mouse_input in mouse_inputs { + drop( + self.conn.grab_button( + false, + self.screen.root, + u32::from(self.mouse_event_mask) as u16, + xproto::GrabMode::ASYNC, + xproto::GrabMode::ASYNC, + x11rb::NONE, + x11rb::NONE, + xproto::ButtonIndex::try_from(mouse_input.button as u8).unwrap(), + mouse_input + .modifiers + .iter() + .fold(0u16, |acc, &m| acc | { + u16::from(match m { + Modifier::Ctrl => xproto::ModMask::CONTROL, + Modifier::Shift => xproto::ModMask::SHIFT, + Modifier::Alt => xproto::ModMask::M1, + Modifier::Super => xproto::ModMask::M4, + Modifier::NumLock => xproto::ModMask::M2, + Modifier::ScrollLock => xproto::ModMask::M5, + _ => xproto::ModMask::ANY, + }) + }) + | u16::from(m), + ), + ); } }