wlr_keyboard_group: introduce enter and leave

This introduces the enter and leave events for wlr_keyboard_group.

The enter event is emitted when a keyboard is added to the group while a
key is pressed that is not pressed by any other keyboard in the group.
The data is a wl_array of the pressed key codes unique to the keyboard
that should now be considered pressed.

Similarly the leave event is emitted when a keyboard is removed from the
group while at least one key is pressed that is not pressed by any other
keyboard in the group. The data is a wl_array of the pressed key codes
unique to the keyboard that should now be considered released.

The purpose of these events are to allow the compositor to update its
state to avoid corruption. Additionally, for the leave event, the
focused surface may have been notified of a key press for some or all of
the key codes and needs to be notified of a key release to avoid state
corruption.

These were previously emitted as normal key events, but they are not
normal key events. There is no actual key press or release associated
with the events. It's purely for state keeping purposes. Emitting them
as separate events allows the compositor to handle them differently.
Since these are purely for state keeping purposes and are not associated
with an actual key being pressed or released, bindings should not be
triggered as a result of these events.
This commit is contained in:
Brian Ashworth 2020-05-31 17:10:15 -04:00 committed by Tudor Brindus
parent 8ab4d91380
commit 32148808ad
4 changed files with 84 additions and 12 deletions

View File

@ -0,0 +1,8 @@
#include "wlr/types/wlr_keyboard.h"
void keyboard_key_update(struct wlr_keyboard *keyboard,
struct wlr_event_keyboard_key *event);
bool keyboard_modifier_update(struct wlr_keyboard *keyboard);
void keyboard_led_update(struct wlr_keyboard *keyboard);

View File

@ -18,6 +18,30 @@ struct wlr_keyboard_group {
struct wlr_input_device *input_device; struct wlr_input_device *input_device;
struct wl_list devices; // keyboard_group_device::link struct wl_list devices; // keyboard_group_device::link
struct wl_list keys; // keyboard_group_key::link struct wl_list keys; // keyboard_group_key::link
struct {
/*
* Sent when a keyboard has entered the group with keys currently
* pressed that are not pressed by any other keyboard in the group. The
* data for this signal will be a wl_array containing the key codes.
* This should be used to update the compositor's internal state.
* Bindings should not be triggered based off of these key codes and
* they should also not notify any surfaces of the key press.
*/
struct wl_signal enter;
/*
* Sent when a keyboard has left the group with keys currently pressed
* that are not pressed by any other keyboard in the group. The data for
* this signal will be a wl_array containing the key codes. This should
* be used to update the compositor's internal state. Bindings should
* not be triggered based off of these key codes. Additionally, surfaces
* should only be notified if they received a corresponding key press
* for the key code.
*/
struct wl_signal leave;
} events;
void *data; void *data;
}; };

View File

