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 <[email protected]> --- 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, ®istry_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 _______________________________________________ wayland-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/wayland-devel
