[PATCH v2] wayland-server: Add API to control globals visibility

Olivier Fourdan ofourdan at redhat.com
Mon Aug 1 13:22:59 UTC 2016


Add a new API to let compositor decide whether or not a wl_global
should be advertised to the clients via wl_registry_bind() or
display_get_registry()

By using its own filter, the compositor can decide which wl_global would
be listed to clients.

Compositors can use this mechanism to hide their own private interfaces
that regular clients should not use.

Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
---
 v2: Follow-up on Jonas' comments on v1:
     Add global's data as user data in callback filter function
     Pass wl_global instead of wl_interface in callback filter function
     Add wl_global_get_interface() to retrieve the wl_interface from the
     given wl_global
     Post an error if a client tries to bind a global which is filtered
     out.

 Note: Hey Jonas, I am not quite sure what you meant by "Probably want a
       void *user_data here", did you mean to pass the wl_global's data
       or add a new void * alltogether? I took it you meant the former.

 src/wayland-server-core.h | 12 ++++++++++
 src/wayland-server.c      | 54 +++++++++++++++++++++++++++++++++++++++----
 tests/display-test.c      | 58 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 119 insertions(+), 5 deletions(-)

diff --git a/src/wayland-server-core.h b/src/wayland-server-core.h
index ad1292f..6ccceb8 100644
--- a/src/wayland-server-core.h
+++ b/src/wayland-server-core.h
@@ -28,6 +28,7 @@
 
 #include <sys/types.h>
 #include <stdint.h>
+#include <stdbool.h>
 #include "wayland-util.h"
 #include "wayland-version.h"
 
@@ -164,6 +165,17 @@ wl_global_create(struct wl_display *display,
 void
 wl_global_destroy(struct wl_global *global);
 
+typedef bool (*wl_display_filter_global_func_t)(const struct wl_client *client,
+						const struct wl_global *global,
+						void *data);
+
+void
+wl_display_set_filter_global(struct wl_display *display,
+			     wl_display_filter_global_func_t filter);
+
+const struct wl_interface *
+wl_global_get_interface(const struct wl_global *global);
+
 struct wl_client *
 wl_client_create(struct wl_display *display, int fd);
 
diff --git a/src/wayland-server.c b/src/wayland-server.c
index 19aa2e8..032e5e0 100644
--- a/src/wayland-server.c
+++ b/src/wayland-server.c
@@ -98,6 +98,7 @@ struct wl_display {
 	struct wl_signal destroy_signal;
 
 	struct wl_array additional_shm_formats;
+	wl_display_filter_global_func_t wl_display_global_filter;
 };
 
 struct wl_global {
@@ -714,6 +715,16 @@ wl_client_destroy(struct wl_client *client)
 	free(client);
 }
 
+static bool
+filter_global(const struct wl_client *client,
+	      const struct wl_global *global)
+{
+	struct wl_display *display = client->display;
+
+	return display->wl_display_global_filter == NULL ||
+	       display->wl_display_global_filter(client, global, global->data);
+}
+
 static void
 registry_bind(struct wl_client *client,
 	      struct wl_resource *resource, uint32_t name,
@@ -740,6 +751,10 @@ registry_bind(struct wl_client *client,
 				       WL_DISPLAY_ERROR_INVALID_OBJECT,
 				       "invalid version for global %s (%d): have %d, wanted %d",
 				       interface, name, global->version, version);
+	else if (!filter_global(client, global))
+		wl_resource_post_error(resource,
+				       WL_DISPLAY_ERROR_INVALID_OBJECT,
+				       "invalid global %s (%d)", interface, name);
 	else
 		global->bind(client, global->data, version, id);
 }
@@ -795,11 +810,12 @@ display_get_registry(struct wl_client *client,
 		       &registry_resource->link);
 
 	wl_list_for_each(global, &display->global_list, link)
-		wl_resource_post_event(registry_resource,
-				       WL_REGISTRY_GLOBAL,
-				       global->name,
-				       global->interface->name,
-				       global->version);
+		if (filter_global(client, global))
+			wl_resource_post_event(registry_resource,
+					       WL_REGISTRY_GLOBAL,
+					       global->name,
+					       global->interface->name,
+					       global->version);
 }
 
 static const struct wl_display_interface display_interface = {
@@ -867,6 +883,7 @@ wl_display_create(void)
 
 	display->id = 1;
 	display->serial = 0;
+	display->wl_display_global_filter = NULL;
 
 	wl_array_init(&display->additional_shm_formats);
 
@@ -940,6 +957,27 @@ wl_display_destroy(struct wl_display *display)
 	free(display);
 }
 
