File mutter-fix-modifiers.patch of Package mutter
From 7df1c974d5d7c7e0cfb1bb7d926725f6829f1d12 Mon Sep 17 00:00:00 2001
From: Carlos Garnacho <carlosg@gnome.org>
Date: Thu, 25 Sep 2025 15:59:20 +0200
Subject: [PATCH 1/3] core: Let key presses of special modifiers through
When handling the key press of special modifier keys (Super for all
our compositor level keybindings, Control for "locate pointer"), let
this event go through, and possibly be forwarded to Wayland clients.
Rationale is: On X11, we had the passive grab mechanism that possibly
replayed the full sequence of shortcut keystrokes to the client this
allowed clients to seamlessly handle keystrokes that had these special
modifiers. On Wayland, we do nothing of that, except (oh-oh) consuming
the event. This could leave Wayland clients' keyboard tracking unbalanced,
and was compensated at MetaWaylandKeyboard by tracking modifier changes
during update(). We don't need to fully replicate the passive grab mechanism
on Wayland and we can steal the focus at any time, so we may as well send
the key press, and wl_keyboard.leave will clean up when we steal focus away.
Letting this event go through on X11 should have no ill effects, the
keyboard remains passively grabbed by the WM, so allowing further event
propagation only makes more places of the shell able to maybe handle the
Super key press event. Passive grabs are still the mechanism to lend back
control to clients, as opposed to something that happens inside GNOME Shell.
While at it, change to CLUTTER_EVENT_* defines that are clearer at the
intention.
---
src/core/keybindings.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/core/keybindings.c b/src/core/keybindings.c
index 6f2efcc6c4..63fbff63e8 100644
--- a/src/core/keybindings.c
+++ b/src/core/keybindings.c
@@ -1548,6 +1548,8 @@ process_special_modifier_key (MetaDisplay *display,
* per-window key bindings or to the application */
meta_compositor_handle_event (compositor, event, window,
META_EVENT_MODE_REPLAY);
+
+ return CLUTTER_EVENT_PROPAGATE;
}
}
else if (clutter_event_type (event) == CLUTTER_KEY_RELEASE)
@@ -1564,7 +1566,7 @@ process_special_modifier_key (MetaDisplay *display,
meta_compositor_handle_event (compositor, event, window,
META_EVENT_MODE_THAW);
- return TRUE;
+ return CLUTTER_EVENT_STOP;
}
else if (clutter_event_type (event) == CLUTTER_KEY_PRESS &&
((modifiers & ~(IGNORED_MODIFIERS)) & CLUTTER_MODIFIER_MASK) == 0 &&
@@ -1576,10 +1578,10 @@ process_special_modifier_key (MetaDisplay *display,
meta_compositor_handle_event (compositor, event, window,
META_EVENT_MODE_KEEP_FROZEN);
- return TRUE;
+ return CLUTTER_EVENT_PROPAGATE;
}
else
- return FALSE;
+ return CLUTTER_EVENT_PROPAGATE;
}
--
2.51.0
From 9d6b65d112c898138123429d47ebea939f5d320b Mon Sep 17 00:00:00 2001
From: Carlos Garnacho <carlosg@gnome.org>
Date: Thu, 25 Sep 2025 12:08:01 +0200
Subject: [PATCH 2/3] wayland: Check modifier state from event prior to event
delivery
Refactor the code handling possible modifier state changes prior to event
delivery so they all happen in handle_event(), and use the modifier state
from incoming events there, in order to ensure the client is in sync with
the expected modifiers for the upcoming event.
After the key event is delivered, we still do check the keymap state for
the most recent modifier state (e.g. after the key(s) were handled). This
risks more wl_keyboard.modifier changes than strictly necessary, but there
should an additional guarantee about it: That a wl_keyboard.key will always
have a last wl_keyboard.modifiers matching the ClutterEvent state.
In exchange for not doing event delivery during update(), we also need to
ensure the modifiers are up to date when entering a new surface, e.g. after
alt-tab. Generally, we shouldn't be doing Wayland protocol send*() calls
from update(), so this is a move in the right direction.
---
src/wayland/meta-wayland-keyboard.c | 65 ++++++++++++++++++++++-------
1 file changed, 51 insertions(+), 14 deletions(-)
diff --git a/src/wayland/meta-wayland-keyboard.c b/src/wayland/meta-wayland-keyboard.c
index ac22adf663..d7e044c458 100644
--- a/src/wayland/meta-wayland-keyboard.c
+++ b/src/wayland/meta-wayland-keyboard.c
@@ -527,8 +527,28 @@ update_pressed_keys (struct wl_array *keys,
}
}
-static void
-maybe_update_modifiers (MetaWaylandKeyboard *keyboard)
+static gboolean
+maybe_update_modifiers (MetaWaylandKeyboard *keyboard,
+ ClutterModifierType pressed,
+ ClutterModifierType latched,
+ ClutterModifierType locked)
+{
+ if (keyboard->xkb_info.modifiers.pressed != pressed ||
+ keyboard->xkb_info.modifiers.latched != latched ||
+ keyboard->xkb_info.modifiers.locked != locked)
+ {
+ keyboard->xkb_info.modifiers.pressed = pressed;
+ keyboard->xkb_info.modifiers.latched = latched;
+ keyboard->xkb_info.modifiers.locked = locked;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+maybe_update_modifiers_from_keymap (MetaWaylandKeyboard *keyboard)
{
MetaBackend *backend = backend_from_keyboard (keyboard);
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
@@ -541,16 +561,27 @@ maybe_update_modifiers (MetaWaylandKeyboard *keyboard)
&latched,
&locked);
- if (keyboard->xkb_info.modifiers.pressed != pressed ||
- keyboard->xkb_info.modifiers.latched != latched ||
- keyboard->xkb_info.modifiers.locked != locked)
- {
- keyboard->xkb_info.modifiers.pressed = pressed;
- keyboard->xkb_info.modifiers.latched = latched;
- keyboard->xkb_info.modifiers.locked = locked;
+ return maybe_update_modifiers (keyboard, pressed, latched, locked);
+}
- notify_modifiers (keyboard);
- }
+static gboolean
+maybe_update_modifiers_from_event (MetaWaylandKeyboard *keyboard,
+ const ClutterEvent *event)
+{
+ ClutterModifierType pressed, locked, latched;
+ ClutterEventType event_type;
+
+ event_type = clutter_event_type (event);
+
+ if (event_type != CLUTTER_KEY_PRESS && event_type != CLUTTER_KEY_RELEASE)
+ return FALSE;
+
+ clutter_event_get_key_state (event,
+ &pressed,
+ &latched,
+ &locked);
+
+ return maybe_update_modifiers (keyboard, pressed, latched, locked);
}
void
@@ -566,8 +597,6 @@ meta_wayland_keyboard_update (MetaWaylandKeyboard *keyboard,
if (!update_pressed_keys (&keyboard->pressed_keys, evdev_code, is_press))
return;
- maybe_update_modifiers (keyboard);
-
xkb_state_update_key (keyboard->xkb_info.state,
hardware_keycode,
is_press ? XKB_KEY_DOWN : XKB_KEY_UP);
@@ -599,6 +628,10 @@ meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard,
is_press ? "press" : "release",
hardware_keycode);
+ if (maybe_update_modifiers_from_event (keyboard,
+ (const ClutterEvent *) event))
+ notify_modifiers (keyboard);
+
handled = meta_wayland_keyboard_broadcast_key (keyboard,
(const ClutterEvent *) event);
@@ -612,7 +645,8 @@ meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard,
"No wayland surface is focused, continuing normal operation");
}
- maybe_update_modifiers (keyboard);
+ if (maybe_update_modifiers_from_keymap (keyboard))
+ notify_modifiers (keyboard);
return handled;
}
@@ -671,6 +705,7 @@ broadcast_focus (MetaWaylandKeyboard *keyboard,
wl_keyboard_send_enter (resource, keyboard->focus_serial,
keyboard->focus_surface->resource,
&keyboard->pressed_keys);
+
keyboard_send_modifiers (keyboard, resource, keyboard->focus_serial);
}
@@ -734,6 +769,8 @@ meta_wayland_keyboard_set_focus (MetaWaylandKeyboard *keyboard,
keyboard->focus_serial =
meta_wayland_input_device_next_serial (input_device);
+ maybe_update_modifiers_from_keymap (keyboard);
+
wl_resource_for_each (resource, &keyboard->focus_resource_list)
{
broadcast_focus (keyboard, resource);
--
2.51.0
From d66094486383562ec8a38594ec821a7f4f3bdd0f Mon Sep 17 00:00:00 2001
From: Carlos Garnacho <carlosg@gnome.org>
Date: Fri, 26 Sep 2025 08:20:51 +0200
Subject: [PATCH 3/3] tests: Add some tests for wl_keyboard behavior
Test .key and .modifier order, focus changes through alt-tab, and
a Super-based keycombo that is not handled by the compositor.
---
src/tests/meson.build | 11 +
src/tests/wayland-keyboard-tests.c | 300 +++++++++++++++++++++
src/tests/wayland-test-clients/keyboard.c | 297 ++++++++++++++++++++
src/tests/wayland-test-clients/meson.build | 3 +
4 files changed, 611 insertions(+)
create mode 100644 src/tests/wayland-keyboard-tests.c
create mode 100644 src/tests/wayland-test-clients/keyboard.c
diff --git a/src/tests/meson.build b/src/tests/meson.build
index c1dc9ea9d6..692b23c433 100644
--- a/src/tests/meson.build
+++ b/src/tests/meson.build
@@ -914,6 +914,17 @@ wayland_test_cases = [
test_client_executables.get('drm-lease'),
],
},
+ {
+ 'name': 'wayland-keyboard',
+ 'suite': 'wayland',
+ 'sources': [
+ 'wayland-keyboard-tests.c',
+ wayland_test_utils,
+ ],
+ 'depends': [
+ test_client_executables.get('keyboard'),
+ ],
+ },
{
'name': 'wayland-fullscreen',
'suite': 'wayland',
diff --git a/src/tests/wayland-keyboard-tests.c b/src/tests/wayland-keyboard-tests.c
new file mode 100644
index 0000000000..5e43ccd50a
--- /dev/null
+++ b/src/tests/wayland-keyboard-tests.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2025 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Carlos Garnacho <carlosg@gnome.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+#include <wayland-client.h>
+#include <gdesktop-enums.h>
+#include <linux/input-event-codes.h>
+
+#include "backends/meta-virtual-monitor.h"
+#include "backends/native/meta-backend-native.h"
+#include "backends/native/meta-kms.h"
+#include "backends/native/meta-kms-device.h"
+#include "core/window-private.h"
+#include "meta-test/meta-context-test.h"
+#include "tests/meta-test-utils.h"
+#include "tests/meta-monitor-test-utils.h"
+#include "tests/meta-ref-test.h"
+#include "tests/meta-wayland-test-driver.h"
+#include "tests/meta-wayland-test-utils.h"
+
+#include "dummy-client-protocol.h"
+#include "dummy-server-protocol.h"
+
+static MetaContext *test_context;
+static MetaWaylandTestDriver *test_driver;
+static MetaVirtualMonitor *virtual_monitor;
+
+static void
+wait_for_sync_point (unsigned int sync_point)
+{
+ meta_wayland_test_driver_wait_for_sync_point (test_driver, sync_point);
+}
+
+static void
+keyboard_event_order (void)
+{
+ MetaBackend *backend = meta_context_get_backend (test_context);
+ ClutterSeat *seat = meta_backend_get_default_seat (backend);
+ g_autoptr (ClutterVirtualInputDevice) virtual_keyboard = NULL;
+ MetaWaylandTestClient *wayland_test_client;
+
+ virtual_keyboard = clutter_seat_create_virtual_device (seat,
+ CLUTTER_KEYBOARD_DEVICE);
+
+ /* Test correct event order of key and modifier events */
+ wayland_test_client =
+ meta_wayland_test_client_new_with_args (test_context,
+ "keyboard",
+ "event-order",
+ NULL);
+ meta_wait_for_client_window (test_context, "event-order");
+ wait_for_sync_point (0);
+
+ clutter_virtual_input_device_notify_key (virtual_keyboard,
+ g_get_monotonic_time (),
+ KEY_LEFTSHIFT,
+ CLUTTER_KEY_STATE_PRESSED);
+
+ wait_for_sync_point (1);
+
+ clutter_virtual_input_device_notify_key (virtual_keyboard,
+ g_get_monotonic_time (),
+ KEY_LEFTSHIFT,
+ CLUTTER_KEY_STATE_RELEASED);
+
+ meta_wayland_test_client_finish (wayland_test_client);
+}
+
+static void
+keyboard_event_order2 (void)
+{
+ MetaBackend *backend = meta_context_get_backend (test_context);
+ ClutterSeat *seat = meta_backend_get_default_seat (backend);
+ g_autoptr (ClutterVirtualInputDevice) virtual_keyboard = NULL;
+ MetaWaylandTestClient *wayland_test_client;
+
+ virtual_keyboard = clutter_seat_create_virtual_device (seat,
+ CLUTTER_KEYBOARD_DEVICE);
+
+ /* Test that pressed non-modifier already has modifier set */
+ wayland_test_client =
+ meta_wayland_test_client_new_with_args (test_context,
+ "keyboard",
+ "event-order2",
+ NULL);
+ meta_wait_for_client_window (test_context, "event-order2");
+ wait_for_sync_point (0);
+
+ clutter_virtual_input_device_notify_key (virtual_keyboard,
+ g_get_monotonic_time (),
+ KEY_LEFTSHIFT,
+ CLUTTER_KEY_STATE_PRESSED);
+
+ clutter_virtual_input_device_notify_key (virtual_keyboard,
+ g_get_monotonic_time (),
+ KEY_F,
+ CLUTTER_KEY_STATE_PRESSED);
+
+ wait_for_sync_point (1);
+
+ meta_wayland_test_client_finish (wayland_test_client);
+}
+
+static void
+keyboard_client_shortcut (void)
+{
+ MetaBackend *backend = meta_context_get_backend (test_context);
+ ClutterSeat *seat = meta_backend_get_default_seat (backend);
+ g_autoptr (ClutterVirtualInputDevice) virtual_keyboard = NULL;
+ MetaWaylandTestClient *wayland_test_client;
+
+ virtual_keyboard = clutter_seat_create_virtual_device (seat,
+ CLUTTER_KEYBOARD_DEVICE);
+
+ /* Test shortcut behavior with super key */
+ wayland_test_client =
+ meta_wayland_test_client_new_with_args (test_context,
+ "keyboard",
+ "client-shortcut",
+ NULL);
+ meta_wait_for_client_window (test_context, "client-shortcut");
+ wait_for_sync_point (0);
+
+ clutter_virtual_input_device_notify_key (virtual_keyboard,
+ g_get_monotonic_time (),
+ KEY_LEFTMETA,
+ CLUTTER_KEY_STATE_PRESSED);
+ wait_for_sync_point (1);
+
+ clutter_virtual_input_device_notify_key (virtual_keyboard,
+ g_get_monotonic_time (),
+ KEY_F,
+ CLUTTER_KEY_STATE_PRESSED);
+ wait_for_sync_point (2);
+
+ clutter_virtual_input_device_notify_key (virtual_keyboard,
+ g_get_monotonic_time (),
+ KEY_F,
+ CLUTTER_KEY_STATE_RELEASED);
+ wait_for_sync_point (3);
+
+ clutter_virtual_input_device_notify_key (virtual_keyboard,
+ g_get_monotonic_time (),
+ KEY_LEFTMETA,
+ CLUTTER_KEY_STATE_RELEASED);
+ wait_for_sync_point (4);
+
+ meta_wayland_test_client_finish (wayland_test_client);
+}
+
+static void
+keyboard_focus_switch (void)
+{
+ MetaBackend *backend = meta_context_get_backend (test_context);
+ ClutterSeat *seat = meta_backend_get_default_seat (backend);
+ g_autoptr (ClutterVirtualInputDevice) virtual_keyboard = NULL;
+ MetaWaylandTestClient *wayland_test_client, *wayland_test_client2;
+
+ virtual_keyboard = clutter_seat_create_virtual_device (seat,
+ CLUTTER_KEYBOARD_DEVICE);
+
+ /* Test super-tab app switching */
+ wayland_test_client =
+ meta_wayland_test_client_new_with_args (test_context,
+ "keyboard",
+ "focus-switch-dest",
+ NULL);
+ meta_wait_for_client_window (test_context, "focus-switch-dest");
+
+ wait_for_sync_point (0);
+
+ wayland_test_client2 =
+ meta_wayland_test_client_new_with_args (test_context,
+ "keyboard",
+ "focus-switch-source",
+ NULL);
+ meta_wait_for_client_window (test_context, "focus-switch-source");
+ wait_for_sync_point (100);
+
+ clutter_virtual_input_device_notify_key (virtual_keyboard,
+ g_get_monotonic_time (),
+ KEY_LEFTMETA,
+ CLUTTER_KEY_STATE_PRESSED);
+ wait_for_sync_point (101);
+
+ clutter_virtual_input_device_notify_key (virtual_keyboard,
+ g_get_monotonic_time (),
+ KEY_TAB,
+ CLUTTER_KEY_STATE_PRESSED);
+ clutter_virtual_input_device_notify_key (virtual_keyboard,
+ g_get_monotonic_time (),
+ KEY_TAB,
+ CLUTTER_KEY_STATE_RELEASED);
+ clutter_virtual_input_device_notify_key (virtual_keyboard,
+ g_get_monotonic_time (),
+ KEY_LEFTMETA,
+ CLUTTER_KEY_STATE_RELEASED);
+ wait_for_sync_point (1);
+
+ meta_wayland_test_client_finish (wayland_test_client);
+ meta_wayland_test_client_finish (wayland_test_client2);
+}
+
+static void
+on_before_tests (void)
+{
+ MetaWaylandCompositor *compositor =
+ meta_context_get_wayland_compositor (test_context);
+ MetaBackend *backend = meta_context_get_backend (test_context);
+ MetaMonitorManager *monitor_manager =
+ meta_backend_get_monitor_manager (backend);
+#ifdef MUTTER_PRIVILEGED_TEST
+ MetaKms *kms = meta_backend_native_get_kms (META_BACKEND_NATIVE (backend));
+ MetaKmsDevice *kms_device = meta_kms_get_devices (kms)->data;
+#endif
+
+ test_driver = meta_wayland_test_driver_new (compositor);
+
+#ifdef MUTTER_PRIVILEGED_TEST
+ meta_wayland_test_driver_set_property (test_driver,
+ "gpu-path",
+ meta_kms_device_get_path (kms_device));
+
+ meta_set_custom_monitor_config_full (backend,
+ "vkms-640x480.xml",
+ META_MONITORS_CONFIG_FLAG_NONE);
+#else
+ virtual_monitor = meta_create_test_monitor (test_context,
+ 640, 480, 60.0);
+#endif
+ meta_monitor_manager_reload (monitor_manager);
+}
+
+static void
+on_after_tests (void)
+{
+ g_clear_object (&test_driver);
+ g_clear_object (&virtual_monitor);
+}
+
+static void
+init_tests (void)
+{
+ g_test_add_func ("/wayland/keyboard/event-order",
+ keyboard_event_order);
+ g_test_add_func ("/wayland/keyboard/event-order-2",
+ keyboard_event_order2);
+ g_test_add_func ("/wayland/keyboard/client-shortcut",
+ keyboard_client_shortcut);
+ g_test_add_func ("/wayland/keyboard/focus-switch",
+ keyboard_focus_switch);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_autoptr (MetaContext) context = NULL;
+
+#ifdef MUTTER_PRIVILEGED_TEST
+ return 0;
+#endif
+
+ context = meta_create_test_context (META_CONTEXT_TEST_TYPE_HEADLESS,
+ META_CONTEXT_TEST_FLAG_NO_X11 |
+ META_CONTEXT_TEST_FLAG_TEST_CLIENT);
+ g_assert_true (meta_context_configure (context, &argc, &argv, NULL));
+ meta_context_test_set_background_color (META_CONTEXT_TEST (context),
+ COGL_COLOR_INIT (255, 255, 255, 255));
+
+ test_context = context;
+
+ init_tests ();
+
+ g_signal_connect (context, "before-tests",
+ G_CALLBACK (on_before_tests), NULL);
+ g_signal_connect (context, "after-tests",
+ G_CALLBACK (on_after_tests), NULL);
+
+ return meta_context_test_run_tests (META_CONTEXT_TEST (context),
+ META_TEST_RUN_FLAG_NONE);
+}
diff --git a/src/tests/wayland-test-clients/keyboard.c b/src/tests/wayland-test-clients/keyboard.c
new file mode 100644
index 0000000000..7661ce9e03
--- /dev/null
+++ b/src/tests/wayland-test-clients/keyboard.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2025 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Carlos Garnacho <carlosg@gnome.org>
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <linux/input-event-codes.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <wayland-client.h>
+
+#include "wayland-test-client-utils.h"
+
+static gboolean running;
+static struct wl_seat *wl_seat;
+static struct wl_keyboard *wl_keyboard;
+static int key_event_count;
+static int sync_point_count;
+static int enter_event_count;
+static uint32_t pressed_mods;
+static char *test_name;
+
+#define SHIFT_MASK (1 << 0)
+#define SUPER_MASK (1 << 6)
+
+static void
+sync_point (WaylandDisplay *display)
+{
+ test_driver_sync_point (display->test_driver, sync_point_count++, NULL);
+}
+
+static void
+keyboard_handle_keymap (void *data,
+ struct wl_keyboard *keyboard,
+ uint32_t format,
+ int fd,
+ uint32_t size)
+{
+}
+
+static void
+keyboard_handle_enter (void *data,
+ struct wl_keyboard *keyboard,
+ uint32_t serial,
+ struct wl_surface *surface,
+ struct wl_array *keys)
+{
+ WaylandDisplay *display = data;
+
+ enter_event_count++;
+ sync_point (display);
+}
+
+static void
+keyboard_handle_leave (void *data,
+ struct wl_keyboard *keyboard,
+ uint32_t serial,
+ struct wl_surface *surface)
+{
+ if (g_strcmp0 (test_name, "focus-switch-source") == 0)
+ running = FALSE;
+}
+
+static void
+keyboard_handle_key (void *data,
+ struct wl_keyboard *keyboard,
+ uint32_t serial,
+ uint32_t time,
+ uint32_t key,
+ uint32_t state)
+{
+ WaylandDisplay *display = data;
+
+ key_event_count++;
+
+ if (g_strcmp0 (test_name, "event-order") == 0)
+ {
+ g_assert_cmpint (key, ==, KEY_LEFTSHIFT);
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
+ g_assert_cmpint ((pressed_mods & SHIFT_MASK), ==, 0);
+ else
+ g_assert_cmpint ((pressed_mods & SHIFT_MASK), ==, SHIFT_MASK);
+ }
+ else if (g_strcmp0 (test_name, "event-order2") == 0)
+ {
+ if (key == KEY_LEFTSHIFT)
+ {
+ g_assert_cmpint (state, ==, WL_KEYBOARD_KEY_STATE_PRESSED);
+ g_assert_cmpint ((pressed_mods & SHIFT_MASK), ==, 0);
+ }
+ else if (key == KEY_F)
+ {
+ g_assert_cmpint (state, ==, WL_KEYBOARD_KEY_STATE_PRESSED);
+ g_assert_cmpint ((pressed_mods & SHIFT_MASK), ==, SHIFT_MASK);
+ sync_point (display);
+ running = FALSE;
+ }
+ }
+ else if (g_strcmp0 (test_name, "client-shortcut") == 0)
+ {
+ if (key == KEY_F)
+ {
+ g_assert_cmpint ((pressed_mods & SUPER_MASK), ==, SUPER_MASK);
+ sync_point (display);
+ }
+ else
+ {
+ g_assert_cmpint (key, ==, KEY_LEFTMETA);
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
+ g_assert_cmpint ((pressed_mods & SUPER_MASK), ==, 0);
+ else
+ g_assert_cmpint ((pressed_mods & SUPER_MASK), ==, SUPER_MASK);
+ }
+ }
+ else if (g_strcmp0 (test_name, "focus-switch-source") == 0 ||
+ g_strcmp0 (test_name, "focus-switch-dest") == 0)
+ {
+ if (key_event_count == 1 && state == WL_KEYBOARD_KEY_STATE_PRESSED)
+ {
+ g_assert_cmpint (key, ==, KEY_LEFTMETA);
+ g_assert_cmpint ((pressed_mods & SUPER_MASK), ==, 0);
+ }
+ }
+}
+
+static void
+keyboard_handle_modifiers (void *data,
+ struct wl_keyboard *keyboard,
+ uint32_t serial,
+ uint32_t mods_pressed,
+ uint32_t mods_latched,
+ uint32_t mods_locked,
+ uint32_t group)
+{
+ WaylandDisplay *display = data;
+
+ pressed_mods = mods_pressed;
+
+ if (g_strcmp0 (test_name, "event-order") == 0)
+ {
+ if (mods_pressed)
+ {
+ g_assert_cmpint (key_event_count, ==, 1);
+ sync_point (display);
+ }
+ else if (key_event_count > 0)
+ {
+ g_assert_cmpint (key_event_count, ==, 2);
+ sync_point (display);
+ running = FALSE;
+ }
+ }
+ else if (g_strcmp0 (test_name, "event-order2") == 0)
+ {
+ if (key_event_count > 0)
+ g_assert_cmpint (key_event_count, ==, 1);
+ }
+ else if (g_strcmp0 (test_name, "client-shortcut") == 0)
+ {
+ if (mods_pressed)
+ {
+ g_assert_cmpint (key_event_count, ==, 1);
+ sync_point (display);
+ }
+ else if (key_event_count > 0)
+ {
+ g_assert_cmpint (key_event_count, ==, 4);
+ sync_point (display);
+ running = FALSE;
+ }
+ }
+ else if (g_strcmp0 (test_name, "focus-switch-source") == 0)
+ {
+ if (mods_pressed && key_event_count > 0)
+ {
+ g_assert_cmpint (key_event_count, ==, 1);
+ sync_point (display);
+ }
+ }
+ else if (g_strcmp0 (test_name, "focus-switch-dest") == 0)
+ {
+ if (enter_event_count == 2 && (pressed_mods & SUPER_MASK) == 0)
+ running = FALSE;
+ }
+}
+
+static const struct wl_keyboard_listener keyboard_listener = {
+ keyboard_handle_keymap,
+ keyboard_handle_enter,
+ keyboard_handle_leave,
+ keyboard_handle_key,
+ keyboard_handle_modifiers,
+};
+
+static void
+seat_handle_capabilities (void *data,
+ struct wl_seat *seat,
+ enum wl_seat_capability caps)
+{
+ WaylandDisplay *display = data;
+
+ if (caps & WL_SEAT_CAPABILITY_KEYBOARD)
+ {
+ wl_keyboard = wl_seat_get_keyboard (seat);
+ wl_keyboard_add_listener (wl_keyboard, &keyboard_listener, display);
+ }
+}
+
+static void
+seat_handle_name (void *data,
+ struct wl_seat *seat,
+ const char *name)
+{
+}
+
+static const struct wl_seat_listener seat_listener = {
+ seat_handle_capabilities,
+ seat_handle_name,
+};
+
+static void
+handle_registry_global (void *data,
+ struct wl_registry *registry,
+ uint32_t id,
+ const char *interface,
+ uint32_t version)
+{
+ WaylandDisplay *display = data;
+
+ if (strcmp (interface, "wl_seat") == 0)
+ {
+ wl_seat = wl_registry_bind (registry, id, &wl_seat_interface, 1);
+ wl_seat_add_listener (wl_seat, &seat_listener, display);
+ }
+}
+
+static void
+handle_registry_global_remove (void *data,
+ struct wl_registry *registry,
+ uint32_t name)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+ handle_registry_global,
+ handle_registry_global_remove
+};
+
+int
+main (int argc,
+ char **argv)
+{
+ g_autoptr (WaylandDisplay) display = NULL;
+ g_autoptr (WaylandSurface) surface = NULL;
+ struct wl_registry *registry;
+
+ /* Use a sync counter in a different range */
+ if (g_strcmp0 (argv[1], "focus-switch-source") == 0)
+ sync_point_count = 100;
+
+ display = wayland_display_new (WAYLAND_DISPLAY_CAPABILITY_TEST_DRIVER);
+
+ registry = wl_display_get_registry (display->display);
+ wl_registry_add_listener (registry, ®istry_listener, display);
+ wl_display_roundtrip (display->display);
+
+ surface = wayland_surface_new (display,
+ argv[1],
+ 100, 100, 0xffffffff);
+ wl_surface_commit (surface->wl_surface);
+
+ test_name = argv[1];
+
+ running = TRUE;
+ while (running)
+ wayland_display_dispatch (display);
+
+ wl_display_roundtrip (display->display);
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/tests/wayland-test-clients/meson.build b/src/tests/wayland-test-clients/meson.build
index 0afff53ec0..b76c300def 100644
--- a/src/tests/wayland-test-clients/meson.build
+++ b/src/tests/wayland-test-clients/meson.build
@@ -145,6 +145,9 @@ wayland_test_clients = [
{
'name': 'invalid-size-limits-on-map-client',
},
+ {
+ 'name': 'keyboard',
+ },
]
test_client_executables = {}
--
2.51.0