mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
Implement key repeat for compositor binds
This commit is contained in:
committed by
Ivan Molodetskikh
parent
a56e4ff436
commit
d3aebdbec4
@@ -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
@@ -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,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user