alt-tab
This commit is contained in:
+85
-8
@@ -24,6 +24,7 @@ use wayland_protocols_wlr::layer_shell::v1::client::{
|
||||
use wayland_protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::Shape;
|
||||
|
||||
use crate::app::{App, Ending};
|
||||
use crate::config::AltTabMode;
|
||||
use crate::shm;
|
||||
use crate::theme::{Rect, fit_centred};
|
||||
|
||||
@@ -32,23 +33,41 @@ use crate::theme::{Rect, fit_centred};
|
||||
const KEY_ESC: u32 = 1;
|
||||
const KEY_TAB: u32 = 15;
|
||||
const KEY_Q: u32 = 16;
|
||||
const KEY_ENTER: u32 = 28;
|
||||
const KEY_LEFTCTRL: u32 = 29;
|
||||
// hjkl, by physical position: the same keys as vim on a qwerty layout.
|
||||
const KEY_H: u32 = 35;
|
||||
const KEY_J: u32 = 36;
|
||||
const KEY_K: u32 = 37;
|
||||
const KEY_L: u32 = 38;
|
||||
const KEY_ENTER: u32 = 28;
|
||||
const KEY_LEFTSHIFT: u32 = 42;
|
||||
const KEY_RIGHTSHIFT: u32 = 54;
|
||||
const KEY_LEFTALT: u32 = 56;
|
||||
const KEY_KPENTER: u32 = 96;
|
||||
const KEY_RIGHTCTRL: u32 = 97;
|
||||
const KEY_RIGHTALT: u32 = 100;
|
||||
const KEY_HOME: u32 = 102;
|
||||
const KEY_UP: u32 = 103;
|
||||
const KEY_PGUP: u32 = 104;
|
||||
const KEY_LEFT: u32 = 105;
|
||||
const KEY_RIGHT: u32 = 106;
|
||||
const KEY_END: u32 = 107;
|
||||
const KEY_DOWN: u32 = 108;
|
||||
const KEY_PGUP: u32 = 104;
|
||||
const KEY_PGDN: u32 = 109;
|
||||
const KEY_LEFTMETA: u32 = 125;
|
||||
const KEY_RIGHTMETA: u32 = 126;
|
||||
|
||||
fn is_trigger_modifier(code: u32) -> bool {
|
||||
matches!(
|
||||
code,
|
||||
KEY_LEFTALT
|
||||
| KEY_RIGHTALT
|
||||
| KEY_LEFTMETA
|
||||
| KEY_RIGHTMETA
|
||||
| KEY_LEFTCTRL
|
||||
| KEY_RIGHTCTRL
|
||||
)
|
||||
}
|
||||
|
||||
/// evdev button code, as wl_pointer reports it.
|
||||
const BTN_LEFT: u32 = 0x110;
|
||||
@@ -105,6 +124,9 @@ impl App {
|
||||
}
|
||||
pool.destroy();
|
||||
self.chrome = Some(shm::Chrome::new(&file, pw, ph)?);
|
||||
if let (Some(mgr), Some(seat)) = (&self.inhibit_mgr, &self.seat) {
|
||||
self.inhibitor = Some(mgr.inhibit_shortcuts(&surface, seat, qh, ()));
|
||||
}
|
||||
self.surface = Some(surface);
|
||||
Ok(())
|
||||
}
|
||||
@@ -296,6 +318,9 @@ impl App {
|
||||
}
|
||||
|
||||
fn key(&mut self, code: u32, qh: &QueueHandle<Self>) {
|
||||
if self.is_alt_tab && is_trigger_modifier(code) {
|
||||
self.latched_modifiers.insert(code);
|
||||
}
|
||||
match code {
|
||||
KEY_LEFTSHIFT | KEY_RIGHTSHIFT => self.shift = true,
|
||||
KEY_ESC | KEY_Q => self.ending = Ending::Cancelled,
|
||||
@@ -315,6 +340,62 @@ impl App {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn key_up(&mut self, code: u32) {
|
||||
if code == KEY_LEFTSHIFT || code == KEY_RIGHTSHIFT {
|
||||
self.shift = false;
|
||||
}
|
||||
if self.is_alt_tab
|
||||
&& self.latched_modifiers.remove(&code)
|
||||
&& self.latched_modifiers.is_empty()
|
||||
{
|
||||
if self.ending == Ending::Running {
|
||||
self.picked = self.tiles.get(self.sel).map(|t| t.target.clone());
|
||||
self.ending = Ending::Picked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn keyboard_enter(&mut self, keys: Vec<u8>, qh: &QueueHandle<Self>) {
|
||||
self.focused = true;
|
||||
let held_keys: Vec<u32> = keys
|
||||
.chunks_exact(4)
|
||||
.map(|chunk| u32::from_ne_bytes(chunk.try_into().unwrap()))
|
||||
.collect();
|
||||
|
||||
if held_keys.iter().any(|&k| k == KEY_LEFTSHIFT || k == KEY_RIGHTSHIFT) {
|
||||
self.shift = true;
|
||||
}
|
||||
|
||||
let held_modifiers: Vec<u32> = held_keys
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|&k| is_trigger_modifier(k))
|
||||
.collect();
|
||||
|
||||
if !held_modifiers.is_empty() && self.alt_tab != AltTabMode::No {
|
||||
self.is_alt_tab = true;
|
||||
for &m in &held_modifiers {
|
||||
self.latched_modifiers.insert(m);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- event plumbing -------------------------------------------------------
|
||||
@@ -379,18 +460,14 @@ impl Dispatch<WlKeyboard, ()> for App {
|
||||
match event {
|
||||
wl_keyboard::Event::Key { key, state, .. } => match state {
|
||||
WEnum::Value(wl_keyboard::KeyState::Pressed) => app.key(key, qh),
|
||||
WEnum::Value(wl_keyboard::KeyState::Released)
|
||||
if key == KEY_LEFTSHIFT || key == KEY_RIGHTSHIFT =>
|
||||
{
|
||||
app.shift = false
|
||||
}
|
||||
WEnum::Value(wl_keyboard::KeyState::Released) => app.key_up(key),
|
||||
_ => {}
|
||||
},
|
||||
// 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 { .. } => app.focused = true,
|
||||
wl_keyboard::Event::Enter { keys, .. } => app.keyboard_enter(keys, qh),
|
||||
wl_keyboard::Event::Leave { .. } => app.focused = false,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user