Cycle tabs with Alt+Up/Down matched by keysym
Ctrl+PageUp/PageDown and Ctrl+Tab are needed by real apps (browser tabs, terminal scrollback), and a tabbed-wlroots hotkey consumes the combo before the nested client ever sees it. Alt+Up/Down is free on both the GNOME host (no wm/mutter/shell binding) and labwc, so the keys reach the compositor instead of being grabbed or swallowed. Alt+Up/Down is used in some apps but in minor ways. I tried to use Alt+PageUp/PageDown but it fails on some laptops like mine.
This commit is contained in:
parent
a86a21d76e
commit
54c3c99e1d
3 changed files with 90 additions and 30 deletions
|
|
@ -116,6 +116,8 @@ struct tabbed_server {
|
|||
|
||||
struct xkb_context *xkb_ctx;
|
||||
struct xkb_keymap *xkb_keymap;
|
||||
struct xkb_state *xkb_state;
|
||||
xkb_mod_index_t xkb_alt_mod;
|
||||
};
|
||||
|
||||
/* ===================== Host-side (nested) state ===================== */
|
||||
|
|
@ -359,6 +361,7 @@ static void shutdown_cleanup(void) {
|
|||
}
|
||||
free(server.apps);
|
||||
server.apps = NULL;
|
||||
if (server.xkb_state) xkb_state_unref(server.xkb_state);
|
||||
if (server.xkb_keymap) xkb_keymap_unref(server.xkb_keymap);
|
||||
if (server.xkb_ctx) xkb_context_unref(server.xkb_ctx);
|
||||
bridge_cleanup();
|
||||
|
|
@ -1818,6 +1821,23 @@ static const struct zxdg_toplevel_decoration_v1_listener host_decoration_listene
|
|||
|
||||
/* ---------- host keyboard ---------- */
|
||||
|
||||
/* (Re)build the xkb_state used to translate raw evdev keycodes into
|
||||
keysyms for host hotkey matching. The state is derived from whatever
|
||||
keymap is current (host-provided or fallback), so the hotkeys work on
|
||||
any keyboard/layout without hardcoded keycodes. */
|
||||
static void xkb_state_reload(void)
|
||||
{
|
||||
if (server.xkb_state) {
|
||||
xkb_state_unref(server.xkb_state);
|
||||
server.xkb_state = NULL;
|
||||
}
|
||||
if (!server.xkb_keymap)
|
||||
return;
|
||||
server.xkb_state = xkb_state_new(server.xkb_keymap);
|
||||
server.xkb_alt_mod =
|
||||
xkb_keymap_mod_get_index(server.xkb_keymap, XKB_MOD_NAME_ALT);
|
||||
}
|
||||
|
||||
static void host_kbd_keymap(void *data, struct wl_keyboard *kbd, uint32_t format,
|
||||
int32_t fd, uint32_t size)
|
||||
{
|
||||
|
|
@ -1865,6 +1885,7 @@ static void host_kbd_keymap(void *data, struct wl_keyboard *kbd, uint32_t format
|
|||
if (server.xkb_keymap)
|
||||
xkb_keymap_unref(server.xkb_keymap);
|
||||
server.xkb_keymap = km;
|
||||
xkb_state_reload();
|
||||
tabbed_log(TABBED_LOG_DEBUG, "tabbed-wlroots: host keymap parsed OK\n");
|
||||
} else {
|
||||
tabbed_log(TABBED_LOG_INFO, "tabbed-wlroots: failed to parse host "
|
||||
|
|
@ -2030,11 +2051,29 @@ static void host_kbd_key(void *data, struct wl_keyboard *kbd, uint32_t serial,
|
|||
return;
|
||||
(void)kbd;
|
||||
bridge.kbd_serial = serial;
|
||||
if (server.xkb_state)
|
||||
xkb_state_update_key(server.xkb_state, key + 8,
|
||||
state == WL_KEYBOARD_KEY_STATE_PRESSED
|
||||
? XKB_KEY_DOWN : XKB_KEY_UP);
|
||||
xkb_keysym_t sym = server.xkb_state
|
||||
? xkb_state_key_get_one_sym(server.xkb_state, key + 8)
|
||||
: XKB_KEY_NoSymbol;
|
||||
/* With no compiled keymap (xkb init failed) fall back to the raw
|
||||
evdev keycodes. */
|
||||
bool keysym_fallback = !server.xkb_state;
|
||||
bool alt = server.xkb_state
|
||||
? xkb_state_mod_index_is_active(server.xkb_state,
|
||||
server.xkb_alt_mod, XKB_STATE_MODS_EFFECTIVE)
|
||||
: (host.host_mods_depressed & (1u << 3)) != 0;
|
||||
char symbuf[32];
|
||||
xkb_keysym_get_name(sym, symbuf, sizeof(symbuf));
|
||||
tabbed_log(TABBED_LOG_DEBUG,
|
||||
"[kbd] key=%u state=%u ctrl=%d repeat_key=%u\n", key, state,
|
||||
(host.host_mods_depressed & (1u << 2)) != 0, host.repeat_key);
|
||||
"[kbd] key=%u state=%u sym=%s ctrl=%d alt=%d repeat_key=%u\n",
|
||||
key, state, sym ? symbuf : "-",
|
||||
(host.host_mods_depressed & (1u << 2)) != 0, alt, host.repeat_key);
|
||||
/* F11 toggles the host window fullscreen. */
|
||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED && key == 87) {
|
||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED &&
|
||||
(sym == XKB_KEY_F11 || (keysym_fallback && key == 87))) {
|
||||
host.fullscreen = !host.fullscreen;
|
||||
tabbed_log(TABBED_LOG_INFO, "host fullscreen=%d\n", host.fullscreen);
|
||||
if (host.fullscreen)
|
||||
|
|
@ -2045,26 +2084,36 @@ static void host_kbd_key(void *data, struct wl_keyboard *kbd, uint32_t serial,
|
|||
}
|
||||
/* Temporary: F9/F10 frame dumps for the grey-band diagnosis. */
|
||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
||||
if (key == 67) /* F9 */ {
|
||||
if (sym == XKB_KEY_F9 || (keysym_fallback && key == 67)) {
|
||||
dump_scene_req = true;
|
||||
needs_redraw = true;
|
||||
return;
|
||||
}
|
||||
if (key == 68) /* F10 */ {
|
||||
if (sym == XKB_KEY_F10 || (keysym_fallback && key == 68)) {
|
||||
dump_host_req = true;
|
||||
needs_redraw = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Ctrl+PageUp / Ctrl+PageDown cycle tabs. The host sends a single
|
||||
physical press, so repeats are synthesized by kbd_repeat_start();
|
||||
subsequent host presses of an already-held key are ignored so a
|
||||
host-sent repeat never cycles twice. */
|
||||
bool ctrl = (host.host_mods_depressed & (1u << 2)) != 0;
|
||||
if (ctrl && (key == 104 || key == 109)) {
|
||||
/* Alt+Up / Alt+Down cycle tabs. The host sends a single physical
|
||||
press, so repeats are synthesized by kbd_repeat_start(); subsequent
|
||||
host presses of an already-held key are ignored so a host-sent
|
||||
repeat never cycles twice. Plain arrow keys (not Fn+arrow) are used
|
||||
because many laptop firmwares swallow Alt+Fn+PageUp (the Fn key
|
||||
reports as XF86WakeUp there), which makes Alt+PageUp/PageDown
|
||||
unreliable. */
|
||||
bool prev, next;
|
||||
if (server.xkb_state) {
|
||||
prev = sym == XKB_KEY_Up;
|
||||
next = sym == XKB_KEY_Down;
|
||||
} else {
|
||||
prev = key == 103;
|
||||
next = key == 108;
|
||||
}
|
||||
if (alt && (prev || next)) {
|
||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
||||
if (host.repeat_key != key) {
|
||||
int dir = key == 109 ? 1 : -1;
|
||||
int dir = next ? 1 : -1;
|
||||
cycle_tab(dir);
|
||||
kbd_repeat_start(key, dir);
|
||||
}
|
||||
|
|
@ -2095,7 +2144,14 @@ static void host_kbd_modifiers(void *data, struct wl_keyboard *kbd, uint32_t ser
|
|||
return;
|
||||
(void)kbd;
|
||||
host.host_mods_depressed = mods_depressed;
|
||||
if (host.repeat_key && !(mods_depressed & (1u << 2)))
|
||||
if (server.xkb_state)
|
||||
xkb_state_update_mask(server.xkb_state, mods_depressed,
|
||||
mods_latched, mods_locked, group, group, group);
|
||||
bool alt = server.xkb_state
|
||||
? xkb_state_mod_index_is_active(server.xkb_state,
|
||||
server.xkb_alt_mod, XKB_STATE_MODS_EFFECTIVE)
|
||||
: (mods_depressed & (1u << 3)) != 0;
|
||||
if (host.repeat_key && !alt)
|
||||
kbd_repeat_cancel();
|
||||
wlr_keyboard_notify_modifiers(&server.keyboard, mods_depressed,
|
||||
mods_latched, mods_locked, group);
|
||||
|
|
@ -2887,8 +2943,10 @@ int main(int argc, char *argv[]) {
|
|||
};
|
||||
server.xkb_keymap = xkb_keymap_new_from_names(
|
||||
server.xkb_ctx, &names, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
if (server.xkb_keymap)
|
||||
if (server.xkb_keymap) {
|
||||
wlr_keyboard_set_keymap(&server.keyboard, server.xkb_keymap);
|
||||
xkb_state_reload();
|
||||
}
|
||||
}
|
||||
wlr_seat_set_keyboard(server.seat, &server.keyboard);
|
||||
|
||||
|
|
@ -2976,7 +3034,7 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
|
||||
tabbed_log(TABBED_LOG_INFO, "tabbed-wlroots: running. "
|
||||
"Ctrl+PageUp/PageDown switches tabs.\n");
|
||||
"Alt+Up/Down switches tabs.\n");
|
||||
|
||||
/* Main event loop: service both the server and the host displays */
|
||||
int server_fd = wl_event_loop_get_fd(server.loop);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue