seat/keyboard: support sending modifiers when not focused

This adds wlr_seat_client_notify_modifiers which sends the current
keyboard modifiers to an additional client, tracking what the client
last saw to avoid unneeded duplicates. This is useful to make actions
like "ctrl+scroll" work correctly when pointer and keyboard focus are on
distinct clients.

The tracked modifier state is also used to avoid sending unneeded
modifiers events in default_keyboard_modifiers.  No de-duplication is
performed in wlr_seat_keyboard_send_modifiers because that function is
used in cases where the protocol unconditionally requires a modifiers
event to be sent (such as after wl_keyboard_enter).
This commit is contained in:
Daniel De Graaf 2024-01-23 19:30:33 -05:00
parent 3ad4374a54
commit 8ad0c0166b
2 changed files with 65 additions and 12 deletions

View file

@ -55,6 +55,13 @@ struct wlr_seat_client {
struct wlr_serial_ringset serials; struct wlr_serial_ringset serials;
bool needs_touch_frame; bool needs_touch_frame;
// The client's current knowledge of the keyboard modifier state. This
// will lag behind the actual state if the client does not have
// keyboard focus; it is used by wlr_seat_client_notify_modifiers to
// allow actions like ctrl+scroll to work without sending duplicate
// events if modifiers are unchanged.
struct wlr_keyboard_modifiers tracked_modifiers;
// When the client doesn't support high-resolution scroll, accumulate deltas // When the client doesn't support high-resolution scroll, accumulate deltas
// until we can notify a discrete event. // until we can notify a discrete event.
// Some mice have a free spinning wheel, making possible to lock the wheel // Some mice have a free spinning wheel, making possible to lock the wheel
@ -526,6 +533,14 @@ void wlr_seat_keyboard_send_key(struct wlr_seat *seat, uint32_t time_msec,
void wlr_seat_keyboard_send_modifiers(struct wlr_seat *seat, void wlr_seat_keyboard_send_modifiers(struct wlr_seat *seat,
const struct wlr_keyboard_modifiers *modifiers); const struct wlr_keyboard_modifiers *modifiers);
/**
* Update an additional client's view of the keyboard modifier state. This
* does nothing if the target has keyboard focus or if its view of the modifier
* state still matches the current modifier state.
*/
void wlr_seat_client_notify_modifiers(struct wlr_seat *seat,
struct wlr_seat_client *target);
/** /**
* Send a keyboard enter event to the given surface and consider it to be the * Send a keyboard enter event to the given surface and consider it to be the
* focused surface for the keyboard. This will send a leave event to the last * focused surface for the keyboard. This will send a leave event to the last

View file

@ -10,6 +10,11 @@
#include "types/wlr_data_device.h" #include "types/wlr_data_device.h"
#include "types/wlr_seat.h" #include "types/wlr_seat.h"
static void seat_keyboard_send_modifiers(struct wlr_seat *seat,
const struct wlr_keyboard_modifiers *modifiers,
struct wlr_seat_client *client,
bool force);
static void default_keyboard_enter(struct wlr_seat_keyboard_grab *grab, static void default_keyboard_enter(struct wlr_seat_keyboard_grab *grab,
struct wlr_surface *surface, const uint32_t keycodes[], size_t num_keycodes, struct wlr_surface *surface, const uint32_t keycodes[], size_t num_keycodes,
const struct wlr_keyboard_modifiers *modifiers) { const struct wlr_keyboard_modifiers *modifiers) {
@ -27,7 +32,7 @@ static void default_keyboard_key(struct wlr_seat_keyboard_grab *grab,
static void default_keyboard_modifiers(struct wlr_seat_keyboard_grab *grab, static void default_keyboard_modifiers(struct wlr_seat_keyboard_grab *grab,
const struct wlr_keyboard_modifiers *modifiers) { const struct wlr_keyboard_modifiers *modifiers) {
wlr_seat_keyboard_send_modifiers(grab->seat, modifiers); seat_keyboard_send_modifiers(grab->seat, modifiers, NULL, false);
} }
static void default_keyboard_cancel(struct wlr_seat_keyboard_grab *grab) { static void default_keyboard_cancel(struct wlr_seat_keyboard_grab *grab) {
@ -195,34 +200,67 @@ static void seat_keyboard_handle_surface_destroy(struct wl_listener *listener,
wlr_seat_keyboard_clear_focus(state->seat); wlr_seat_keyboard_clear_focus(state->seat);
} }
void wlr_seat_keyboard_send_modifiers(struct wlr_seat *seat, static void seat_keyboard_send_modifiers(struct wlr_seat *seat,
const struct wlr_keyboard_modifiers *modifiers) { const struct wlr_keyboard_modifiers *modifiers,
struct wlr_seat_client *client = seat->keyboard_state.focused_client; struct wlr_seat_client *client,
bool force) {
const struct wlr_keyboard_modifiers empty = {};
struct wl_resource *resource;
if (client == NULL) { if (client == NULL) {
client = seat->keyboard_state.focused_client;
}
if (client == NULL) {
return;
}
if (modifiers == NULL) {
modifiers = ∅
}
struct wlr_keyboard_modifiers *tracked = &client->tracked_modifiers;
if (!force && tracked->depressed == modifiers->depressed &&
tracked->latched == modifiers->latched &&
tracked->locked == modifiers->locked &&
tracked->group == modifiers->group) {
return; return;
} }
uint32_t serial = wlr_seat_client_next_serial(client); uint32_t serial = wlr_seat_client_next_serial(client);
struct wl_resource *resource; *tracked = *modifiers;
wl_resource_for_each(resource, &client->keyboards) { wl_resource_for_each(resource, &client->keyboards) {
if (seat_client_from_keyboard_resource(resource) == NULL) { if (seat_client_from_keyboard_resource(resource) == NULL) {
continue; continue;
} }
if (modifiers == NULL) { wl_keyboard_send_modifiers(resource, serial,
wl_keyboard_send_modifiers(resource, serial, 0, 0, 0, 0); modifiers->depressed, modifiers->latched,
} else { modifiers->locked, modifiers->group);
wl_keyboard_send_modifiers(resource, serial,
modifiers->depressed, modifiers->latched,
modifiers->locked, modifiers->group);
}
} }
} }
void wlr_seat_keyboard_send_modifiers(struct wlr_seat *seat,
const struct wlr_keyboard_modifiers *modifiers) {
seat_keyboard_send_modifiers(seat, modifiers, NULL, true);
}
void wlr_seat_client_notify_modifiers(struct wlr_seat *seat,
struct wlr_seat_client *target) {
struct wlr_seat_client *focused = seat->keyboard_state.focused_client;
if (target == NULL || target == focused) {
return;
}
struct wlr_keyboard_modifiers *modifiers = &seat->keyboard_state.keyboard->modifiers;
seat_keyboard_send_modifiers(seat, modifiers, target, false);
}
void seat_client_send_keyboard_leave_raw(struct wlr_seat_client *seat_client, void seat_client_send_keyboard_leave_raw(struct wlr_seat_client *seat_client,
struct wlr_surface *surface) { struct wlr_surface *surface) {
uint32_t serial = wlr_seat_client_next_serial(seat_client); uint32_t serial = wlr_seat_client_next_serial(seat_client);
struct wl_resource *resource; struct wl_resource *resource;
const struct wlr_keyboard_modifiers unset = {};
seat_client->tracked_modifiers = unset;
wl_resource_for_each(resource, &seat_client->keyboards) { wl_resource_for_each(resource, &seat_client->keyboards) {
if (seat_client_from_keyboard_resource(resource) == NULL) { if (seat_client_from_keyboard_resource(resource) == NULL) {
continue; continue;