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 "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 */
void handle_keyboard_key(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_absolute(struct wl_listener *listener, void *data);
void handle_pointer_button(struct wl_listener *listener, void *data);
@ -61,13 +70,22 @@ real_pointer_update_global(struct wl_real_pointer_listener *pl)
static void
handle_keyboard_destroy(struct wl_listener *listener, void *data)
{
(void)listener; (void)data;
INPUT_LISTENER_REMOVE(wl_state.real_keyboard_key_listener);
INPUT_LISTENER_REMOVE(wl_state.real_keyboard_modifiers_listener);
INPUT_LISTENER_REMOVE(wl_state.real_keyboard_destroy_listener);
(void)data;
struct wl_keyboard_listener *kl =
wl_container_of(listener, kl, destroy);
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.keyboard_dev = NULL;
}
}
free(kl);
}
static void
real_pointer_handle_motion(struct wl_listener *listener, void *data)
@ -160,19 +178,16 @@ handle_backend_new_input(struct wl_listener *listener, void *data)
}
if (wl_state.seat)
wlr_seat_set_keyboard(wl_state.seat, kb);
/* Wire key events — use wlr_keyboard_group or attach to each device.
* wlroots seat aggregates all keyboards automatically when we call
* wlr_seat_set_keyboard, but we still need per-device listeners for
* the key events to reach our handler. Use dynamic allocation. */
struct wl_listener *key_l = calloc(1, sizeof(*key_l));
struct wl_listener *mod_l = calloc(1, sizeof(*mod_l));
if (key_l) {
key_l->notify = handle_keyboard_key;
wl_signal_add(&kb->events.key, key_l);
}
if (mod_l) {
mod_l->notify = handle_keyboard_modifiers;
wl_signal_add(&kb->events.modifiers, mod_l);
/* Wire key events with wrapper structs that track the keyboard */
struct wl_keyboard_listener *kl = calloc(1, sizeof(*kl));
if (kl) {
kl->keyboard = kb;
kl->key.notify = handle_keyboard_key;
wl_signal_add(&kb->events.key, &kl->key);
kl->modifiers.notify = handle_keyboard_modifiers;
wl_signal_add(&kb->events.modifiers, &kl->modifiers);
kl->destroy.notify = handle_keyboard_destroy;
wl_signal_add(&dev->events.destroy, &kl->destroy);
}
wwarning("wl_backend: keyboard wired dev=%p xkb_state=%s", (void*)dev,
kb->xkb_state ? "yes" : "no");

View File

@ -15,6 +15,14 @@ int wWaylandKeyPress(unsigned int keycode, unsigned int modifiers);
#include "wl_types.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 */
WMScreen *wl_get_wmscreen(void);
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)
{
struct wlr_keyboard_key_event *event = data;
(void)listener;
wwarning("handle_keyboard_key: keycode=%u state=%d", event->keycode, event->state);
struct wl_keyboard_listener *kl =
wl_container_of(listener, kl, key);
struct wlr_keyboard *kb = kl->keyboard;
if (!wl_state.seat)
return;
struct wlr_keyboard *kb = wl_state.seat->keyboard_state.keyboard;
if (!kb) {
wwarning("handle_keyboard_key: no seat keyboard for event keycode=%u dev=%p", event->keycode, (void*)wl_state.keyboard_dev);
if (!kb || !kb->xkb_state) {
wwarning("handle_keyboard_key: no xkb_state for keycode=%u", event->keycode);
return;
}
wlr_seat_set_keyboard(wl_state.seat, kb);
wlr_seat_keyboard_notify_modifiers(wl_state.seat, &kb->modifiers);
uint32_t keycode = event->keycode + 8;
@ -102,10 +110,12 @@ handle_keyboard_key(struct wl_listener *listener, void *data)
void
handle_keyboard_modifiers(struct wl_listener *listener, void *data)
{
(void)listener; (void)data;
if (!wl_state.seat) return;
struct wlr_keyboard *kb = wl_state.seat->keyboard_state.keyboard;
if (!kb) return;
(void)data;
struct wl_keyboard_listener *kl =
wl_container_of(listener, kl, modifiers);
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);
/* Cache the real keyboard's depressed modifiers so pointer button
* handlers can read them regardless of which keyboard the seat