From 98b3d911eb56f54794889afca9ec7aeb7d5862cf Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 4 May 2026 14:27:06 +0900 Subject: [PATCH 1/3] Remove key state debugging by LABWC_DEBUG_KEY_STATE env var I think this is now less demanded due to the introduction of DebugToggleKeyStateIndicator action. --- docs/labwc.1.scd | 4 ---- src/input/key-state.c | 25 ------------------------- 2 files changed, 29 deletions(-) diff --git a/docs/labwc.1.scd b/docs/labwc.1.scd index 31f28c19..60f9b495 100644 --- a/docs/labwc.1.scd +++ b/docs/labwc.1.scd @@ -158,10 +158,6 @@ example: *LABWC_DEBUG_FOO=1 labwc*. for *Cascade*) for config and menu files respectively. -*LABWC_DEBUG_KEY_STATE* - Enable logging of press and release events for bound keys (generally - key-combinations like *Ctrl-Alt-t*). - # SEE ALSO labwc-actions(5), labwc-config(5), labwc-menu(5), labwc-theme(5) diff --git a/src/input/key-state.c b/src/input/key-state.c index 6d089891..fde90634 100644 --- a/src/input/key-state.c +++ b/src/input/key-state.c @@ -169,40 +169,15 @@ key_state_indicator_toggle(void) show_debug_indicator = !show_debug_indicator; } -static void -report(struct lab_set *key_set, const char *msg) -{ - static char *should_print; - static bool has_run; - - if (!has_run) { - should_print = getenv("LABWC_DEBUG_KEY_STATE"); - has_run = true; - } - if (!should_print) { - return; - } - printf("%s", msg); - for (int i = 0; i < key_set->size; ++i) { - printf("%d,", key_set->values[i]); - } - printf("\n"); -} - uint32_t * key_state_pressed_sent_keycodes(void) { - report(&pressed, "before - pressed:"); - report(&bound, "before - bound:"); - /* pressed_sent = pressed - bound */ pressed_sent = pressed; for (int i = 0; i < bound.size; ++i) { lab_set_remove(&pressed_sent, bound.values[i]); } - report(&pressed_sent, "after - pressed_sent:"); - return pressed_sent.values; } From 885db240f35bacaf4d515a4a6f34dfe41bcb0a7b Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 4 May 2026 01:12:58 +0900 Subject: [PATCH 2/3] keyboard: simplify key state handling We used to store both `pressed` keys and `bound` keys and derive `pressed_sent` keys from them, which are notified to the client when switching focus. But I think this was overengineered and we can remove `pressed` keys and `bound` keys. Instead we now directly modify `pressed_sent` keys just before calling `wlr_seat_keyboard_notify_key()`. Technically we could even remove `pressed_sent` keys as they are duplicated in wlr_seat->keyboard_state.keyboard->keycodes, but we keep `pressed_sent` keys as it is easier to access. The only place which required `bound` keys was `handle_modifiers()`. This function checked `bound` keys to keep widnow switcher alive when Alt-Tab is pressed and only the Alt modifier is released. The window switcher is then finished when Tab key is released. I replaced the check with `(seat->keyboard_group->keyboard.num_keycodes > 0)`. This slightly changes the behavior; when Alt-Tab and any other key e.g. X are pressed and Tab key and Alt modifier are released, the window switcher is now kept alive. But I don't think this breaks any workflows for users. --- include/input/key-state.h | 17 -------- include/labwc.h | 3 ++ src/input/key-state.c | 83 ++------------------------------------- src/input/keyboard.c | 44 ++++++++++----------- src/seat.c | 11 ++---- 5 files changed, 33 insertions(+), 125 deletions(-) diff --git a/include/input/key-state.h b/include/input/key-state.h index 81374b6c..8fe978e1 100644 --- a/include/input/key-state.h +++ b/include/input/key-state.h @@ -2,9 +2,6 @@ #ifndef LABWC_KEY_STATE_H #define LABWC_KEY_STATE_H -#include -#include - struct seat; /* @@ -16,18 +13,4 @@ struct seat; void key_state_indicator_update(struct seat *seat); void key_state_indicator_toggle(void); -/** - * key_state_pressed_sent_keycodes - generate array of pressed+sent keys - * Note: The array is generated by subtracting any bound keys from _all_ pressed - * keys (because bound keys were not forwarded to clients). - */ -uint32_t *key_state_pressed_sent_keycodes(void); -int key_state_nr_pressed_sent_keycodes(void); - -void key_state_set_pressed(uint32_t keycode, bool is_pressed); -void key_state_store_pressed_key_as_bound(uint32_t keycode); -bool key_state_corresponding_press_event_was_bound(uint32_t keycode); -void key_state_bound_key_remove(uint32_t keycode); -int key_state_nr_bound_keys(void); - #endif /* LABWC_KEY_STATE_H */ diff --git a/include/labwc.h b/include/labwc.h index dc4c1311..9ca19fee 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -83,6 +83,9 @@ struct seat { struct lab_set bound_buttons; + /* Keys that have been pressed and not consumed by compositor actions */ + struct lab_set pressed_sent_keys; + struct { bool active; struct { diff --git a/src/input/key-state.c b/src/input/key-state.c index fde90634..4028bd31 100644 --- a/src/input/key-state.c +++ b/src/input/key-state.c @@ -2,8 +2,6 @@ #include "input/key-state.h" #include #include -#include -#include #include #include #include @@ -14,8 +12,6 @@ #include "labwc.h" #include "scaled-buffer/scaled-font-buffer.h" -static struct lab_set pressed, bound, pressed_sent; - static bool show_debug_indicator; static struct indicator_state { struct wlr_scene_tree *tree; @@ -109,31 +105,12 @@ update_key_indicator_callback(void *data) struct buf buf = BUF_INIT; - buf_add(&buf, "pressed="); - for (int i = 0; i < pressed.size; i++) { - const char *keyname = keycode_to_keyname(indicator_state.keymap, - pressed.values[i]); - buf_add_fmt(&buf, "%s (%d), ", keyname, pressed.values[i]); - } - scaled_font_buffer_update(indicator_state.sfb_pressed, buf.data, - -1, &rc.font_osd, black, white); - - buf_clear(&buf); - buf_add(&buf, "bound="); - for (int i = 0; i < bound.size; i++) { - const char *keyname = keycode_to_keyname(indicator_state.keymap, - bound.values[i]); - buf_add_fmt(&buf, "%s (%d), ", keyname, bound.values[i]); - } - scaled_font_buffer_update(indicator_state.sfb_bound, buf.data, - -1, &rc.font_osd, black, white); - - buf_clear(&buf); + struct lab_set *pressed_sent = &server.seat.pressed_sent_keys; buf_add(&buf, "pressed_sent="); - for (int i = 0; i < pressed_sent.size; i++) { + for (int i = 0; i < pressed_sent->size; i++) { const char *keyname = keycode_to_keyname(indicator_state.keymap, - pressed_sent.values[i]); - buf_add_fmt(&buf, "%s (%d), ", keyname, pressed_sent.values[i]); + pressed_sent->values[i]); + buf_add_fmt(&buf, "%s (%d), ", keyname, pressed_sent->values[i]); } scaled_font_buffer_update(indicator_state.sfb_pressed_sent, buf.data, -1, &rc.font_osd, black, white); @@ -168,55 +145,3 @@ key_state_indicator_toggle(void) { show_debug_indicator = !show_debug_indicator; } - -uint32_t * -key_state_pressed_sent_keycodes(void) -{ - /* pressed_sent = pressed - bound */ - pressed_sent = pressed; - for (int i = 0; i < bound.size; ++i) { - lab_set_remove(&pressed_sent, bound.values[i]); - } - - return pressed_sent.values; -} - -int -key_state_nr_pressed_sent_keycodes(void) -{ - return pressed_sent.size; -} - -void -key_state_set_pressed(uint32_t keycode, bool is_pressed) -{ - if (is_pressed) { - lab_set_add(&pressed, keycode); - } else { - lab_set_remove(&pressed, keycode); - } -} - -void -key_state_store_pressed_key_as_bound(uint32_t keycode) -{ - lab_set_add(&bound, keycode); -} - -bool -key_state_corresponding_press_event_was_bound(uint32_t keycode) -{ - return lab_set_contains(&bound, keycode); -} - -void -key_state_bound_key_remove(uint32_t keycode) -{ - lab_set_remove(&bound, keycode); -} - -int -key_state_nr_bound_keys(void) -{ - return bound.size; -} diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 98be5d11..1d6073f1 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -153,7 +153,7 @@ handle_modifiers(struct wl_listener *listener, void *data) if ((cycling || seat->workspace_osd_shown_by_modifier) && !keyboard_get_all_modifiers(seat)) { if (cycling) { - if (key_state_nr_bound_keys()) { + if (seat->keyboard_group->keyboard.num_keycodes > 0) { should_cancel_cycling_on_next_key_release = true; } else { should_cancel_cycling_on_next_key_release = false; @@ -381,7 +381,7 @@ handle_key_release(uint32_t evdev_keycode) * Release events for keys that were not bound should always be * forwarded to clients to avoid stuck keys. */ - if (!key_state_corresponding_press_event_was_bound(evdev_keycode)) { + if (lab_set_contains(&server.seat.pressed_sent_keys, evdev_keycode)) { return LAB_KEY_HANDLED_FALSE; } @@ -403,7 +403,6 @@ handle_key_release(uint32_t evdev_keycode) * If a press event was handled by a compositor binding, then do * not forward the corresponding release event to clients. */ - key_state_bound_key_remove(evdev_keycode); return LAB_KEY_HANDLED_TRUE; } @@ -496,12 +495,8 @@ handle_compositor_keybindings(struct keyboard *keyboard, struct keyinfo keyinfo = get_keyinfo(wlr_keyboard, event->keycode); bool locked = server.session_lock_manager->locked; - key_state_set_pressed(event->keycode, - event->state == WL_KEYBOARD_KEY_STATE_PRESSED); - if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { if (cur_keybind && cur_keybind->on_release) { - key_state_bound_key_remove(event->keycode); if (locked && !cur_keybind->allow_when_locked) { cur_keybind = NULL; return LAB_KEY_HANDLED_TRUE; @@ -515,7 +510,6 @@ handle_compositor_keybindings(struct keyboard *keyboard, /* Catch C-A-F1 to C-A-F12 to change tty */ if (handle_change_vt_key(keyboard, &keyinfo.translated)) { - key_state_store_pressed_key_as_bound(event->keycode); return LAB_KEY_HANDLED_TRUE_AND_VT_CHANGED; } @@ -526,12 +520,10 @@ handle_compositor_keybindings(struct keyboard *keyboard, */ if (!locked) { if (server.input_mode == LAB_INPUT_STATE_MENU) { - key_state_store_pressed_key_as_bound(event->keycode); handle_menu_keys(&keyinfo.translated); return LAB_KEY_HANDLED_TRUE; } else if (server.input_mode == LAB_INPUT_STATE_CYCLE) { if (handle_cycle_view_key(&keyinfo)) { - key_state_store_pressed_key_as_bound(event->keycode); return LAB_KEY_HANDLED_TRUE; } } @@ -542,12 +534,6 @@ handle_compositor_keybindings(struct keyboard *keyboard, */ cur_keybind = match_keybinding(&keyinfo, keyboard->is_virtual); if (cur_keybind && (!locked || cur_keybind->allow_when_locked)) { - /* - * Update key-state before action_run() because the action - * might lead to seat_focus() in which case we pass the - * 'pressed-sent' keys to the new surface. - */ - key_state_store_pressed_key_as_bound(event->keycode); if (!cur_keybind->on_release) { actions_run(NULL, &cur_keybind->actions, NULL); } @@ -637,11 +623,10 @@ handle_key(struct wl_listener *listener, void *data) enum lab_key_handled handled = handle_compositor_keybindings(keyboard, event); - if (handled == LAB_KEY_HANDLED_TRUE_AND_VT_CHANGED) { - return; - } - - if (handled) { + switch (handled) { + case LAB_KEY_HANDLED_TRUE_AND_VT_CHANGED: + break; + case LAB_KEY_HANDLED_TRUE: /* * We do not start the repeat-timer on pressed modifiers (like * Super_L) because it is only for our own internal use with @@ -651,10 +636,25 @@ handle_key(struct wl_listener *listener, void *data) && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { start_keybind_repeat(keyboard, event); } - } else if (!input_method_keyboard_grab_forward_key(keyboard, event)) { + break; + case LAB_KEY_HANDLED_FALSE: + /* + * Note: Technically we could just use + * wlr_seat->keyboard_state.keyboard->keycodes instead, but we + * have seat->pressed_sent_keys for convenience. + */ + if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { + lab_set_add(&seat->pressed_sent_keys, event->keycode); + } else { + lab_set_remove(&seat->pressed_sent_keys, event->keycode); + } + if (input_method_keyboard_grab_forward_key(keyboard, event)) { + break; + } wlr_seat_set_keyboard(wlr_seat, keyboard->wlr_keyboard); wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, event->keycode, event->state); + break; } } diff --git a/src/seat.c b/src/seat.c index fe85dc26..ec0e96ec 100644 --- a/src/seat.c +++ b/src/seat.c @@ -782,12 +782,11 @@ seat_force_focus_surface(struct seat *seat, struct wlr_surface *surface) if (server.session_lock_manager->locked) { return; } - uint32_t *pressed_sent_keycodes = key_state_pressed_sent_keycodes(); - int nr_pressed_sent_keycodes = key_state_nr_pressed_sent_keycodes(); struct wlr_keyboard *kb = &seat->keyboard_group->keyboard; wlr_seat_keyboard_enter(seat->wlr_seat, surface, - pressed_sent_keycodes, nr_pressed_sent_keycodes, &kb->modifiers); + seat->pressed_sent_keys.values, seat->pressed_sent_keys.size, + &kb->modifiers); } static void @@ -832,12 +831,10 @@ seat_focus(struct seat *seat, struct wlr_surface *surface, * those that were actually _sent_ to clients (that is, those that were * not bound). */ - uint32_t *pressed_sent_keycodes = key_state_pressed_sent_keycodes(); - int nr_pressed_sent_keycodes = key_state_nr_pressed_sent_keycodes(); - struct wlr_keyboard *kb = &seat->keyboard_group->keyboard; wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface, - pressed_sent_keycodes, nr_pressed_sent_keycodes, &kb->modifiers); + seat->pressed_sent_keys.values, seat->pressed_sent_keys.size, + &kb->modifiers); input_method_relay_set_focus(seat->input_method_relay, surface); From 78f2b4656c2c7b10c4574f0cdc0843f596f759a4 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 4 May 2026 02:19:07 +0900 Subject: [PATCH 3/3] Rename key-state.c to key-state-indicator.c --- include/input/{key-state.h => key-state-indicator.h} | 6 +++--- src/action.c | 2 +- src/input/{key-state.c => key-state-indicator.c} | 2 +- src/input/keyboard.c | 2 +- src/input/meson.build | 2 +- src/seat.c | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) rename include/input/{key-state.h => key-state-indicator.h} (77%) rename src/input/{key-state.c => key-state-indicator.c} (99%) diff --git a/include/input/key-state.h b/include/input/key-state-indicator.h similarity index 77% rename from include/input/key-state.h rename to include/input/key-state-indicator.h index 8fe978e1..5ebe5891 100644 --- a/include/input/key-state.h +++ b/include/input/key-state-indicator.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_KEY_STATE_H -#define LABWC_KEY_STATE_H +#ifndef LABWC_KEY_STATE_INDICATOR_H +#define LABWC_KEY_STATE_INDICATOR_H struct seat; @@ -13,4 +13,4 @@ struct seat; void key_state_indicator_update(struct seat *seat); void key_state_indicator_toggle(void); -#endif /* LABWC_KEY_STATE_H */ +#endif /* LABWC_KEY_STATE_INDICATOR_H */ diff --git a/src/action.c b/src/action.c index daf8fb30..2403d1a2 100644 --- a/src/action.c +++ b/src/action.c @@ -21,7 +21,7 @@ #include "cycle.h" #include "debug.h" #include "input/keyboard.h" -#include "input/key-state.h" +#include "input/key-state-indicator.h" #include "labwc.h" #include "magnifier.h" #include "menu/menu.h" diff --git a/src/input/key-state.c b/src/input/key-state-indicator.c similarity index 99% rename from src/input/key-state.c rename to src/input/key-state-indicator.c index 4028bd31..bc9a00dd 100644 --- a/src/input/key-state.c +++ b/src/input/key-state-indicator.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "input/key-state.h" +#include "input/key-state-indicator.h" #include #include #include diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 1d6073f1..a095208f 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -14,7 +14,7 @@ #include "cycle.h" #include "idle.h" #include "input/ime.h" -#include "input/key-state.h" +#include "input/key-state-indicator.h" #include "labwc.h" #include "menu/menu.h" #include "session-lock.h" diff --git a/src/input/meson.build b/src/input/meson.build index 4d56f00d..9ba19f52 100644 --- a/src/input/meson.build +++ b/src/input/meson.build @@ -5,7 +5,7 @@ labwc_sources += files( 'gestures.c', 'input.c', 'keyboard.c', - 'key-state.c', + 'key-state-indicator.c', 'touch.c', 'ime.c', ) diff --git a/src/seat.c b/src/seat.c index ec0e96ec..9c710888 100644 --- a/src/seat.c +++ b/src/seat.c @@ -25,7 +25,7 @@ #include "input/tablet-pad.h" #include "input/input.h" #include "input/keyboard.h" -#include "input/key-state.h" +#include "input/key-state-indicator.h" #include "labwc.h" #include "output.h" #include "session-lock.h"