Implement key repeat for compositor binds

This commit is contained in:
Salman Farooq
2024-06-30 22:37:44 +05:00
committed by Ivan Molodetskikh
parent a56e4ff436
commit d3aebdbec4
3 changed files with 73 additions and 2 deletions
+16
View File
@@ -886,6 +886,7 @@ pub struct Binds(pub Vec<Bind>);
pub struct Bind { pub struct Bind {
pub key: Key, pub key: Key,
pub action: Action, pub action: Action,
pub repeat: bool,
pub cooldown: Option<Duration>, pub cooldown: Option<Duration>,
pub allow_when_locked: bool, pub allow_when_locked: bool,
} }
@@ -2217,11 +2218,15 @@ where
.parse::<Key>() .parse::<Key>()
.map_err(|e| DecodeError::conversion(&node.node_name, e.wrap_err("invalid keybind")))?; .map_err(|e| DecodeError::conversion(&node.node_name, e.wrap_err("invalid keybind")))?;
let mut repeat = true;
let mut cooldown = None; let mut cooldown = None;
let mut allow_when_locked = false; let mut allow_when_locked = false;
let mut allow_when_locked_node = None; let mut allow_when_locked_node = None;
for (name, val) in &node.properties { for (name, val) in &node.properties {
match &***name { match &***name {
"repeat" => {
repeat = knuffel::traits::DecodeScalar::decode(val, ctx)?;
}
"cooldown-ms" => { "cooldown-ms" => {
cooldown = Some(Duration::from_millis( cooldown = Some(Duration::from_millis(
knuffel::traits::DecodeScalar::decode(val, ctx)?, knuffel::traits::DecodeScalar::decode(val, ctx)?,
@@ -2249,6 +2254,7 @@ where
let dummy = Self { let dummy = Self {
key, key,
action: Action::Spawn(vec![]), action: Action::Spawn(vec![]),
repeat: true,
cooldown: None, cooldown: None,
allow_when_locked: false, allow_when_locked: false,
}; };
@@ -2276,6 +2282,7 @@ where
Ok(Self { Ok(Self {
key, key,
action, action,
repeat,
cooldown, cooldown,
allow_when_locked, allow_when_locked,
}) })
@@ -2844,6 +2851,7 @@ mod tests {
modifiers: Modifiers::COMPOSITOR, modifiers: Modifiers::COMPOSITOR,
}, },
action: Action::Spawn(vec!["alacritty".to_owned()]), action: Action::Spawn(vec!["alacritty".to_owned()]),
repeat: true,
cooldown: None, cooldown: None,
allow_when_locked: true, allow_when_locked: true,
}, },
@@ -2853,6 +2861,7 @@ mod tests {
modifiers: Modifiers::COMPOSITOR, modifiers: Modifiers::COMPOSITOR,
}, },
action: Action::CloseWindow, action: Action::CloseWindow,
repeat: true,
cooldown: None, cooldown: None,
allow_when_locked: false, allow_when_locked: false,
}, },
@@ -2862,6 +2871,7 @@ mod tests {
modifiers: Modifiers::COMPOSITOR | Modifiers::SHIFT, modifiers: Modifiers::COMPOSITOR | Modifiers::SHIFT,
}, },
action: Action::FocusMonitorLeft, action: Action::FocusMonitorLeft,
repeat: true,
cooldown: None, cooldown: None,
allow_when_locked: false, allow_when_locked: false,
}, },
@@ -2871,6 +2881,7 @@ mod tests {
modifiers: Modifiers::COMPOSITOR | Modifiers::SHIFT | Modifiers::CTRL, modifiers: Modifiers::COMPOSITOR | Modifiers::SHIFT | Modifiers::CTRL,
}, },
action: Action::MoveWindowToMonitorRight, action: Action::MoveWindowToMonitorRight,
repeat: true,
cooldown: None, cooldown: None,
allow_when_locked: false, allow_when_locked: false,
}, },
@@ -2880,6 +2891,7 @@ mod tests {
modifiers: Modifiers::COMPOSITOR, modifiers: Modifiers::COMPOSITOR,
}, },
action: Action::ConsumeWindowIntoColumn, action: Action::ConsumeWindowIntoColumn,
repeat: true,
cooldown: None, cooldown: None,
allow_when_locked: false, allow_when_locked: false,
}, },
@@ -2889,6 +2901,7 @@ mod tests {
modifiers: Modifiers::COMPOSITOR, modifiers: Modifiers::COMPOSITOR,
}, },
action: Action::FocusWorkspace(WorkspaceReference::Index(1)), action: Action::FocusWorkspace(WorkspaceReference::Index(1)),
repeat: true,
cooldown: None, cooldown: None,
allow_when_locked: false, allow_when_locked: false,
}, },
@@ -2900,6 +2913,7 @@ mod tests {
action: Action::FocusWorkspace(WorkspaceReference::Name( action: Action::FocusWorkspace(WorkspaceReference::Name(
"workspace-1".to_string(), "workspace-1".to_string(),
)), )),
repeat: true,
cooldown: None, cooldown: None,
allow_when_locked: false, allow_when_locked: false,
}, },
@@ -2909,6 +2923,7 @@ mod tests {
modifiers: Modifiers::COMPOSITOR | Modifiers::SHIFT, modifiers: Modifiers::COMPOSITOR | Modifiers::SHIFT,
}, },
action: Action::Quit(true), action: Action::Quit(true),
repeat: true,
cooldown: None, cooldown: None,
allow_when_locked: false, allow_when_locked: false,
}, },
@@ -2918,6 +2933,7 @@ mod tests {
modifiers: Modifiers::COMPOSITOR, modifiers: Modifiers::COMPOSITOR,
}, },
action: Action::FocusWorkspaceDown, action: Action::FocusWorkspaceDown,
repeat: true,
cooldown: Some(Duration::from_millis(150)), cooldown: Some(Duration::from_millis(150)),
allow_when_locked: false, allow_when_locked: false,
}, },
+55 -2
View File
@@ -294,6 +294,19 @@ impl State {
let time = Event::time_msec(&event); let time = Event::time_msec(&event);
let pressed = event.state() == KeyState::Pressed; let pressed = event.state() == KeyState::Pressed;
// Stop bind key repeat on any release. This won't work 100% correctly in cases like:
// 1. Press Mod
// 2. Press Left (repeat starts)
// 3. Press PgDown (new repeat starts)
// 4. Release Left (PgDown repeat stops)
// But it's good enough for now.
// FIXME: handle this properly.
if !pressed {
if let Some(token) = self.niri.bind_repeat_timer.take() {
self.niri.event_loop.remove(token);
}
}
let Some(Some(bind)) = self.niri.seat.get_keyboard().unwrap().input( let Some(Some(bind)) = self.niri.seat.get_keyboard().unwrap().input(
self, self,
event.key_code(), event.key_code(),
@@ -330,12 +343,44 @@ impl State {
return; return;
}; };
// Filter actions when the key is released or the session is locked.
if !pressed { if !pressed {
return; return;
} }
self.handle_bind(bind); self.handle_bind(bind.clone());
// Start the key repeat timer if necessary.
if !bind.repeat {
return;
}
// Stop the previous key repeat if any.
if let Some(token) = self.niri.bind_repeat_timer.take() {
self.niri.event_loop.remove(token);
}
let config = self.niri.config.borrow();
let config = &config.input.keyboard;
let repeat_rate = config.repeat_rate;
if repeat_rate == 0 {
return;
}
let repeat_duration = Duration::from_secs_f64(1. / f64::from(repeat_rate));
let repeat_timer =
Timer::from_duration(Duration::from_millis(u64::from(config.repeat_delay)));
let token = self
.niri
.event_loop
.insert_source(repeat_timer, move |_, _, state| {
state.handle_bind(bind.clone());
TimeoutAction::ToDuration(repeat_duration)
})
.unwrap();
self.niri.bind_repeat_timer = Some(token);
} }
pub fn handle_bind(&mut self, bind: Bind) { pub fn handle_bind(&mut self, bind: Bind) {
@@ -2132,6 +2177,7 @@ fn should_intercept_key(
modifiers: Modifiers::empty(), modifiers: Modifiers::empty(),
}, },
action, action,
repeat: true,
cooldown: None, cooldown: None,
allow_when_locked: false, allow_when_locked: false,
}); });
@@ -2181,6 +2227,7 @@ fn find_bind(
modifiers: Modifiers::empty(), modifiers: Modifiers::empty(),
}, },
action, action,
repeat: true,
cooldown: None, cooldown: None,
allow_when_locked: false, allow_when_locked: false,
}); });
@@ -2512,6 +2559,7 @@ mod tests {
modifiers: Modifiers::COMPOSITOR | Modifiers::CTRL, modifiers: Modifiers::COMPOSITOR | Modifiers::CTRL,
}, },
action: Action::CloseWindow, action: Action::CloseWindow,
repeat: true,
cooldown: None, cooldown: None,
allow_when_locked: false, allow_when_locked: false,
}]); }]);
@@ -2645,6 +2693,7 @@ mod tests {
modifiers: Modifiers::COMPOSITOR, modifiers: Modifiers::COMPOSITOR,
}, },
action: Action::CloseWindow, action: Action::CloseWindow,
repeat: true,
cooldown: None, cooldown: None,
allow_when_locked: false, allow_when_locked: false,
}, },
@@ -2654,6 +2703,7 @@ mod tests {
modifiers: Modifiers::SUPER, modifiers: Modifiers::SUPER,
}, },
action: Action::FocusColumnLeft, action: Action::FocusColumnLeft,
repeat: true,
cooldown: None, cooldown: None,
allow_when_locked: false, allow_when_locked: false,
}, },
@@ -2663,6 +2713,7 @@ mod tests {
modifiers: Modifiers::empty(), modifiers: Modifiers::empty(),
}, },
action: Action::FocusWindowDown, action: Action::FocusWindowDown,
repeat: true,
cooldown: None, cooldown: None,
allow_when_locked: false, allow_when_locked: false,
}, },
@@ -2672,6 +2723,7 @@ mod tests {
modifiers: Modifiers::COMPOSITOR | Modifiers::SUPER, modifiers: Modifiers::COMPOSITOR | Modifiers::SUPER,
}, },
action: Action::FocusWindowUp, action: Action::FocusWindowUp,
repeat: true,
cooldown: None, cooldown: None,
allow_when_locked: false, allow_when_locked: false,
}, },
@@ -2681,6 +2733,7 @@ mod tests {
modifiers: Modifiers::SUPER | Modifiers::ALT, modifiers: Modifiers::SUPER | Modifiers::ALT,
}, },
action: Action::FocusColumnRight, action: Action::FocusColumnRight,
repeat: true,
cooldown: None, cooldown: None,
allow_when_locked: false, allow_when_locked: false,
}, },
+2
View File
@@ -234,6 +234,7 @@ pub struct Niri {
/// Scancodes of the keys to suppress. /// Scancodes of the keys to suppress.
pub suppressed_keys: HashSet<u32>, pub suppressed_keys: HashSet<u32>,
pub bind_cooldown_timers: HashMap<Key, RegistrationToken>, pub bind_cooldown_timers: HashMap<Key, RegistrationToken>,
pub bind_repeat_timer: Option<RegistrationToken>,
pub keyboard_focus: KeyboardFocus, pub keyboard_focus: KeyboardFocus,
pub idle_inhibiting_surfaces: HashSet<WlSurface>, pub idle_inhibiting_surfaces: HashSet<WlSurface>,
pub is_fdo_idle_inhibited: Arc<AtomicBool>, pub is_fdo_idle_inhibited: Arc<AtomicBool>,
@@ -1657,6 +1658,7 @@ impl Niri {
popup_grab: None, popup_grab: None,
suppressed_keys: HashSet::new(), suppressed_keys: HashSet::new(),
bind_cooldown_timers: HashMap::new(), bind_cooldown_timers: HashMap::new(),
bind_repeat_timer: Option::default(),
presentation_state, presentation_state,
security_context_state, security_context_state,
gamma_control_manager_state, gamma_control_manager_state,