Fix stuck HUD on fast modifier release
This commit is contained in:
+90
@@ -40,6 +40,7 @@ enum DaemonMessage {
|
||||
}
|
||||
|
||||
struct Session {
|
||||
id: u64,
|
||||
groups: Vec<AppGroup>,
|
||||
selected: usize,
|
||||
original: usize,
|
||||
@@ -49,6 +50,7 @@ struct Session {
|
||||
struct RuntimeState {
|
||||
history: FocusHistory,
|
||||
session: Option<Session>,
|
||||
next_session_id: u64,
|
||||
window_cycle: Option<WindowCycleSession>,
|
||||
ignore_focus_until: Option<Instant>,
|
||||
}
|
||||
@@ -107,6 +109,7 @@ impl Switcher {
|
||||
state: RefCell::new(RuntimeState {
|
||||
history,
|
||||
session: None,
|
||||
next_session_id: 0,
|
||||
window_cycle: None,
|
||||
ignore_focus_until: None,
|
||||
}),
|
||||
@@ -149,7 +152,57 @@ impl Switcher {
|
||||
}
|
||||
});
|
||||
|
||||
let switcher = Rc::clone(self);
|
||||
controller.connect_modifiers(move |_controller, modifiers| {
|
||||
if !switch_modifier_is_pressed(modifiers) {
|
||||
switcher.finish(true);
|
||||
}
|
||||
Propagation::Proceed
|
||||
});
|
||||
|
||||
self.window.add_controller(controller);
|
||||
|
||||
let focus_controller = gtk::EventControllerLegacy::new();
|
||||
let switcher = Rc::clone(self);
|
||||
focus_controller.connect_event(move |_controller, event| {
|
||||
let Some(focus_event) = event.downcast_ref::<gdk::FocusEvent>() else {
|
||||
return Propagation::Proceed;
|
||||
};
|
||||
if !focus_event.is_in() {
|
||||
return Propagation::Proceed;
|
||||
}
|
||||
|
||||
let session_id = switcher
|
||||
.state
|
||||
.borrow()
|
||||
.session
|
||||
.as_ref()
|
||||
.map(|session| session.id);
|
||||
let Some(session_id) = session_id else {
|
||||
return Propagation::Proceed;
|
||||
};
|
||||
|
||||
let switcher = Rc::clone(&switcher);
|
||||
// The Wayland keyboard-enter and modifier updates can arrive in
|
||||
// adjacent dispatches. Let GDK apply both before checking whether
|
||||
// Super was released before the exclusive grab took effect.
|
||||
glib::timeout_add_local_once(Duration::from_millis(10), move || {
|
||||
let is_current_session = switcher
|
||||
.state
|
||||
.borrow()
|
||||
.session
|
||||
.as_ref()
|
||||
.is_some_and(|session| session.id == session_id);
|
||||
if is_current_session
|
||||
&& current_keyboard_modifiers()
|
||||
.is_some_and(|modifiers| !switch_modifier_is_pressed(modifiers))
|
||||
{
|
||||
switcher.finish(true);
|
||||
}
|
||||
});
|
||||
Propagation::Proceed
|
||||
});
|
||||
self.window.add_controller(focus_controller);
|
||||
}
|
||||
|
||||
fn handle_message(&self, message: DaemonMessage) {
|
||||
@@ -247,7 +300,10 @@ impl Switcher {
|
||||
(false, Direction::Next) => (original + 1) % groups.len(),
|
||||
(false, Direction::Previous) => (original + groups.len() - 1) % groups.len(),
|
||||
};
|
||||
let session_id = state.next_session_id;
|
||||
state.next_session_id = state.next_session_id.wrapping_add(1);
|
||||
state.session = Some(Session {
|
||||
id: session_id,
|
||||
groups,
|
||||
selected,
|
||||
original,
|
||||
@@ -503,6 +559,17 @@ impl Switcher {
|
||||
}
|
||||
}
|
||||
|
||||
fn switch_modifier_is_pressed(modifiers: gdk::ModifierType) -> bool {
|
||||
modifiers.intersects(gdk::ModifierType::SUPER_MASK | gdk::ModifierType::META_MASK)
|
||||
}
|
||||
|
||||
fn current_keyboard_modifiers() -> Option<gdk::ModifierType> {
|
||||
gdk::Display::default()?
|
||||
.default_seat()?
|
||||
.keyboard()
|
||||
.map(|keyboard| keyboard.modifier_state())
|
||||
}
|
||||
|
||||
fn install_css() {
|
||||
let provider = gtk::CssProvider::new();
|
||||
provider.load_from_data(
|
||||
@@ -657,3 +724,26 @@ fn main() -> Result<()> {
|
||||
Some(command) => anyhow::bail!("unknown command: {command}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn switch_modifier_accepts_super_and_meta_mappings() {
|
||||
assert!(switch_modifier_is_pressed(gdk::ModifierType::SUPER_MASK));
|
||||
assert!(switch_modifier_is_pressed(gdk::ModifierType::META_MASK));
|
||||
assert!(switch_modifier_is_pressed(
|
||||
gdk::ModifierType::SHIFT_MASK | gdk::ModifierType::SUPER_MASK
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn switch_modifier_rejects_unrelated_modifiers() {
|
||||
assert!(!switch_modifier_is_pressed(gdk::ModifierType::empty()));
|
||||
assert!(!switch_modifier_is_pressed(gdk::ModifierType::SHIFT_MASK));
|
||||
assert!(!switch_modifier_is_pressed(
|
||||
gdk::ModifierType::CONTROL_MASK | gdk::ModifierType::ALT_MASK
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user