Fix VT switching and multi-keyboard support

- Use wrapper struct to track keyboard pointer per-listener
- Get keyboard from listener that fired event, not from seat
- Properly clean up listeners on keyboard disconnect
This commit is contained in:
Levi Neely 2026-06-29 11:48:49 +02:00
parent 1d3843f62a
commit 15f19afeb4
2 changed files with 54 additions and 29 deletions

View File

@ -12,9 +12,18 @@
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include "wl_types.h" #include "wl_types.h"
/* Wrapper struct for per-keyboard listeners */
struct wl_keyboard_listener {
struct wlr_keyboard *keyboard;
struct wl_listener key;
struct wl_listener modifiers;
struct wl_listener destroy;
};
/* Forward declarations */ /* Forward declarations */
void handle_keyboard_key(struct wl_listener *listener, void *data); void handle_keyboard_key(struct wl_listener *listener, void *data);
void handle_keyboard_modifiers(struct wl_listener *listener, void *data); void handle_keyboard_modifiers(struct wl_listener *listener, void *data);
static void handle_keyboard_destroy(struct wl_listener *listener, void *data);
void handle_pointer_motion(struct wl_listener *listener, void *data); void handle_pointer_motion(struct wl_listener *listener, void *data);
void handle_pointer_motion_absolute(struct wl_listener *listener, void *data); void handle_pointer_motion_absolute(struct wl_listener *listener, void *data);
void handle_pointer_button(struct wl_listener *listener, void *data); void handle_pointer_button(struct wl_listener *listener, void *data);
@ -61,12 +70,21 @@ real_pointer_update_global(struct wl_real_pointer_listener *pl)
static void static void
handle_keyboard_destroy(struct wl_listener *listener, void *data) handle_keyboard_destroy(struct wl_listener *listener, void *data)
{ {
(void)listener; (void)data; (void)data;
INPUT_LISTENER_REMOVE(wl_state.real_keyboard_key_listener); struct wl_keyboard_listener *kl =
INPUT_LISTENER_REMOVE(wl_state.real_keyboard_modifiers_listener); wl_container_of(listener, kl, destroy);
INPUT_LISTENER_REMOVE(wl_state.real_keyboard_destroy_listener); wl_list_remove(&kl->key.link);
wl_list_remove(&kl->modifiers.link);
wl_list_remove(&kl->destroy.link);
/* Clear global if this was the tracked keyboard */
if (wl_state.keyboard_dev) {
struct wlr_keyboard *kb = wlr_keyboard_from_input_device(wl_state.keyboard_dev);
if (kb == kl->keyboard) {
wl_state.real_keyboard_dev = NULL; wl_state.real_keyboard_dev = NULL;
wl_state.keyboard_dev = NULL; wl_state.keyboard_dev = NULL;
}
}
free(kl);
} }
static void static void
@ -160,19 +178,16 @@ handle_backend_new_input(struct wl_listener *listener, void *data)
} }
if (wl_state.seat) if (wl_state.seat)
wlr_seat_set_keyboard(wl_state.seat, kb); wlr_seat_set_keyboard(wl_state.seat, kb);
/* Wire key events — use wlr_keyboard_group or attach to each device. /* Wire key events with wrapper structs that track the keyboard */
* wlroots seat aggregates all keyboards automatically when we call struct wl_keyboard_listener *kl = calloc(1, sizeof(*kl));
* wlr_seat_set_keyboard, but we still need per-device listeners for if (kl) {
* the key events to reach our handler. Use dynamic allocation. */ kl->keyboard = kb;
struct wl_listener *key_l = calloc(1, sizeof(*key_l)); kl->key.notify = handle_keyboard_key;
struct wl_listener *mod_l = calloc(1, sizeof(*mod_l)); wl_signal_add(&kb->events.key, &kl->key);
if (key_l) { kl->modifiers.notify = handle_keyboard_modifiers;
key_l->notify = handle_keyboard_key; wl_signal_add(&kb->events.modifiers, &kl->modifiers);
wl_signal_add(&kb->events.key, key_l); kl->destroy.notify = handle_keyboard_destroy;
} wl_signal_add(&dev->events.destroy, &kl->destroy);
if (mod_l) {
mod_l->notify = handle_keyboard_modifiers;
wl_signal_add(&kb->events.modifiers, mod_l);
} }
wwarning("wl_backend: keyboard wired dev=%p xkb_state=%s", (void*)dev, wwarning("wl_backend: keyboard wired dev=%p xkb_state=%s", (void*)dev,
kb->xkb_state ? "yes" : "no"); kb->xkb_state ? "yes" : "no");

