diff --git a/src/meson.build b/src/meson.build
index a902f7df1..947f7b45a 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -595,6 +595,8 @@ if have_wayland
'wayland/meta-wayland-dma-buf.h',
'wayland/meta-wayland-dnd-surface.c',
'wayland/meta-wayland-dnd-surface.h',
+ 'wayland/meta-wayland-filter-manager.c',
+ 'wayland/meta-wayland-filter-manager.h',
'wayland/meta-wayland-gtk-shell.c',
'wayland/meta-wayland-gtk-shell.h',
'wayland/meta-wayland.h',
diff --git a/src/tests/meson.build b/src/tests/meson.build
index ebf22bf5e..79f7a7e91 100644
--- a/src/tests/meson.build
+++ b/src/tests/meson.build
@@ -408,6 +408,9 @@ if have_native_tests
'suite': 'wayland',
'sources': [
'wayland-unit-tests.c',
+ dummy_client_header,
+ dummy_server_header,
+ dummy_protocol_code,
wayland_test_utils,
],
},
diff --git a/src/tests/protocol/dummy.xml b/src/tests/protocol/dummy.xml
new file mode 100644
index 000000000..a8b486b9d
--- /dev/null
+++ b/src/tests/protocol/dummy.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/tests/protocol/meson.build b/src/tests/protocol/meson.build
index 62d3fce1e..b4a9ae2d7 100644
--- a/src/tests/protocol/meson.build
+++ b/src/tests/protocol/meson.build
@@ -30,3 +30,36 @@ test_driver_protocol_code = custom_target(
'@INPUT@', '@OUTPUT@',
]
)
+
+dummy_server_header = custom_target(
+ 'dummy server header',
+ input: 'dummy.xml',
+ output: 'dummy-server-protocol.h',
+ command: [
+ wayland_scanner,
+ 'server-header',
+ '@INPUT@', '@OUTPUT@',
+ ]
+)
+
+dummy_client_header = custom_target(
+ 'dummy client header',
+ input: 'dummy.xml',
+ output: 'dummy-client-protocol.h',
+ command: [
+ wayland_scanner,
+ 'client-header',
+ '@INPUT@', '@OUTPUT@',
+ ]
+)
+
+dummy_protocol_code = custom_target(
+ 'dummy protocol code',
+ input: 'dummy.xml',
+ output: 'dummy-protocol.c',
+ command: [
+ wayland_scanner,
+ 'private-code',
+ '@INPUT@', '@OUTPUT@',
+ ]
+)
diff --git a/src/tests/wayland-unit-tests.c b/src/tests/wayland-unit-tests.c
index 748f9d9a4..89c39e5e7 100644
--- a/src/tests/wayland-unit-tests.c
+++ b/src/tests/wayland-unit-tests.c
@@ -29,8 +29,13 @@
#include "tests/meta-test-utils.h"
#include "tests/meta-wayland-test-driver.h"
#include "tests/meta-wayland-test-utils.h"
+#include "wayland/meta-wayland-client-private.h"
+#include "wayland/meta-wayland-filter-manager.h"
#include "wayland/meta-wayland-surface.h"
+#include "dummy-client-protocol.h"
+#include "dummy-server-protocol.h"
+
static MetaContext *test_context;
static MetaWaylandTestDriver *test_driver;
static MetaVirtualMonitor *virtual_monitor;
@@ -677,6 +682,176 @@ xdg_foreign_set_parent_of (void)
meta_wayland_test_client_finish (wayland_test_client);
}
+static MetaWaylandAccess
+dummy_global_filter (const struct wl_client *client,
+ const struct wl_global *global,
+ gpointer user_data)
+{
+ MetaWaylandClient *allowed_client = META_WAYLAND_CLIENT (user_data);
+
+ if (g_object_get_data (G_OBJECT (allowed_client),
+ "test-client-destroyed"))
+ return META_WAYLAND_ACCESS_DENIED;
+ else if (meta_wayland_client_matches (allowed_client, client))
+ return META_WAYLAND_ACCESS_ALLOWED;
+ else
+ return META_WAYLAND_ACCESS_DENIED;
+}
+
+static void
+dummy_bind (struct wl_client *client,
+ void *data,
+ uint32_t version,
+ uint32_t id)
+
+{
+ g_assert_not_reached ();
+}
+
+static void
+handle_registry_global (void *user_data,
+ struct wl_registry *registry,
+ uint32_t id,
+ const char *interface,
+ uint32_t version)
+{
+ gboolean *global_seen = user_data;
+
+ if (strcmp (interface, dummy_interface.name) == 0)
+ *global_seen = TRUE;
+}
+
+static void
+handle_registry_global_remove (void *user_data,
+ struct wl_registry *registry,
+ uint32_t name)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+ handle_registry_global,
+ handle_registry_global_remove
+};
+
+static gpointer
+test_client_thread_func (gpointer user_data)
+{
+ int fd = GPOINTER_TO_INT (user_data);
+ struct wl_display *wl_display;
+ struct wl_registry *wl_registry;
+ gboolean global_seen = FALSE;
+
+ wl_display = wl_display_connect_to_fd (fd);
+ g_assert_nonnull (wl_display);
+
+ wl_registry = wl_display_get_registry (wl_display);
+ wl_registry_add_listener (wl_registry, ®istry_listener, &global_seen);
+ wl_display_roundtrip (wl_display);
+ wl_registry_destroy (wl_registry);
+
+ wl_display_disconnect (wl_display);
+
+ return GINT_TO_POINTER (global_seen);
+}
+
+static void
+on_client_destroyed (MetaWaylandClient *client,
+ gboolean *client_destroyed)
+{
+ *client_destroyed = TRUE;
+ g_object_set_data (G_OBJECT (client), "test-client-destroyed",
+ GINT_TO_POINTER (TRUE));
+}
+
+static void
+wayland_registry_filter (void)
+{
+ g_autoptr (GError) error = NULL;
+ MetaWaylandCompositor *wayland_compositor =
+ meta_context_get_wayland_compositor (test_context);
+ MetaWaylandFilterManager *filter_manager =
+ meta_wayland_compositor_get_filter_manager (wayland_compositor);
+ struct wl_display *wayland_display =
+ meta_wayland_compositor_get_wayland_display (wayland_compositor);
+ struct wl_global *dummy_global;
+ int fd;
+ g_autoptr (MetaWaylandClient) client1 = NULL;
+ g_autoptr (MetaWaylandClient) client2 = NULL;
+ g_autoptr (MetaWaylandClient) client3 = NULL;
+ g_autoptr (GThread) thread1 = NULL;
+ g_autoptr (GThread) thread2 = NULL;
+ g_autoptr (GThread) thread3 = NULL;
+ gboolean client1_destroyed = FALSE;
+ gboolean client2_destroyed = FALSE;
+ gboolean client3_destroyed = FALSE;
+ gboolean client1_saw_global;
+ gboolean client2_saw_global;
+ gboolean client3_saw_global;
+
+ client1 = meta_wayland_client_new_indirect (test_context, &error);
+ g_assert_nonnull (client1);
+ g_assert_null (error);
+ client2 = meta_wayland_client_new_indirect (test_context, &error);
+ g_assert_nonnull (client2);
+ g_assert_null (error);
+ client3 = meta_wayland_client_new_indirect (test_context, &error);
+ g_assert_nonnull (client3);
+ g_assert_null (error);
+
+ g_signal_connect (client1, "client-destroyed",
+ G_CALLBACK (on_client_destroyed), &client1_destroyed);
+ g_signal_connect (client2, "client-destroyed",
+ G_CALLBACK (on_client_destroyed), &client2_destroyed);
+ g_signal_connect (client3, "client-destroyed",
+ G_CALLBACK (on_client_destroyed), &client3_destroyed);
+
+ dummy_global = wl_global_create (wayland_display,
+ &dummy_interface,
+ 1, NULL, dummy_bind);
+ meta_wayland_filter_manager_add_global (filter_manager,
+ dummy_global,
+ dummy_global_filter,
+ client1);
+
+ fd = meta_wayland_client_setup_fd (client1, &error);
+ g_assert_cmpint (fd, >=, 0);
+ g_assert_null (error);
+ thread1 = g_thread_new ("test client thread 1",
+ test_client_thread_func,
+ GINT_TO_POINTER (fd));
+
+ fd = meta_wayland_client_setup_fd (client2, &error);
+ g_assert_cmpint (fd, >=, 0);
+ g_assert_null (error);
+ thread2 = g_thread_new ("test client thread 2",
+ test_client_thread_func,
+ GINT_TO_POINTER (fd));
+
+ while (!client1_destroyed || !client2_destroyed)
+ g_main_context_iteration (NULL, TRUE);
+
+ client1_saw_global = GPOINTER_TO_INT (g_thread_join (thread1));
+ client2_saw_global = GPOINTER_TO_INT (g_thread_join (thread2));
+
+ g_assert_true (client1_saw_global);
+ g_assert_false (client2_saw_global);
+
+ meta_wayland_filter_manager_remove_global (filter_manager, dummy_global);
+ wl_global_destroy (dummy_global);
+
+ fd = meta_wayland_client_setup_fd (client3, &error);
+ g_assert_cmpint (fd, >=, 0);
+ g_assert_null (error);
+ thread3 = g_thread_new ("test client thread 3",
+ test_client_thread_func,
+ GINT_TO_POINTER (fd));
+ while (!client3_destroyed)
+ g_main_context_iteration (NULL, TRUE);
+
+ client3_saw_global = GPOINTER_TO_INT (g_thread_join (thread3));
+ g_assert_false (client3_saw_global);
+}
+
static void
on_before_tests (void)
{
@@ -722,6 +897,8 @@ init_tests (void)
toplevel_bounds_monitors);
g_test_add_func ("/wayland/xdg-foreign/set-parent-of",
xdg_foreign_set_parent_of);
+ g_test_add_func ("/wayland/registry/filter",
+ wayland_registry_filter);
}
int
diff --git a/src/wayland/meta-wayland-filter-manager.c b/src/wayland/meta-wayland-filter-manager.c
new file mode 100644
index 000000000..9f1e3efd5
--- /dev/null
+++ b/src/wayland/meta-wayland-filter-manager.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2023 Red Hat
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "meta-wayland-filter-manager.h"
+
+#include "meta-wayland.h"
+
+struct _MetaWaylandFilterManager
+{
+ GHashTable *filters;
+};
+
+typedef struct _MetaWaylandFilter
+{
+ MetaWaylandFilterFunc func;
+ gpointer user_data;
+} MetaWaylandFilter;
+
+static bool
+global_filter_func (const struct wl_client *client,
+ const struct wl_global *global,
+ void *user_data)
+{
+ MetaWaylandFilterManager *filter_manager = user_data;
+ MetaWaylandFilter *filter;
+
+ filter = g_hash_table_lookup (filter_manager->filters, global);
+ if (!filter)
+ return true;
+
+ switch (filter->func (client, global, filter->user_data))
+ {
+ case META_WAYLAND_ACCESS_ALLOWED:
+ return true;
+ case META_WAYLAND_ACCESS_DENIED:
+ return false;
+ }
+
+ g_assert_not_reached ();
+}
+
+MetaWaylandFilterManager *
+meta_wayland_filter_manager_new (MetaWaylandCompositor *compositor)
+{
+ struct wl_display *wayland_display =
+ meta_wayland_compositor_get_wayland_display (compositor);
+ MetaWaylandFilterManager *filter_manager;
+
+ filter_manager = g_new0 (MetaWaylandFilterManager, 1);
+ filter_manager->filters = g_hash_table_new_full (NULL, NULL, NULL, g_free);
+ wl_display_set_global_filter (wayland_display,
+ global_filter_func, filter_manager);
+
+ return filter_manager;
+}
+
+void
+meta_wayland_filter_manager_free (MetaWaylandFilterManager *filter_manager)
+{
+ g_hash_table_unref (filter_manager->filters);
+ g_free (filter_manager);
+}
+
+void
+meta_wayland_filter_manager_add_global (MetaWaylandFilterManager *filter_manager,
+ struct wl_global *global,
+ MetaWaylandFilterFunc filter_func,
+ gpointer user_data)
+{
+ MetaWaylandFilter *filter;
+
+ g_return_if_fail (!g_hash_table_lookup (filter_manager->filters, global));
+
+ filter = g_new0 (MetaWaylandFilter, 1);
+ filter->func = filter_func;
+ filter->user_data = user_data;
+
+ g_hash_table_insert (filter_manager->filters, global, filter);
+}
+
+void
+meta_wayland_filter_manager_remove_global (MetaWaylandFilterManager *filter_manager,
+ struct wl_global *global)
+{
+ g_hash_table_remove (filter_manager->filters, global);
+}
diff --git a/src/wayland/meta-wayland-filter-manager.h b/src/wayland/meta-wayland-filter-manager.h
new file mode 100644
index 000000000..789dfc959
--- /dev/null
+++ b/src/wayland/meta-wayland-filter-manager.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2023 Red Hat
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_WAYLAND_FILTER_MANAGER_H
+#define META_WAYLAND_FILTER_MANAGER_H
+
+#include
+#include
+
+#include "core/util-private.h"
+#include "wayland/meta-wayland-types.h"
+
+typedef enum _MetaWaylandAccess
+{
+ META_WAYLAND_ACCESS_ALLOWED,
+ META_WAYLAND_ACCESS_DENIED,
+} MetaWaylandAccess;
+
+typedef struct _MetaWaylandFilterManager MetaWaylandFilterManager;
+
+typedef MetaWaylandAccess (* MetaWaylandFilterFunc) (const struct wl_client *client,
+ const struct wl_global *global,
+ gpointer user_data);
+
+MetaWaylandFilterManager * meta_wayland_filter_manager_new (MetaWaylandCompositor *compositor);
+
+void meta_wayland_filter_manager_free (MetaWaylandFilterManager *filter_manager);
+
+META_EXPORT_TEST
+void meta_wayland_filter_manager_add_global (MetaWaylandFilterManager *filter_manager,
+ struct wl_global *global,
+ MetaWaylandFilterFunc filter_func,
+ gpointer user_data);
+
+
+META_EXPORT_TEST
+void meta_wayland_filter_manager_remove_global (MetaWaylandFilterManager *filter_manager,
+ struct wl_global *global);
+
+#endif /* META_WAYLAND_FILTER_MANAGER_H */
diff --git a/src/wayland/meta-wayland-types.h b/src/wayland/meta-wayland-types.h
index 44e302358..81c38d9e2 100644
--- a/src/wayland/meta-wayland-types.h
+++ b/src/wayland/meta-wayland-types.h
@@ -73,4 +73,6 @@ typedef struct _MetaXWaylandManager MetaXWaylandManager;
typedef struct _MetaWaylandXdgForeign MetaWaylandXdgForeign;
+typedef struct _MetaWaylandFilterManager MetaWaylandFilterManager;
+
#endif
diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
index ebb911094..61f6d485b 100644
--- a/src/wayland/meta-wayland.c
+++ b/src/wayland/meta-wayland.c
@@ -38,6 +38,7 @@
#include "wayland/meta-wayland-data-device.h"
#include "wayland/meta-wayland-dma-buf.h"
#include "wayland/meta-wayland-egl-stream.h"
+#include "wayland/meta-wayland-filter-manager.h"
#include "wayland/meta-wayland-inhibit-shortcuts-dialog.h"
#include "wayland/meta-wayland-inhibit-shortcuts.h"
#include "wayland/meta-wayland-legacy-xdg-foreign.h"
@@ -75,6 +76,8 @@ static char *_display_name_override;
typedef struct _MetaWaylandCompositorPrivate
{
gboolean is_wayland_egl_display_bound;
+
+ MetaWaylandFilterManager *filter_manager;
} MetaWaylandCompositorPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (MetaWaylandCompositor, meta_wayland_compositor,
@@ -451,6 +454,8 @@ static void
meta_wayland_compositor_finalize (GObject *object)
{
MetaWaylandCompositor *compositor = META_WAYLAND_COMPOSITOR (object);
+ MetaWaylandCompositorPrivate *priv =
+ meta_wayland_compositor_get_instance_private (compositor);
MetaBackend *backend = meta_context_get_backend (compositor->context);
ClutterActor *stage = meta_backend_get_stage (backend);
@@ -470,6 +475,8 @@ meta_wayland_compositor_finalize (GObject *object)
g_clear_pointer (&compositor->seat, meta_wayland_seat_free);
+ g_clear_pointer (&priv->filter_manager, meta_wayland_filter_manager_free);
+
g_clear_pointer (&compositor->display_name, g_free);
g_clear_pointer (&compositor->wayland_display, wl_display_destroy);
g_clear_pointer (&compositor->source, g_source_destroy);
@@ -480,6 +487,9 @@ meta_wayland_compositor_finalize (GObject *object)
static void
meta_wayland_compositor_init (MetaWaylandCompositor *compositor)
{
+ MetaWaylandCompositorPrivate *priv =
+ meta_wayland_compositor_get_instance_private (compositor);
+
compositor->scheduled_surface_associations = g_hash_table_new (NULL, NULL);
wl_log_set_handler_server (meta_wayland_log_func);
@@ -487,6 +497,8 @@ meta_wayland_compositor_init (MetaWaylandCompositor *compositor)
compositor->wayland_display = wl_display_create ();
if (compositor->wayland_display == NULL)
g_error ("Failed to create the global wl_display");
+
+ priv->filter_manager = meta_wayland_filter_manager_new (compositor);
}
static void
@@ -861,3 +873,12 @@ meta_wayland_compositor_get_wayland_display (MetaWaylandCompositor *compositor)
{
return compositor->wayland_display;
}
+
+MetaWaylandFilterManager *
+meta_wayland_compositor_get_filter_manager (MetaWaylandCompositor *compositor)
+{
+ MetaWaylandCompositorPrivate *priv =
+ meta_wayland_compositor_get_instance_private (compositor);
+
+ return priv->filter_manager;
+}
diff --git a/src/wayland/meta-wayland.h b/src/wayland/meta-wayland.h
index cf13fa107..e0e5c9251 100644
--- a/src/wayland/meta-wayland.h
+++ b/src/wayland/meta-wayland.h
@@ -102,8 +102,12 @@ MetaXWaylandManager * meta_wayland_compositor_get_xwayland_manager (MetaWaylan
META_EXPORT_TEST
MetaContext * meta_wayland_compositor_get_context (MetaWaylandCompositor *compositor);
+META_EXPORT_TEST
struct wl_display * meta_wayland_compositor_get_wayland_display (MetaWaylandCompositor *compositor);
gboolean meta_wayland_compositor_is_grabbed (MetaWaylandCompositor *compositor);
+META_EXPORT_TEST
+MetaWaylandFilterManager * meta_wayland_compositor_get_filter_manager (MetaWaylandCompositor *compositor);
+
#endif
diff --git a/src/wayland/meta-xwayland-grab-keyboard.c b/src/wayland/meta-xwayland-grab-keyboard.c
index 69213b65a..25322517b 100644
--- a/src/wayland/meta-xwayland-grab-keyboard.c
+++ b/src/wayland/meta-xwayland-grab-keyboard.c
@@ -26,6 +26,7 @@
#include "meta/meta-backend.h"
#include "backends/meta-settings-private.h"
+#include "wayland/meta-wayland-filter-manager.h"
#include "wayland/meta-wayland-private.h"
#include "wayland/meta-wayland-versions.h"
#include "wayland/meta-wayland-seat.h"
@@ -339,12 +340,37 @@ bind_keyboard_grab (struct wl_client *client,
NULL, NULL);
}
+static MetaWaylandAccess
+xwayland_grab_keyboard_filter (const struct wl_client *client,
+ const struct wl_global *global,
+ gpointer user_data)
+{
+ MetaWaylandCompositor *compositor = user_data;
+ MetaXWaylandManager *xwayland_manager = &compositor->xwayland_manager;
+
+ if (client == xwayland_manager->client)
+ return META_WAYLAND_ACCESS_ALLOWED;
+ else
+ return META_WAYLAND_ACCESS_DENIED;
+}
+
gboolean
meta_xwayland_grab_keyboard_init (MetaWaylandCompositor *compositor)
{
- return (wl_global_create (compositor->wayland_display,
- &zwp_xwayland_keyboard_grab_manager_v1_interface,
- META_ZWP_XWAYLAND_KEYBOARD_GRAB_V1_VERSION,
- NULL,
- bind_keyboard_grab) != NULL);
+ struct wl_global *global;
+ MetaWaylandFilterManager *filter_manager;
+
+ global = wl_global_create (compositor->wayland_display,
+ &zwp_xwayland_keyboard_grab_manager_v1_interface,
+ META_ZWP_XWAYLAND_KEYBOARD_GRAB_V1_VERSION,
+ NULL,
+ bind_keyboard_grab);
+
+ filter_manager = meta_wayland_compositor_get_filter_manager (compositor);
+ meta_wayland_filter_manager_add_global (filter_manager,
+ global,
+ xwayland_grab_keyboard_filter,
+ compositor);
+
+ return TRUE;
}
diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c
index b8c858483..8a50b93f3 100644
--- a/src/wayland/meta-xwayland.c
+++ b/src/wayland/meta-xwayland.c
@@ -73,11 +73,6 @@ static void meta_xwayland_stop_xserver (MetaXWaylandManager *manager);
static void
meta_xwayland_set_primary_output (MetaX11Display *x11_display);
-static bool
-meta_xwayland_global_filter (const struct wl_client *client,
- const struct wl_global *global,
- void *data);
-
static MetaMonitorManager *
monitor_manager_from_x11_display (MetaX11Display *x11_display)
{
@@ -1127,9 +1122,7 @@ meta_xwayland_init (MetaXWaylandManager *manager,
/* Xwayland specific protocol, needs to be filtered out for all other clients */
meta_xwayland_grab_keyboard_init (compositor);
- wl_display_set_global_filter (compositor->wayland_display,
- meta_xwayland_global_filter,
- compositor);
+
return TRUE;
}
@@ -1299,23 +1292,6 @@ meta_xwayland_manager_handle_xevent (MetaXWaylandManager *manager,
return FALSE;
}
-static bool
-meta_xwayland_global_filter (const struct wl_client *client,
- const struct wl_global *global,
- void *data)
-{
- MetaWaylandCompositor *compositor = (MetaWaylandCompositor *) data;
- MetaXWaylandManager *xwayland_manager = &compositor->xwayland_manager;
-
- /* Keyboard grabbing protocol is for Xwayland only */
- if (client != xwayland_manager->client)
- return (wl_global_get_interface (global) !=
- &zwp_xwayland_keyboard_grab_manager_v1_interface);
-
- /* All others are visible to all clients */
- return true;
-}
-
gboolean
meta_xwayland_signal (MetaXWaylandManager *manager,
int signum,