[PATCH 2/2] weston input: Add the key states counters

Martin Minarik minarik11 at student.fiit.stuba.sk
Tue Jun 11 16:41:52 PDT 2013


This fixes the compositor, so that it delivers the correct events, when attached
multiple keyboards to the same seat. This is tested under the evdev backend,
perhaps it works under multiseat X.

The way to verify this:
1. press A on keyboard 1
2. press A on keyboard 2
3. release A on keyboard 1
4. release A on keyboard 2

The actual sequence:

[2134420.1337]  -> wl_keyboard at 39.key(2316, 899782584, 30, 1)
[2134421.1337]  -> wl_keyboard at 39.key(2316, 899782584, 30, 1)
[2134422.1337]  -> wl_keyboard at 39.key(2316, 899782584, 30, 0)
[2134423.1337]  -> wl_keyboard at 39.key(2316, 899782584, 30, 0)

The correct sequence - after applying this patch:

[2134420.1337]  -> wl_keyboard at 39.key(2316, 899782584, 30, 1)
[2134423.1337]  -> wl_keyboard at 39.key(2316, 899782584, 30, 0)

The reason is, because the wl_keyboard device must from the client's
perspective act like single input device, as discussed.
---
 src/compositor-wayland.c |   12 +++++-
 src/compositor-x11.c     |   17 +++++---
 src/compositor.h         |    2 +
 src/evdev.c              |   28 ++++++++-----
 src/evdev.h              |    6 +--
 src/input.c              |  104 +++++++++++++++++++++++++++++++++-------------
 tests/weston-test.c      |    5 ++-
 7 files changed, 125 insertions(+), 49 deletions(-)