View File

@ -15,6 +15,14 @@ int wWaylandKeyPress(unsigned int keycode, unsigned int modifiers);
#include "wl_types.h" #include "wl_types.h"
#include <WINGs/WINGsP.h> #include <WINGs/WINGsP.h>
/* Wrapper struct for per-keyboard listeners (defined in wl_input_fwd.c) */
struct wl_keyboard_listener {
struct wlr_keyboard *keyboard;
struct wl_listener key;
struct wl_listener modifiers;
struct wl_listener destroy;
};
/* Forward declarations */ /* Forward declarations */
WMScreen *wl_get_wmscreen(void); WMScreen *wl_get_wmscreen(void);
void W_WL_EnqueueEvent(struct W_Screen *scr, const WMEvent *ev, struct W_View *target); void W_WL_EnqueueEvent(struct W_Screen *scr, const WMEvent *ev, struct W_View *target);
@ -24,19 +32,19 @@ void
handle_keyboard_key(struct wl_listener *listener, void *data) handle_keyboard_key(struct wl_listener *listener, void *data)
{ {
struct wlr_keyboard_key_event *event = data; struct wlr_keyboard_key_event *event = data;
(void)listener; struct wl_keyboard_listener *kl =
wl_container_of(listener, kl, key);
wwarning("handle_keyboard_key: keycode=%u state=%d", event->keycode, event->state); struct wlr_keyboard *kb = kl->keyboard;
if (!wl_state.seat) if (!wl_state.seat)
return; return;
struct wlr_keyboard *kb = wl_state.seat->keyboard_state.keyboard; if (!kb || !kb->xkb_state) {
if (!kb) { wwarning("handle_keyboard_key: no xkb_state for keycode=%u", event->keycode);
wwarning("handle_keyboard_key: no seat keyboard for event keycode=%u dev=%p", event->keycode, (void*)wl_state.keyboard_dev);
return; return;
} }
wlr_seat_set_keyboard(wl_state.seat, kb);
wlr_seat_keyboard_notify_modifiers(wl_state.seat, &kb->modifiers); wlr_seat_keyboard_notify_modifiers(wl_state.seat, &kb->modifiers);
uint32_t keycode = event->keycode + 8; uint32_t keycode = event->keycode + 8;
@ -102,10 +110,12 @@ handle_keyboard_key(struct wl_listener *listener, void *data)
void void
handle_keyboard_modifiers(struct wl_listener *listener, void *data) handle_keyboard_modifiers(struct wl_listener *listener, void *data)
{ {
(void)listener; (void)data; (void)data;
if (!wl_state.seat) return; struct wl_keyboard_listener *kl =
struct wlr_keyboard *kb = wl_state.seat->keyboard_state.keyboard; wl_container_of(listener, kl, modifiers);
if (!kb) return; struct wlr_keyboard *kb = kl->keyboard;
if (!wl_state.seat || !kb) return;
wlr_seat_set_keyboard(wl_state.seat, kb);
wlr_seat_keyboard_notify_modifiers(wl_state.seat, &kb->modifiers); wlr_seat_keyboard_notify_modifiers(wl_state.seat, &kb->modifiers);
/* Cache the real keyboard's depressed modifiers so pointer button /* Cache the real keyboard's depressed modifiers so pointer button
* handlers can read them regardless of which keyboard the seat * handlers can read them regardless of which keyboard the seat