From b12396d8a1d762f4de56e3b3b17779375a56ea76 Mon Sep 17 00:00:00 2001 From: rustysec Date: Fri, 31 May 2024 07:26:14 -0700 Subject: [PATCH 1/7] keyboard configs --- niri-config/src/lib.rs | 32 +++++++++++++++++++++++++++----- src/input/mod.rs | 17 +++++++++++++++++ src/niri.rs | 32 ++++++++++++++++++++++---------- 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index cbf70e5..0db8432 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -59,8 +59,8 @@ pub struct Config { // FIXME: Add other devices. #[derive(knuffel::Decode, Debug, Default, PartialEq)] pub struct Input { - #[knuffel(child, default)] - pub keyboard: Keyboard, + #[knuffel(children(name = "keyboard"), default)] + pub keyboards: Vec, #[knuffel(child, default)] pub touchpad: Touchpad, #[knuffel(child, default)] @@ -81,8 +81,28 @@ pub struct Input { pub workspace_auto_back_and_forth: bool, } -#[derive(knuffel::Decode, Debug, PartialEq, Eq)] +impl Input { + pub fn default_keyboard(&self) -> Keyboard { + self.keyboards + .iter() + .find(|keyboard| keyboard.name.is_none()) + .cloned() + .unwrap_or_default() + } + + pub fn keyboard_named>(&self, name: K) -> Keyboard { + self.keyboards + .iter() + .find(|keyboard| keyboard.name.as_deref() == Some(name.as_ref())) + .cloned() + .unwrap_or_default() + } +} + +#[derive(knuffel::Decode, Debug, PartialEq, Eq, Clone)] pub struct Keyboard { + #[knuffel(argument, unwrap(argument))] + pub name: Option, #[knuffel(child, default)] pub xkb: Xkb, // The defaults were chosen to match wlroots and sway. @@ -97,6 +117,7 @@ pub struct Keyboard { impl Default for Keyboard { fn default() -> Self { Self { + name: None, xkb: Default::default(), repeat_delay: 600, repeat_rate: 25, @@ -143,7 +164,7 @@ pub enum CenterFocusedColumn { OnOverflow, } -#[derive(knuffel::DecodeScalar, Debug, Default, PartialEq, Eq)] +#[derive(knuffel::DecodeScalar, Debug, Default, PartialEq, Eq, Clone)] pub enum TrackLayout { /// The layout change is global. #[default] @@ -2509,7 +2530,7 @@ mod tests { "##, Config { input: Input { - keyboard: Keyboard { + keyboards: Keyboard { xkb: Xkb { layout: "us,ru".to_owned(), options: Some("grp:win_space_toggle".to_owned()), @@ -2518,6 +2539,7 @@ mod tests { repeat_delay: 600, repeat_rate: 25, track_layout: TrackLayout::Window, + ..Default::default() }, touchpad: Touchpad { off: false, diff --git a/src/input/mod.rs b/src/input/mod.rs index 40607ad..6b14f94 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -269,6 +269,23 @@ impl State { } fn on_keyboard(&mut self, event: I::KeyboardKeyEvent) { + { + let device = event.device(); + let xkb = self + .niri + .config + .borrow() + .input + .keyboard_named(device.name()) + .xkb; + + let keyboard = self.niri.seat.get_keyboard().unwrap(); + + if let Err(err) = keyboard.set_xkb_config(self, xkb.to_xkb_config()) { + warn!("error updating xkb config: {err:?}"); + } + } + let comp_mod = self.backend.mod_key(); let serial = SERIAL_COUNTER.next_serial(); diff --git a/src/niri.rs b/src/niri.rs index c03edd1..2346723 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -808,7 +808,15 @@ impl State { } } - if self.niri.config.borrow().input.keyboard.track_layout == TrackLayout::Window { + if self + .niri + .config + .borrow() + .input + .default_keyboard() + .track_layout + == TrackLayout::Window + { let current_layout = keyboard.with_xkb_state(self, |context| context.active_layout()); @@ -908,19 +916,23 @@ impl State { self.niri.cursor_texture_cache.clear(); } + let default_keyboard = config.input.default_keyboard(); + + let old_keyboard = old_config.input.default_keyboard(); + // We need &mut self to reload the xkb config, so just store it here. - if config.input.keyboard.xkb != old_config.input.keyboard.xkb { - reload_xkb = Some(config.input.keyboard.xkb.clone()); + if default_keyboard.xkb != old_keyboard.xkb { + reload_xkb = Some(default_keyboard.xkb.clone()); } // Reload the repeat info. - if config.input.keyboard.repeat_rate != old_config.input.keyboard.repeat_rate - || config.input.keyboard.repeat_delay != old_config.input.keyboard.repeat_delay + if default_keyboard.repeat_rate != old_keyboard.repeat_rate + || default_keyboard.repeat_delay != old_keyboard.repeat_delay { let keyboard = self.niri.seat.get_keyboard().unwrap(); keyboard.change_repeat_info( - config.input.keyboard.repeat_rate.into(), - config.input.keyboard.repeat_delay.into(), + default_keyboard.repeat_rate.into(), + default_keyboard.repeat_delay.into(), ); } @@ -1375,9 +1387,9 @@ impl Niri { let mut seat: Seat = seat_state.new_wl_seat(&display_handle, backend.seat_name()); seat.add_keyboard( - config_.input.keyboard.xkb.to_xkb_config(), - config_.input.keyboard.repeat_delay.into(), - config_.input.keyboard.repeat_rate.into(), + config_.input.default_keyboard().xkb.to_xkb_config(), + config_.input.default_keyboard().repeat_delay.into(), + config_.input.default_keyboard().repeat_rate.into(), ) .unwrap(); seat.add_pointer(); From f76b3bb79468e7f1efd5c2ff3997c14117410fac Mon Sep 17 00:00:00 2001 From: rustysec Date: Sat, 1 Jun 2024 06:05:16 -0700 Subject: [PATCH 2/7] per keyboard repeat config --- src/input/mod.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 6b14f94..5bb78f6 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -271,19 +271,24 @@ impl State { fn on_keyboard(&mut self, event: I::KeyboardKeyEvent) { { let device = event.device(); - let xkb = self + + let keyboard_config = self .niri .config .borrow() .input - .keyboard_named(device.name()) - .xkb; + .keyboard_named(device.name()); let keyboard = self.niri.seat.get_keyboard().unwrap(); - if let Err(err) = keyboard.set_xkb_config(self, xkb.to_xkb_config()) { + if let Err(err) = keyboard.set_xkb_config(self, keyboard_config.xkb.to_xkb_config()) { warn!("error updating xkb config: {err:?}"); } + + keyboard.change_repeat_info( + keyboard_config.repeat_rate.into(), + keyboard_config.repeat_delay.into(), + ); } let comp_mod = self.backend.mod_key(); From 5da2ad89dd6c766bbcb1146fffea38128bf57016 Mon Sep 17 00:00:00 2001 From: rustysec Date: Sat, 1 Jun 2024 10:30:21 -0700 Subject: [PATCH 3/7] clean up and keyboard config caching --- src/input/mod.rs | 48 +++++++++++++++++++++++++++--------------------- src/niri.rs | 4 ++++ 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 5bb78f6..987f7ed 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -269,27 +269,7 @@ impl State { } fn on_keyboard(&mut self, event: I::KeyboardKeyEvent) { - { - let device = event.device(); - - let keyboard_config = self - .niri - .config - .borrow() - .input - .keyboard_named(device.name()); - - let keyboard = self.niri.seat.get_keyboard().unwrap(); - - if let Err(err) = keyboard.set_xkb_config(self, keyboard_config.xkb.to_xkb_config()) { - warn!("error updating xkb config: {err:?}"); - } - - keyboard.change_repeat_info( - keyboard_config.repeat_rate.into(), - keyboard_config.repeat_delay.into(), - ); - } + self.apply_keyboard_config::(&event); let comp_mod = self.backend.mod_key(); @@ -341,6 +321,32 @@ impl State { self.handle_bind(bind); } + fn apply_keyboard_config(&mut self, event: &I::KeyboardKeyEvent) { + let device = event.device(); + + let keyboard_config = self + .niri + .config + .borrow() + .input + .keyboard_named(device.name()); + + if keyboard_config != self.niri.current_keyboard { + let keyboard = self.niri.seat.get_keyboard().unwrap(); + + if let Err(err) = keyboard.set_xkb_config(self, keyboard_config.xkb.to_xkb_config()) { + warn!("error updating xkb config: {err:?}"); + } + + keyboard.change_repeat_info( + keyboard_config.repeat_rate.into(), + keyboard_config.repeat_delay.into(), + ); + + self.niri.current_keyboard = keyboard_config; + } + } + pub fn handle_bind(&mut self, bind: Bind) { let Some(cooldown) = bind.cooldown else { self.do_action(bind.action, bind.allow_when_locked); diff --git a/src/niri.rs b/src/niri.rs index 2346723..9c645d4 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -271,6 +271,8 @@ pub struct Niri { // Casts are dropped before PipeWire to prevent a double-free (yay). pub casts: Vec, pub pipewire: Option, + + pub current_keyboard: niri_config::Keyboard, } pub struct OutputState { @@ -1587,6 +1589,8 @@ impl Niri { pipewire, casts: vec![], + + current_keyboard: Default::default(), } } From 13911640b05135055d154ea545c458af0b8559d5 Mon Sep 17 00:00:00 2001 From: rustysec Date: Sat, 1 Jun 2024 11:20:59 -0700 Subject: [PATCH 4/7] fixing tests for multi keyboard --- niri-config/src/lib.rs | 45 +++++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index 0db8432..f23b14e 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -2368,6 +2368,16 @@ mod tests { } } + keyboard "test_keyboard" { + repeat-delay 500 + repeat-rate 30 + track-layout "window" + xkb { + layout "us,ru" + options "grp:win_space_toggle" + } + } + touchpad { tap dwt @@ -2530,17 +2540,30 @@ mod tests { "##, Config { input: Input { - keyboards: Keyboard { - xkb: Xkb { - layout: "us,ru".to_owned(), - options: Some("grp:win_space_toggle".to_owned()), + keyboards: vec![ + Keyboard { + xkb: Xkb { + layout: "us,ru".to_owned(), + options: Some("grp:win_space_toggle".to_owned()), + ..Default::default() + }, + repeat_delay: 600, + repeat_rate: 25, + track_layout: TrackLayout::Window, ..Default::default() }, - repeat_delay: 600, - repeat_rate: 25, - track_layout: TrackLayout::Window, - ..Default::default() - }, + Keyboard { + xkb: Xkb { + layout: "us,ru".to_owned(), + options: Some("grp:win_space_toggle".to_owned()), + ..Default::default() + }, + repeat_delay: 500, + repeat_rate: 30, + track_layout: TrackLayout::Window, + name: Some("test_keyboard".to_owned()), + }, + ], touchpad: Touchpad { off: false, tap: true, @@ -2936,7 +2959,7 @@ mod tests { #[test] fn default_repeat_params() { let config = Config::parse("config.kdl", "").unwrap(); - assert_eq!(config.input.keyboard.repeat_delay, 600); - assert_eq!(config.input.keyboard.repeat_rate, 25); + assert_eq!(config.input.default_keyboard().repeat_delay, 600); + assert_eq!(config.input.default_keyboard().repeat_rate, 25); } } From 176de913427ebabe4c61f447ec8ee2b9e1dc03bd Mon Sep 17 00:00:00 2001 From: rustysec Date: Sun, 2 Jun 2024 11:28:06 -0700 Subject: [PATCH 5/7] pr feedback part one --- niri-config/src/lib.rs | 8 ++++---- src/niri.rs | 15 +++++++-------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index f23b14e..c736023 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -82,7 +82,7 @@ pub struct Input { } impl Input { - pub fn default_keyboard(&self) -> Keyboard { + pub fn fallback_keyboard(&self) -> Keyboard { self.keyboards .iter() .find(|keyboard| keyboard.name.is_none()) @@ -95,7 +95,7 @@ impl Input { .iter() .find(|keyboard| keyboard.name.as_deref() == Some(name.as_ref())) .cloned() - .unwrap_or_default() + .unwrap_or_else(|| self.fallback_keyboard()) } } @@ -2959,7 +2959,7 @@ mod tests { #[test] fn default_repeat_params() { let config = Config::parse("config.kdl", "").unwrap(); - assert_eq!(config.input.default_keyboard().repeat_delay, 600); - assert_eq!(config.input.default_keyboard().repeat_rate, 25); + assert_eq!(config.input.fallback_keyboard().repeat_delay, 600); + assert_eq!(config.input.fallback_keyboard().repeat_rate, 25); } } diff --git a/src/niri.rs b/src/niri.rs index 9c645d4..2143a62 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -182,6 +182,7 @@ pub struct Niri { // When false, we're idling with monitors powered off. pub monitors_active: bool, + pub current_keyboard: niri_config::Keyboard, pub devices: HashSet, pub tablets: HashMap, pub touch: HashSet, @@ -271,8 +272,6 @@ pub struct Niri { // Casts are dropped before PipeWire to prevent a double-free (yay). pub casts: Vec, pub pipewire: Option, - - pub current_keyboard: niri_config::Keyboard, } pub struct OutputState { @@ -815,7 +814,7 @@ impl State { .config .borrow() .input - .default_keyboard() + .fallback_keyboard() .track_layout == TrackLayout::Window { @@ -918,9 +917,9 @@ impl State { self.niri.cursor_texture_cache.clear(); } - let default_keyboard = config.input.default_keyboard(); + let default_keyboard = config.input.fallback_keyboard(); - let old_keyboard = old_config.input.default_keyboard(); + let old_keyboard = old_config.input.fallback_keyboard(); // We need &mut self to reload the xkb config, so just store it here. if default_keyboard.xkb != old_keyboard.xkb { @@ -1389,9 +1388,9 @@ impl Niri { let mut seat: Seat = seat_state.new_wl_seat(&display_handle, backend.seat_name()); seat.add_keyboard( - config_.input.default_keyboard().xkb.to_xkb_config(), - config_.input.default_keyboard().repeat_delay.into(), - config_.input.default_keyboard().repeat_rate.into(), + config_.input.fallback_keyboard().xkb.to_xkb_config(), + config_.input.fallback_keyboard().repeat_delay.into(), + config_.input.fallback_keyboard().repeat_rate.into(), ) .unwrap(); seat.add_pointer(); From 7bcb649d7adf91940ddee2d5a1bc3eeaacb79611 Mon Sep 17 00:00:00 2001 From: rustysec Date: Mon, 3 Jun 2024 06:27:34 -0700 Subject: [PATCH 6/7] keyboard device config cache --- src/input/mod.rs | 56 ++++++++++++++++++++++++++++++++++++------------ src/niri.rs | 5 +++-- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 987f7ed..7fdefe6 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -268,7 +268,10 @@ impl State { Some(pos + output_geo.loc.to_f64()) } - fn on_keyboard(&mut self, event: I::KeyboardKeyEvent) { + fn on_keyboard(&mut self, event: I::KeyboardKeyEvent) + where + I::Device: 'static, + { self.apply_keyboard_config::(&event); let comp_mod = self.backend.mod_key(); @@ -321,27 +324,52 @@ impl State { self.handle_bind(bind); } - fn apply_keyboard_config(&mut self, event: &I::KeyboardKeyEvent) { + fn apply_keyboard_config(&mut self, event: &I::KeyboardKeyEvent) + where + I::Device: 'static, + { let device = event.device(); - let keyboard_config = self - .niri - .config - .borrow() - .input - .keyboard_named(device.name()); + let keyboard_config = + if let Some(input_device) = (&device as &dyn Any).downcast_ref::() { + if let Some(cached) = self.niri.keyboard_device_cache.get(input_device).cloned() { + cached + } else { + let keyboard_config = self + .niri + .config + .borrow() + .input + .keyboard_named(device.name()); + + self.niri + .keyboard_device_cache + .insert(input_device.clone(), keyboard_config.clone()); + + keyboard_config + } + } else { + self.niri.config.borrow().input.fallback_keyboard() + }; if keyboard_config != self.niri.current_keyboard { let keyboard = self.niri.seat.get_keyboard().unwrap(); - if let Err(err) = keyboard.set_xkb_config(self, keyboard_config.xkb.to_xkb_config()) { - warn!("error updating xkb config: {err:?}"); + if keyboard_config.xkb != self.niri.current_keyboard.xkb { + if let Err(err) = keyboard.set_xkb_config(self, keyboard_config.xkb.to_xkb_config()) + { + warn!("error updating xkb config: {err:?}"); + } } - keyboard.change_repeat_info( - keyboard_config.repeat_rate.into(), - keyboard_config.repeat_delay.into(), - ); + if keyboard_config.repeat_rate != self.niri.current_keyboard.repeat_rate + || keyboard_config.repeat_delay != self.niri.current_keyboard.repeat_delay + { + keyboard.change_repeat_info( + keyboard_config.repeat_rate.into(), + keyboard_config.repeat_delay.into(), + ); + } self.niri.current_keyboard = keyboard_config; } diff --git a/src/niri.rs b/src/niri.rs index 2143a62..195f0c6 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -184,6 +184,7 @@ pub struct Niri { pub current_keyboard: niri_config::Keyboard, pub devices: HashSet, + pub keyboard_device_cache: HashMap, pub tablets: HashMap, pub touch: HashSet, @@ -1506,7 +1507,9 @@ impl Niri { root_surface: HashMap::new(), monitors_active: true, + current_keyboard: Default::default(), devices: HashSet::new(), + keyboard_device_cache: HashMap::new(), tablets: HashMap::new(), touch: HashSet::new(), @@ -1588,8 +1591,6 @@ impl Niri { pipewire, casts: vec![], - - current_keyboard: Default::default(), } } From c498ffcc34f7125d5f0d553d6b3036aa2f4dda9a Mon Sep 17 00:00:00 2001 From: rustysec Date: Mon, 3 Jun 2024 10:46:24 -0700 Subject: [PATCH 7/7] use current keyboard for reload comparisons --- src/niri.rs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/niri.rs b/src/niri.rs index 195f0c6..8817452 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -810,15 +810,7 @@ impl State { } } - if self - .niri - .config - .borrow() - .input - .fallback_keyboard() - .track_layout - == TrackLayout::Window - { + if self.niri.current_keyboard.track_layout == TrackLayout::Window { let current_layout = keyboard.with_xkb_state(self, |context| context.active_layout()); @@ -920,16 +912,16 @@ impl State { let default_keyboard = config.input.fallback_keyboard(); - let old_keyboard = old_config.input.fallback_keyboard(); + let current_keyboard = self.niri.current_keyboard.clone(); // We need &mut self to reload the xkb config, so just store it here. - if default_keyboard.xkb != old_keyboard.xkb { + if default_keyboard.xkb != current_keyboard.xkb { reload_xkb = Some(default_keyboard.xkb.clone()); } // Reload the repeat info. - if default_keyboard.repeat_rate != old_keyboard.repeat_rate - || default_keyboard.repeat_delay != old_keyboard.repeat_delay + if default_keyboard.repeat_rate != current_keyboard.repeat_rate + || default_keyboard.repeat_delay != current_keyboard.repeat_delay { let keyboard = self.niri.seat.get_keyboard().unwrap(); keyboard.change_repeat_info(