@ -6,9 +6,10 @@
#include <wlr/interfaces/wlr_keyboard.h> #include <wlr/interfaces/wlr_keyboard.h>
#include <wlr/types/wlr_keyboard.h> #include <wlr/types/wlr_keyboard.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include "types/wlr_keyboard.h"
#include "util/signal.h" #include "util/signal.h"
static void keyboard_led_update(struct wlr_keyboard *keyboard) { void keyboard_led_update(struct wlr_keyboard *keyboard) {
if (keyboard->xkb_state == NULL) { if (keyboard->xkb_state == NULL) {
return; return;
} }
@ -27,7 +28,7 @@ static void keyboard_led_update(struct wlr_keyboard *keyboard) {
* Update the modifier state of the wlr-keyboard. Returns true if the modifier * Update the modifier state of the wlr-keyboard. Returns true if the modifier
* state changed. * state changed.
*/ */
static bool keyboard_modifier_update(struct wlr_keyboard *keyboard) { bool keyboard_modifier_update(struct wlr_keyboard *keyboard) {
if (keyboard->xkb_state == NULL) { if (keyboard->xkb_state == NULL) {
return false; return false;
} }
@ -55,7 +56,7 @@ static bool keyboard_modifier_update(struct wlr_keyboard *keyboard) {
return true; return true;
} }
static void keyboard_key_update(struct wlr_keyboard *keyboard, void keyboard_key_update(struct wlr_keyboard *keyboard,
struct wlr_event_keyboard_key *event) { struct wlr_event_keyboard_key *event) {
if (event->state == WLR_KEY_PRESSED) { if (event->state == WLR_KEY_PRESSED) {
set_add(keyboard->keycodes, &keyboard->num_keycodes, set_add(keyboard->keycodes, &keyboard->num_keycodes,

View File

@ -5,6 +5,8 @@
#include <time.h> #include <time.h>
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
#include "types/wlr_keyboard.h"
#include "util/signal.h"
#include "wlr/interfaces/wlr_keyboard.h" #include "wlr/interfaces/wlr_keyboard.h"
#include "wlr/types/wlr_keyboard.h" #include "wlr/types/wlr_keyboard.h"
#include "wlr/types/wlr_keyboard_group.h" #include "wlr/types/wlr_keyboard_group.h"
@ -70,6 +72,9 @@ struct wlr_keyboard_group *wlr_keyboard_group_create(void) {
wl_list_init(&group->devices); wl_list_init(&group->devices);
wl_list_init(&group->keys); wl_list_init(&group->keys);
wl_signal_init(&group->events.enter);
wl_signal_init(&group->events.leave);
return group; return group;
} }
@ -81,11 +86,9 @@ struct wlr_keyboard_group *wlr_keyboard_group_from_wlr_keyboard(
return (struct wlr_keyboard_group *)keyboard; return (struct wlr_keyboard_group *)keyboard;
} }
static void handle_keyboard_key(struct wl_listener *listener, void *data) { static bool process_key(struct keyboard_group_device *group_device,
struct keyboard_group_device *group_device = struct wlr_event_keyboard_key *event) {
wl_container_of(listener, group_device, key);
struct wlr_keyboard_group *group = group_device->keyboard->group; struct wlr_keyboard_group *group = group_device->keyboard->group;
struct wlr_event_keyboard_key *event = data;
struct keyboard_group_key *key, *tmp; struct keyboard_group_key *key, *tmp;
wl_list_for_each_safe(key, tmp, &group->keys, link) { wl_list_for_each_safe(key, tmp, &group->keys, link) {
@ -94,12 +97,12 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
} }
if (event->state == WLR_KEY_PRESSED) { if (event->state == WLR_KEY_PRESSED) {
key->count++; key->count++;
return; return false;
} }
if (event->state == WLR_KEY_RELEASED) { if (event->state == WLR_KEY_RELEASED) {
key->count--; key->count--;
if (key->count > 0) { if (key->count > 0) {
return; return false;
} }
wl_list_remove(&key->link); wl_list_remove(&key->link);
free(key); free(key);
@ -112,14 +115,22 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
calloc(1, sizeof(struct keyboard_group_key)); calloc(1, sizeof(struct keyboard_group_key));
if (!key) { if (!key) {
wlr_log(WLR_ERROR, "Failed to allocate keyboard_group_key"); wlr_log(WLR_ERROR, "Failed to allocate keyboard_group_key");
return; return false;
} }
key->keycode = event->keycode; key->keycode = event->keycode;
key->count = 1; key->count = 1;
wl_list_insert(&group->keys, &key->link); wl_list_insert(&group->keys, &key->link);
} }
return true;
}
static void handle_keyboard_key(struct wl_listener *listener, void *data) {
struct keyboard_group_device *group_device =
wl_container_of(listener, group_device, key);
if (process_key(group_device, data)) {
wlr_keyboard_notify_key(&group_device->keyboard->group->keyboard, data); wlr_keyboard_notify_key(&group_device->keyboard->group->keyboard, data);
}
} }
static void handle_keyboard_modifiers(struct wl_listener *listener, static void handle_keyboard_modifiers(struct wl_listener *listener,
@ -189,6 +200,9 @@ static void handle_keyboard_repeat_info(struct wl_listener *listener,
static void refresh_state(struct keyboard_group_device *device, static void refresh_state(struct keyboard_group_device *device,
enum wlr_key_state state) { enum wlr_key_state state) {
struct wl_array keys;
wl_array_init(&keys);
for (size_t i = 0; i < device->keyboard->num_keycodes; i++) { for (size_t i = 0; i < device->keyboard->num_keycodes; i++) {
struct timespec now; struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
@ -198,8 +212,31 @@ static void refresh_state(struct keyboard_group_device *device,
.update_state = true, .update_state = true,
.state = state .state = state
}; };
handle_keyboard_key(&device->key, &event);
// Update the group's key state and determine whether this is a unique
// key that needs to be passed on to the compositor
if (process_key(device, &event)) {
// Update state for wlr_keyboard_group's keyboard
keyboard_key_update(&device->keyboard->group->keyboard, &event);
keyboard_modifier_update(&device->keyboard->group->keyboard);
keyboard_led_update(&device->keyboard->group->keyboard);
// Add the key to the array
uint32_t *key = wl_array_add(&keys, sizeof(uint32_t));
*key = event.keycode;
} }
}
// If there are any unique keys, emit the enter/leave event
if (keys.size > 0) {
if (state == WLR_KEY_PRESSED) {
wlr_signal_emit_safe(&device->keyboard->group->events.enter, &keys);
} else {
wlr_signal_emit_safe(&device->keyboard->group->events.leave, &keys);
}
}
wl_array_release(&keys);
} }
static void remove_keyboard_group_device(struct keyboard_group_device *device) { static void remove_keyboard_group_device(struct keyboard_group_device *device) {
@ -298,6 +335,8 @@ void wlr_keyboard_group_destroy(struct wlr_keyboard_group *group) {
} }
wlr_keyboard_destroy(&group->keyboard); wlr_keyboard_destroy(&group->keyboard);
wl_list_remove(&group->input_device->events.destroy.listener_list); wl_list_remove(&group->input_device->events.destroy.listener_list);
wl_list_remove(&group->events.enter.listener_list);
wl_list_remove(&group->events.leave.listener_list);
free(group->input_device); free(group->input_device);
free(group); free(group);
} }