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

Jonas Ã…dahl jadahl at gmail.com
Wed Aug 3 03:18:24 UTC 2016


On Mon, Aug 01, 2016 at 03:22:59PM +0200, Olivier Fourdan wrote:
> 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.

The API additions to wl_global should be their own patches IMHO.

> 
>  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.

What I meant was that one would pass a user_data with set_filter() but
using the user_data that is already set on the global is fine too I
guess.  If so, we could probably just not pass a user_data, and add a
wl_global_get_user_data() instead.

> 
>  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;

nit: Could be "global_filter". We are already in wl_display here.

>  };
>  
>  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);

nit: if you put () around the a || b expression you can rely on your
editor for indentation.

> +}
> +
>  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

"Set 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

"Set a filter for"

> + * 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.

I'd suggest:

	The set filter will be used during wl_global advertisment to
	determine whether a global object should be advertised to a
	given client, and during wl_global binding to determine whether
	a given client should be allowed to bind to a global.

	Clients that try to bind to a global that was filtered out will
	have an error raised.

	Setting the filter NULL will result in all globals being
	advertised to all clients. The default is no filter.


Jonas

> + *
> + * \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