modkey
This commit is contained in:
+99
-11
@@ -69,6 +69,26 @@ fn is_trigger_modifier(code: u32) -> bool {
|
||||
)
|
||||
}
|
||||
|
||||
/// Keys that should fire repeatedly while held.
|
||||
fn is_repeatable_key(code: u32) -> bool {
|
||||
matches!(
|
||||
code,
|
||||
KEY_TAB
|
||||
| KEY_RIGHT
|
||||
| KEY_LEFT
|
||||
| KEY_DOWN
|
||||
| KEY_UP
|
||||
| KEY_HOME
|
||||
| KEY_END
|
||||
| KEY_PGUP
|
||||
| KEY_PGDN
|
||||
| KEY_H
|
||||
| KEY_J
|
||||
| KEY_K
|
||||
| KEY_L
|
||||
)
|
||||
}
|
||||
|
||||
/// evdev button code, as wl_pointer reports it.
|
||||
const BTN_LEFT: u32 = 0x110;
|
||||
|
||||
@@ -321,6 +341,16 @@ impl App {
|
||||
if self.is_alt_tab && is_trigger_modifier(code) {
|
||||
self.latched_modifiers.insert(code);
|
||||
}
|
||||
// Arm client-side repeat for navigation keys.
|
||||
if is_repeatable_key(code) {
|
||||
let delay = std::time::Duration::from_millis(self.repeat_delay_ms as u64);
|
||||
self.repeat_key = Some(code);
|
||||
self.repeat_next = Some(std::time::Instant::now() + delay);
|
||||
} else {
|
||||
// Non-repeating key clears any held repeat.
|
||||
self.repeat_key = None;
|
||||
self.repeat_next = None;
|
||||
}
|
||||
match code {
|
||||
KEY_LEFTSHIFT | KEY_RIGHTSHIFT => self.shift = true,
|
||||
KEY_ESC | KEY_Q => self.ending = Ending::Cancelled,
|
||||
@@ -345,6 +375,11 @@ impl App {
|
||||
if code == KEY_LEFTSHIFT || code == KEY_RIGHTSHIFT {
|
||||
self.shift = false;
|
||||
}
|
||||
// Clear repeat if this is the key that was held.
|
||||
if self.repeat_key == Some(code) {
|
||||
self.repeat_key = None;
|
||||
self.repeat_next = None;
|
||||
}
|
||||
if self.is_alt_tab
|
||||
&& self.latched_modifiers.remove(&code)
|
||||
&& self.latched_modifiers.is_empty()
|
||||
@@ -356,6 +391,27 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
/// Called by the main loop when the key-repeat timer fires. Fires the
|
||||
/// currently held navigation action, then arms the next repeat tick.
|
||||
pub fn fire_repeat(&mut self, qh: &QueueHandle<Self>) {
|
||||
let Some(code) = self.repeat_key else { return };
|
||||
let rate = std::time::Duration::from_millis(self.repeat_rate_ms as u64);
|
||||
self.repeat_next = Some(std::time::Instant::now() + rate);
|
||||
// Re-run the navigation action without re-arming the delay.
|
||||
match code {
|
||||
KEY_TAB if self.shift => self.move_sel(-1, qh),
|
||||
KEY_TAB | KEY_RIGHT | KEY_L => self.move_sel(1, qh),
|
||||
KEY_LEFT | KEY_H => self.move_sel(-1, qh),
|
||||
KEY_DOWN | KEY_J => self.move_row(1, qh),
|
||||
KEY_UP | KEY_K => self.move_row(-1, qh),
|
||||
KEY_HOME => self.select(0, qh),
|
||||
KEY_END => self.select(self.tiles.len().saturating_sub(1), qh),
|
||||
KEY_PGUP => self.move_row(-self.layout.visible_rows, qh),
|
||||
KEY_PGDN => self.move_row(self.layout.visible_rows, qh),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn keyboard_enter(&mut self, keys: Vec<u8>, qh: &QueueHandle<Self>) {
|
||||
self.focused = true;
|
||||
let held_keys: Vec<u32> = keys
|
||||
@@ -382,17 +438,31 @@ impl App {
|
||||
|
||||
if self.is_alt_tab && !self.initial_stepped {
|
||||
self.initial_stepped = true;
|
||||
if self.alt_tab == AltTabMode::Yes && self.latched_modifiers.is_empty() {
|
||||
// In explicit alt-tab mode, if no modifier was held on enter,
|
||||
// the modifier was released before focus was acquired: commit immediately!
|
||||
if self.ending == Ending::Running {
|
||||
self.picked = self.tiles.get(self.sel).map(|t| t.target.clone());
|
||||
self.ending = Ending::Picked;
|
||||
if self.alt_tab == AltTabMode::Auto {
|
||||
if self.latched_modifiers.is_empty() {
|
||||
// Auto mode: no modifier held on enter means the mod was
|
||||
// released before focus arrived — commit immediately.
|
||||
if self.ending == Ending::Running {
|
||||
self.picked = self.tiles.get(self.sel).map(|t| t.target.clone());
|
||||
self.ending = Ending::Picked;
|
||||
}
|
||||
} else {
|
||||
// Modifier is held: step selection now.
|
||||
let step = if self.shift { -1 } else { 1 };
|
||||
self.move_sel(step, qh);
|
||||
}
|
||||
} else if self.alt_tab == AltTabMode::Auto {
|
||||
// In auto mode, step selection now that we know a modifier was held:
|
||||
let step = if self.shift { -1 } else { 1 };
|
||||
self.move_sel(step, qh);
|
||||
}
|
||||
}
|
||||
|
||||
// For both Yes and Auto: if we are in alt-tab mode but no modifier is
|
||||
// currently held, the mod was released before keyboard focus arrived.
|
||||
// Commit the current selection immediately.
|
||||
if self.is_alt_tab && self.latched_modifiers.is_empty() && self.ending == Ending::Running {
|
||||
if self.alt_tab == AltTabMode::Yes
|
||||
|| (self.alt_tab == AltTabMode::Auto && self.initial_stepped)
|
||||
{
|
||||
self.picked = self.tiles.get(self.sel).map(|t| t.target.clone());
|
||||
self.ending = Ending::Picked;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,12 +533,30 @@ impl Dispatch<WlKeyboard, ()> for App {
|
||||
WEnum::Value(wl_keyboard::KeyState::Released) => app.key_up(key),
|
||||
_ => {}
|
||||
},
|
||||
// Store compositor key-repeat settings for our client-side timer.
|
||||
wl_keyboard::Event::RepeatInfo { rate, delay } => {
|
||||
// rate == 0 means repeat is disabled.
|
||||
if rate > 0 {
|
||||
app.repeat_rate_ms = (1000 / rate as u32).max(1);
|
||||
app.repeat_delay_ms = delay as u32;
|
||||
} else {
|
||||
app.repeat_key = None;
|
||||
app.repeat_next = None;
|
||||
app.repeat_delay_ms = 0;
|
||||
app.repeat_rate_ms = 0;
|
||||
}
|
||||
}
|
||||
// Focus is only tracked here. sway sends leave immediately
|
||||
// followed by enter on the same surface when the pointer crosses
|
||||
// it, so whether the grab is really gone is decided by the main
|
||||
// loop, once the event batch has been dispatched.
|
||||
wl_keyboard::Event::Enter { keys, .. } => app.keyboard_enter(keys, qh),
|
||||
wl_keyboard::Event::Leave { .. } => app.focused = false,
|
||||
wl_keyboard::Event::Leave { .. } => {
|
||||
app.focused = false;
|
||||
// Clear any held repeat — we no longer have the keyboard.
|
||||
app.repeat_key = None;
|
||||
app.repeat_next = None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user