+/** Set-up a filter function for global objects
+ *
+ * \param display The Wayland display object.
+ * \param filter  The global filter funtion.
+ * \return None.
+ *
+ * Setup a filter for the wl_display to advertise or hide global objects
+ * to clients.
+ * Using such a filter that will be called from wl_registry_bind()
+ * handler and display_get_registry(), the compositor can control which
+ * wl_global objects are advertised to any given client.
+ *
+ * \memberof wl_display
+ */
+WL_EXPORT void
+wl_display_set_filter_global(struct wl_display *display,
+			     wl_display_filter_global_func_t filter)
+{
+	display->wl_display_global_filter = filter;
+}
+
 WL_EXPORT struct wl_global *
 wl_global_create(struct wl_display *display,
 		 const struct wl_interface *interface, int version,
@@ -997,6 +1035,12 @@ wl_global_destroy(struct wl_global *global)
 	free(global);
 }
 
+WL_EXPORT const struct wl_interface *
+wl_global_get_interface(const struct wl_global *global)
+{
+	return global->interface;
+}
+
 /** Get the current serial number
  *
  * \param display The display object
diff --git a/tests/display-test.c b/tests/display-test.c
index 17956db..5230913 100644
--- a/tests/display-test.c
+++ b/tests/display-test.c
@@ -125,6 +125,7 @@ struct handler_info {
 	struct wl_seat *seat;
 	uint32_t bind_version;
 	bool use_unversioned;
+	bool has_data_offer;
 };
 
 static void
@@ -145,6 +146,8 @@ registry_handle_globals(void *data, struct wl_registry *registry,
 			hi->seat = wl_registry_bind(registry, id,
 						    &wl_seat_interface, ver);
 		assert(hi->seat);
+	} else if (strcmp(intf, "wl_data_offer") == 0) {
+		hi->has_data_offer = true;
 	}
 }
 
@@ -926,3 +929,58 @@ TEST(error_on_destroyed_object)
 	display_resume(d);
 	display_destroy(d);
 }
+
+static bool
+display_global_filter(const struct wl_client *client,
+		      const struct wl_global *global,
+		      void *data)
+{
+	/* Hide the wl_data_offer interface */
+	if (wl_global_get_interface(global) == &wl_data_offer_interface)
+		return false;
+
+	/* Show all the others */
+	return true;
+}
+
+static void
+bind_data_offer(struct wl_client *client, void *data,
+		uint32_t vers, uint32_t id)
+{
+	struct display *d = data;
+	struct client_info *ci;
+	struct wl_resource *res;
+
+	ci = find_client_info(d, client);
+	assert(ci);
+
+	res = wl_resource_create(client, &wl_data_offer_interface, vers, id);
+	assert(res);
+}
+
+TEST(filter)
+{
+	struct handler_info hi;
+	struct display *d;
+	struct wl_global *g1, *g2;
+
+	d = display_create();
+	wl_display_set_filter_global(d->wl_display, display_global_filter);
+
+	g1 = wl_global_create(d->wl_display, &wl_seat_interface,
+			      1, d, bind_seat);
+	g2 = wl_global_create(d->wl_display, &wl_data_offer_interface,
+			      1, d, bind_data_offer);
+
+	hi.has_data_offer = false;
+	client_create(d, seat_version, &hi);
+	assert(hi.seat != NULL);
+	assert(hi.has_data_offer != true);
+
+	display_run(d);
+
+	wl_global_destroy(g1);
+	wl_global_destroy(g2);
+
+	display_destroy(d);
+}
-- 
2.7.4



More information about the wayland-devel mailing list