From 3b00aabd937fa1e2d058debcd0e3865bb037dc83 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sun, 27 Oct 2024 11:54:06 +0000 Subject: [PATCH] keyboard: broadcast modifiers ...to all clients rather than just the one with keyboard focus on keyboard enter/create, modifer press/release and wlr_seat_set_keyboard(). This enables: - Clients such as panels to display the current keyboard layout without introducing new wayland protocols or other IPC. - Unfocused xdg-shell clients to understand button press with keyboard modifiers for example Ctrl+click. The keymap is forwarded to all clients in wlr_seat_set_keyboard(). When a keymap contains multiple layouts, the selection is made via modifiers, which previously were only sent to the client with keyboard focus. Tested with: https://github.com/johanmalm/keyboard-layout Ref: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4496 Fixes: #2271 --- src/input/keyboard.c | 60 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 01557e61..f0ceb7da 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -80,6 +80,43 @@ end_cycling(struct server *server) should_cancel_cycling_on_next_key_release = false; } +static struct wlr_seat_client * +seat_client_from_keyboard_resource(struct wl_resource *resource) +{ + return wl_resource_get_user_data(resource); +} + +static void +broadcast_modifiers_to_unfocused_clients(struct wlr_seat *seat, + const struct wlr_keyboard_modifiers *modifiers) +{ + struct wlr_seat_client *client; + wl_list_for_each(client, &seat->clients, link) { + if (client == seat->keyboard_state.focused_client) { + /* + * We've already notified the focused client by calling + * wlr_seat_keyboard_notify_modifiers() + */ + continue; + } + uint32_t serial = wlr_seat_client_next_serial(client); + struct wl_resource *resource; + wl_resource_for_each(resource, &client->keyboards) { + if (!seat_client_from_keyboard_resource(resource)) { + continue; + } + if (!modifiers) { + wl_keyboard_send_modifiers(resource, serial, 0, + 0, 0, 0); + } else { + wl_keyboard_send_modifiers(resource, serial, + modifiers->depressed, modifiers->latched, + modifiers->locked, modifiers->group); + } + } + } +} + static void keyboard_modifiers_notify(struct wl_listener *listener, void *data) { @@ -112,8 +149,31 @@ keyboard_modifiers_notify(struct wl_listener *listener, void *data) } if (!input_method_keyboard_grab_forward_modifiers(keyboard)) { + /* Send modifiers to focused client */ wlr_seat_keyboard_notify_modifiers(seat->seat, &wlr_keyboard->modifiers); + + /* + * Also broadcast them to non-keyboard-focused clients. + * + * The Wayland protocol does not specify that modifiers are + * broadcast, so this is not something clients can rely on in + * other compositors. + * + * Sway used to broadcast modifiers but stopped doing so to + * avoid waking up all clients when the modifiers change. + * + * By testing with foot and Ctrl+scroll to change font size, it + * appears that Mutter does not pass modifiers to unfocused + * clients, whereas KWin and Weston pass modifiers to clients + * with pointer-focus. + * + * This could be made configurable if there are unintended + * consequences. If so, modifiers ought to still be passed to + * clients with pointer-focus (see issue #2271) + */ + broadcast_modifiers_to_unfocused_clients(seat->seat, + &wlr_keyboard->modifiers); } }