server: Check visibility when creating/removing globals

Ghost User requested to merge (removed):dynamic-filters into main

I'm experimenting with a permission mechanism within a compositor and the handling of global filtering is a bit inconsistent -- if a client connects and binds a registry, and then the server adds a global afterwards, the client will always get a message for it even if binding that global would result in failure. The first commit should fix this, by checking for visibility before sending the global and global_remove events.

The other issue I ran into is the reverse situation, during adding and removing permissions from clients. If a client starts out as being "unprivileged" and then later gets granted access to some globals that already exist, the compositor needs to manually send that client a global event (and similarly a global_remove event if the access gets revoked). But currently this is not possible because it needs the global's name, so I added wl_global_get_name. Since this requires looping through all the client's resources, I added wl_resource_get_interface to allow for fast pointer comparisons with the built-in libwayland interfaces. This should be safe since wl_interface_equal does it anyway.

Altogether, it should allow for writing a grant_access() and revoke_access() API in the compositor, and a global filter that looks something like this:

/* permission_table is essentially a Set<Tuple<wl_global, wl_client>>,
   with simple get/set functions. Implementation omitted here. */

void global_grant_access(struct permission_table *perms,
                         struct wl_global *global,
                         struct wl_client *client) {
  if (!perms_check_access(perms, global, client)) {
    perms_set_access(perms, global, client, true);
    wl_client_for_each_resource (client, post_global_to_registry, global);

static enum wl_iterator_result post_global_to_registry(struct wl_resource *resource,
                                                       void *user_data) {
  struct wl_global *global = user_data;
  if (wl_resource_get_interface(resource) == &wl_registry_interface) {
    const struct wl_interface *interface = wl_global_get_interface(global);

/* later... */

struct wl_display *create_display(struct permission_table *perms) {
  struct wl_display *display = wl_display_create();
  wl_display_set_global_filter (display, access_filter, perms);
  return display;

static bool
access_filter (const struct wl_client *client,
               const struct wl_global *global,
               void *user_data)
  struct permission_table *perms = user_data;
  if (global_is_privileged(global))
    return perms_check_access(perms, global, client);
  return true;

If this looks good I can try to add unit tests for it before merging.

Edited by Ghost User

Merge request reports