diff --git a/src/compositor-wayland.c b/src/compositor-wayland.c
index f3a98a8..f46809e 100644
--- a/src/compositor-wayland.c
+++ b/src/compositor-wayland.c
@@ -529,11 +529,21 @@ input_handle_keyboard_enter(void *data,
 			    struct wl_array *keys)
 {
 	struct wayland_input *input = data;
+	struct wl_array key_states;
+	unsigned int *k_cnt;
+
+	wl_array_init(&key_states);
+	wl_array_add(&key_states, keys->size);
+	wl_array_for_each(k_cnt, &key_states) {
+		*k_cnt = 1;
+	}
 
 	/* XXX: If we get a modifier event immediately before the focus,
 	 *      we should try to keep the same serial. */
-	notify_keyboard_focus_in(&input->base, keys,
+	notify_keyboard_focus_in(&input->base, keys, &key_states,
 				 STATE_UPDATE_AUTOMATIC);
+
+	wl_array_release(&key_states);
 }
 
 static void
diff --git a/src/compositor-x11.c b/src/compositor-x11.c
index 5a0bcf0..a1e2b57 100644
--- a/src/compositor-x11.c
+++ b/src/compositor-x11.c
@@ -65,7 +65,6 @@ struct x11_compositor {
 	xcb_connection_t	*conn;
 	xcb_screen_t		*screen;
 	xcb_cursor_t		 null_cursor;
-	struct wl_array		 keys;
 	struct wl_event_source	*xcb_source;
 	struct xkb_keymap	*xkb_keymap;
 	unsigned int		 has_xkb;
@@ -1181,6 +1180,8 @@ static int
 x11_compositor_handle_event(int fd, uint32_t mask, void *data)
 {
 	struct x11_compositor *c = data;
+	struct wl_array keys;
+	struct wl_array key_states;
 	struct x11_output *output;
 	xcb_generic_event_t *event, *prev;
 	xcb_client_message_event_t *client_message;
@@ -1225,6 +1226,7 @@ x11_compositor_handle_event(int fd, uint32_t mask, void *data)
 					   key_release->detail - 8,
 					   WL_KEYBOARD_KEY_STATE_RELEASED,
 					   STATE_UPDATE_AUTOMATIC);
+
 				free(prev);
 				prev = NULL;
 				break;
@@ -1233,13 +1235,16 @@ x11_compositor_handle_event(int fd, uint32_t mask, void *data)
 		case XCB_FOCUS_IN:
 			assert(response_type == XCB_KEYMAP_NOTIFY);
 			keymap_notify = (xcb_keymap_notify_event_t *) event;
-			c->keys.size = 0;
+			wl_array_init(&keys);
+			wl_array_init(&key_states);
 			for (i = 0; i < ARRAY_LENGTH(keymap_notify->keys) * 8; i++) {
 				set = keymap_notify->keys[i >> 3] &
 					(1 << (i & 7));
 				if (set) {
-					k = wl_array_add(&c->keys, sizeof *k);
+					k = wl_array_add(&keys, sizeof *k);
 					*k = i;
+					k = wl_array_add(&key_states, sizeof *k);
+					*k = 1;
 				}
 			}
 
@@ -1247,9 +1252,12 @@ x11_compositor_handle_event(int fd, uint32_t mask, void *data)
 			 * event, rather than with the focus event.  I'm not
 			 * sure of the exact semantics around it and whether
 			 * we can ensure that we get both? */
-			notify_keyboard_focus_in(&c->core_seat, &c->keys,
+			notify_keyboard_focus_in(&c->core_seat, &keys, &key_states,
 						 STATE_UPDATE_AUTOMATIC);
 
+			wl_array_release(&keys);
+			wl_array_release(&key_states);
+
 			free(prev);
 			prev = NULL;
 			break;
@@ -1540,7 +1548,6 @@ x11_compositor_create(struct wl_display *display,
 
 	s = xcb_setup_roots_iterator(xcb_get_setup(c->conn));
 	c->screen = s.data;
-	wl_array_init(&c->keys);
 
 	x11_compositor_get_resources(c);
 	x11_compositor_get_wm_info(c);
diff --git a/src/compositor.h b/src/compositor.h
index 22700b7..e6402f5 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -403,6 +403,7 @@ struct weston_keyboard {
 	uint32_t grab_time;
 
 	struct wl_array keys;
+	struct wl_array key_states;
 
 	struct {
 		uint32_t mods_depressed;
@@ -849,6 +850,7 @@ notify_pointer_focus(struct weston_seat *seat, struct weston_output *output,
 
 void
 notify_keyboard_focus_in(struct weston_seat *seat, struct wl_array *keys,
+			 struct wl_array *key_states,
 			 enum weston_key_state_update update_state);
 void
 notify_keyboard_focus_out(struct weston_seat *seat);
diff --git a/src/evdev.c b/src/evdev.c
index 8058c8f..f43c334 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -710,20 +710,24 @@ evdev_device_destroy(struct evdev_device *device)
 }
 
 inline void
-evdev_keys_down_array(struct wl_list *evdev_devices, struct wl_array *keys)
+evdev_key_states_arrays(struct wl_list *evdev_devices, struct wl_array *keys,
+			struct wl_array *key_states)
 {
 	struct evdev_device *device;
-	uint32_t i;
+	uint32_t i, key_state;
 	uint32_t *k;
 
 	for (i = 0; i < KEY_CNT; i++) {
+		key_state = 0;
 		wl_list_for_each(device, evdev_devices, link) {
-			if (TEST_BIT(device->deliv_state.keys, i)) {
-				k = wl_array_add(keys, sizeof *k);
-				*k = i;
-				break;
-				/* XXX:Perhaps deliver duplicate keys too */
-			}
+			if (TEST_BIT(device->deliv_state.keys, i))
+				key_state++;
+		}
+		if (key_state > 0) {
+			k = wl_array_add(keys, sizeof *k);
+			*k = i;
+			k = wl_array_add(key_states, sizeof *k);
+			*k = key_state;
 		}
 	}
 }
@@ -732,7 +736,7 @@ void
 evdev_notify_keyboard_focus(struct weston_seat *seat,
 			    struct wl_list *evdev_devices)
 {
-	struct wl_array keys;
+	struct wl_array keys, key_states;
 	struct evdev_device *device;
 
 	wl_list_for_each(device, evdev_devices, link) {
@@ -740,7 +744,9 @@ evdev_notify_keyboard_focus(struct weston_seat *seat,
 	}
 
 	wl_array_init(&keys);
-	evdev_keys_down_array(evdev_devices, &keys);
-	notify_keyboard_focus_in(seat, &keys, STATE_UPDATE_AUTOMATIC);
+	wl_array_init(&key_states);
+	evdev_key_states_arrays(evdev_devices, &keys, &key_states);
+	notify_keyboard_focus_in(seat, &keys, &key_states, STATE_UPDATE_AUTOMATIC);
 	wl_array_release(&keys);
+	wl_array_release(&key_states);
 }
diff --git a/src/evdev.h b/src/evdev.h
index e6a8e4c..c0bc824 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -129,9 +129,9 @@ evdev_device_destroy(struct evdev_device *device);
 void
 evdev_keys_down_release(struct evdev_device *device);
 
-void
-evdev_keys_down_array(struct wl_list *evdev_devices,
-			   struct wl_array *keys);
+inline void
+evdev_key_states_arrays(struct wl_list *evdev_devices, struct wl_array *keys,
+			struct wl_array *key_states);
 int
 evdev_keys_state_sync(struct evdev_device *d, unsigned int time);
 
diff --git a/src/input.c b/src/input.c
index ae75054..5d9c60b 100644
--- a/src/input.c
+++ b/src/input.c
@@ -357,6 +357,7 @@ weston_keyboard_create(void)
 	memset(keyboard, 0, sizeof *keyboard);
 	wl_list_init(&keyboard->resource_list);
 	wl_array_init(&keyboard->keys);
+	wl_array_init(&keyboard->key_states);
 	keyboard->focus_listener.notify = lose_keyboard_focus;
 	keyboard->default_grab.interface = &default_keyboard_grab_interface;
 	keyboard->default_grab.keyboard = keyboard;
@@ -373,6 +374,7 @@ weston_keyboard_destroy(struct weston_keyboard *keyboard)
 	if (keyboard->focus_resource)
 		wl_list_remove(&keyboard->focus_listener.link);
 	wl_array_release(&keyboard->keys);
+	wl_array_release(&keyboard->key_states);
 	free(keyboard);
 }
 
@@ -817,8 +819,10 @@ update_modifier_state(struct weston_seat *seat, uint32_t serial, uint32_t key,
 
 	if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
 		direction = XKB_KEY_DOWN;
-	else
+	else if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
 		direction = XKB_KEY_UP;
+	else
+		return;
 
 	/* Offset the keycode by 8, as the evdev XKB rules reflect X's
 	 * broken keycode system, which starts at 8. */
@@ -827,6 +831,48 @@ update_modifier_state(struct weston_seat *seat, uint32_t serial, uint32_t key,
 	notify_modifiers(seat, serial);
 }
 
+static uint32_t
+update_key_state(struct weston_keyboard *keyboard, uint32_t key,
+	   enum wl_keyboard_key_state state)
+{
+	uint32_t *k, *k_cnt, *end, *end_cnt, ret_cnt = 0;
+
+	end = keyboard->keys.data + keyboard->keys.size;
+	end_cnt = keyboard->key_states.data + keyboard->key_states.size;
+
+	k_cnt = keyboard->key_states.data;
+
+	wl_array_for_each(k, &keyboard->keys) {
+
+		if (*k == key) {
+			if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+				ret_cnt = ++*k_cnt;
+			} else {
+				ret_cnt = --*k_cnt;
+				if (ret_cnt == 0) {
+					*k = *(end-1);
+					*k_cnt = *(end_cnt-1);
+					keyboard->keys.size -= sizeof *k;
+					keyboard->key_states.size -= sizeof *k_cnt;
+				}
+			}
+			break;
+		}
+		k_cnt++;
+	}
+
+	if (k >= end) {
+		assert (state == WL_KEYBOARD_KEY_STATE_PRESSED);
+		k = wl_array_add(&keyboard->keys, sizeof *k);
+		k_cnt = wl_array_add(&keyboard->key_states, sizeof *k);
+		*k = key;
+		ret_cnt = *k_cnt = 1;
+	}
+
+	return ret_cnt;
+}
+
+
 WL_EXPORT void
 notify_key(struct weston_seat *seat, uint32_t time, uint32_t key,
 	   enum wl_keyboard_key_state state,
@@ -838,7 +884,7 @@ notify_key(struct weston_seat *seat, uint32_t time, uint32_t key,
 		(struct weston_surface *) keyboard->focus;
 	struct weston_keyboard_grab *grab = keyboard->grab;
 	uint32_t serial = wl_display_next_serial(compositor->wl_display);
-	uint32_t *k, *end;
+	uint32_t depressed_cnt;
 
 	if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
 		if (compositor->ping_handler && focus)
@@ -851,35 +897,23 @@ notify_key(struct weston_seat *seat, uint32_t time, uint32_t key,
 		weston_compositor_idle_release(compositor);
 	}
 
-	end = keyboard->keys.data + keyboard->keys.size;
-	for (k = keyboard->keys.data; k < end; k++) {
-		if (*k == key) {
-			/* Ignore server-generated repeats. */
-			if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
-				return;
-			*k = *--end;
-		}
-	}
-	keyboard->keys.size = (void *) end - keyboard->keys.data;
-	if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
-		k = wl_array_add(&keyboard->keys, sizeof *k);
-		*k = key;
-	}
+	depressed_cnt = update_key_state(keyboard, key, state);
 
 	if (grab == &keyboard->default_grab ||
 	    grab == &keyboard->input_method_grab) {
 		weston_compositor_run_key_binding(compositor, seat, time, key,
-						  state);
+						  depressed_cnt);
 		grab = keyboard->grab;
 	}
 
-	grab->interface->key(grab, time, key, state);
+	if (depressed_cnt == state)
+		grab->interface->key(grab, time, key, depressed_cnt);
 
 	if (update_state == STATE_UPDATE_AUTOMATIC) {
 		update_modifier_state(seat,
 				      wl_display_get_serial(compositor->wl_display),
 				      key,
-				      state);
+				      depressed_cnt);
 	}
 }
 
@@ -912,26 +946,37 @@ destroy_device_saved_kbd_focus(struct wl_listener *listener, void *data)
 
 WL_EXPORT void
 notify_keyboard_focus_in(struct weston_seat *seat, struct wl_array *keys,
+			 struct wl_array *key_states,
 			 enum weston_key_state_update update_state)
 {
 	struct weston_compositor *compositor = seat->compositor;
 	struct weston_keyboard *keyboard = seat->keyboard;
 	struct weston_surface *surface;
-	uint32_t *k, serial;
+	uint32_t *k, *k_cnt, serial;
+	unsigned int i;
 
 	serial = wl_display_next_serial(compositor->wl_display);
 	wl_array_copy(&keyboard->keys, keys);
+	wl_array_copy(&keyboard->key_states, key_states);
+
+	wl_array_for_each(k_cnt, &keyboard->key_states) {
+		for (i = 0; i < *k_cnt; i++)
+			weston_compositor_idle_inhibit(compositor);
+	}
+	k_cnt = keyboard->key_states.data;
+
 	wl_array_for_each(k, &keyboard->keys) {
-		weston_compositor_idle_inhibit(compositor);
 		if (update_state == STATE_UPDATE_AUTOMATIC)
-			update_modifier_state(seat, serial, *k,
-					      WL_KEYBOARD_KEY_STATE_PRESSED);
+			update_modifier_state(seat, serial, *k, *k_cnt);
+		k_cnt++;
 	}
 
 	/* Run key bindings after we've updated the state. */
+	k_cnt = keyboard->key_states.data;
+
 	wl_array_for_each(k, &keyboard->keys) {
-		weston_compositor_run_key_binding(compositor, seat, 0, *k,
-						  WL_KEYBOARD_KEY_STATE_PRESSED);
+		weston_compositor_run_key_binding(compositor, seat, 0, *k, *k_cnt);
+		k_cnt++;
 	}
 
 	surface = seat->saved_kbd_focus;
@@ -949,12 +994,15 @@ notify_keyboard_focus_out(struct weston_seat *seat)
 	struct weston_compositor *compositor = seat->compositor;
 	struct weston_keyboard *keyboard = seat->keyboard;
 	uint32_t *k, serial;
+	unsigned int i;
 
 	serial = wl_display_next_serial(compositor->wl_display);
+	wl_array_for_each(k, &keyboard->key_states) {
+		for (i = 0; i > *k; i++)
+			weston_compositor_idle_release(compositor);
+	}
 	wl_array_for_each(k, &keyboard->keys) {
-		weston_compositor_idle_release(compositor);
-		update_modifier_state(seat, serial, *k,
-				      WL_KEYBOARD_KEY_STATE_RELEASED);
+		update_modifier_state(seat, serial, *k, 0);
 	}
 
 	seat->modifier_state = 0;
diff --git a/tests/weston-test.c b/tests/weston-test.c
index b629c86..205754b 100644
--- a/tests/weston-test.c
+++ b/tests/weston-test.c
@@ -150,12 +150,15 @@ activate_surface(struct wl_client *client, struct wl_resource *resource,
 					surface_resource->data : NULL;
 	struct weston_test *test = resource->data;
 	struct weston_seat *seat;
+	struct wl_array keys, key_states;
+	wl_array_init(&keys);
+	wl_array_init(&key_states);
 
 	seat = get_seat(test);
 
 	if (surface) {
 		weston_surface_activate(surface, seat);
-		notify_keyboard_focus_in(seat, &seat->keyboard->keys,
+		notify_keyboard_focus_in(seat, &keys, &key_states,
 					 STATE_UPDATE_AUTOMATIC);
 	}
 	else {
-- 
1.7.10.4



More information about the wayland-devel mailing list