wayland: Add global object depedency tracking

Some features need certain globals to initialize. In order to deal with
these dependencies, add a way to postpone closures that depend on a
certain set of globals, that later will be invoked when required
globals are all received.

https://bugzilla.gnome.org/show_bug.cgi?id=719819
This commit is contained in:
Jonas Ådahl 2015-05-18 14:55:08 +08:00
parent 181e4399f6
commit 4e9be39518
2 changed files with 149 additions and 5 deletions

View File

@ -171,6 +171,110 @@ static const struct xdg_shell_listener xdg_shell_listener = {
xdg_shell_ping,
};
static gboolean
is_known_global (gpointer key, gpointer value, gpointer user_data)
{
const char *required_global = user_data;
const char *known_global = value;
return g_strcmp0 (required_global, known_global) == 0;
}
static gboolean
has_required_globals (GdkWaylandDisplay *display_wayland,
const char *required_globals[])
{
int i = 0;
while (required_globals[i])
{
if (g_hash_table_find (display_wayland->known_globals,
is_known_global,
(gpointer)required_globals[i]) == NULL)
return FALSE;
i++;
}
return TRUE;
}
typedef struct _OnHasGlobalsClosure OnHasGlobalsClosure;
typedef void (*HasGlobalsCallback) (GdkWaylandDisplay *display_wayland,
OnHasGlobalsClosure *closure);
struct _OnHasGlobalsClosure
{
HasGlobalsCallback handler;
const char **required_globals;
};
static void
process_on_globals_closures (GdkWaylandDisplay *display_wayland)
{
GList *iter;
iter = display_wayland->on_has_globals_closures;
while (iter != NULL)
{
GList *next = iter->next;
OnHasGlobalsClosure *closure = iter->data;
if (has_required_globals (display_wayland,
closure->required_globals))
{
closure->handler (display_wayland, closure);
g_free (closure);
display_wayland->on_has_globals_closures =
g_list_delete_link (display_wayland->on_has_globals_closures, iter);
}
iter = next;
}
}
typedef struct
{
OnHasGlobalsClosure base;
uint32_t id;
uint32_t version;
} SeatAddedClosure;
static void
_gdk_wayland_display_add_seat (GdkWaylandDisplay *display_wayland,
uint32_t id,
uint32_t version)
{
GdkDisplay *gdk_display = GDK_DISPLAY_OBJECT (display_wayland);
struct wl_seat *seat;
seat = wl_registry_bind (display_wayland->wl_registry,
id, &wl_seat_interface, MIN (version, 4));
_gdk_wayland_device_manager_add_seat (gdk_display->device_manager,
id, seat);
_gdk_wayland_display_async_roundtrip (display_wayland);
}
static void
seat_added_closure_run (GdkWaylandDisplay *display_wayland,
OnHasGlobalsClosure *closure)
{
SeatAddedClosure *seat_added_closure = (SeatAddedClosure*)closure;
_gdk_wayland_display_add_seat (display_wayland,
seat_added_closure->id,
seat_added_closure->version);
}
static void
postpone_on_globals_closure (GdkWaylandDisplay *display_wayland,
OnHasGlobalsClosure *closure)
{
display_wayland->on_has_globals_closures =
g_list_append (display_wayland->on_has_globals_closures, closure);
}
static void
gdk_registry_handle_global (void *data,
struct wl_registry *registry,
@ -179,9 +283,8 @@ gdk_registry_handle_global (void *data,
uint32_t version)
{
GdkWaylandDisplay *display_wayland = data;
GdkDisplay *gdk_display = GDK_DISPLAY_OBJECT (data);
struct wl_seat *seat;
struct wl_output *output;
gboolean handled = TRUE;
GDK_NOTE (MISC,
g_message ("add global %u, interface %s, version %u", id, interface, version));
@ -226,9 +329,26 @@ gdk_registry_handle_global (void *data,
}
else if (strcmp (interface, "wl_seat") == 0)
{
seat = wl_registry_bind (display_wayland->wl_registry, id, &wl_seat_interface, MIN (version, 4));
_gdk_wayland_device_manager_add_seat (gdk_display->device_manager, id, seat);
_gdk_wayland_display_async_roundtrip (display_wayland);
static const char *required_device_manager_globals[] = {
"wl_compositor",
"wl_data_device_manager",
NULL
};
if (has_required_globals (display_wayland,
required_device_manager_globals))
_gdk_wayland_display_add_seat (display_wayland, id, version);
else
{
SeatAddedClosure *closure;
closure = g_new0 (SeatAddedClosure, 1);
closure->base.handler = seat_added_closure_run;
closure->base.required_globals = required_device_manager_globals;
closure->id = id;
closure->version = version;
postpone_on_globals_closure (display_wayland, &closure->base);
}
}
else if (strcmp (interface, "wl_data_device_manager") == 0)
{
@ -240,6 +360,14 @@ gdk_registry_handle_global (void *data,
display_wayland->subcompositor =
wl_registry_bind (display_wayland->wl_registry, id, &wl_subcompositor_interface, 1);
}
else
handled = FALSE;
if (handled)
g_hash_table_insert (display_wayland->known_globals,
GUINT_TO_POINTER (id), g_strdup (interface));
process_on_globals_closures (display_wayland);
}
static void
@ -254,6 +382,8 @@ gdk_registry_handle_global_remove (void *data,
_gdk_wayland_device_manager_remove_seat (display->device_manager, id);
_gdk_wayland_screen_remove_output (display_wayland->screen, id);
g_hash_table_remove (display_wayland->known_globals, GUINT_TO_POINTER (id));
/* FIXME: the object needs to be destroyed here, we're leaking */
}
@ -297,6 +427,10 @@ _gdk_wayland_display_open (const gchar *display_name)
display_wayland->wl_display = wl_display;
display_wayland->screen = _gdk_wayland_screen_new (display);
display_wayland->event_source = _gdk_wayland_display_event_source_new (display);
display_wayland->known_globals =
g_hash_table_new_full (NULL, NULL, NULL, g_free);
_gdk_wayland_display_init_cursors (display_wayland);
display_wayland->wl_registry = wl_display_get_registry (display_wayland->wl_display);
@ -350,6 +484,9 @@ gdk_wayland_display_dispose (GObject *object)
g_list_foreach (display_wayland->async_roundtrips,
(GFunc) wl_callback_destroy, NULL);
g_hash_table_destroy (display_wayland->known_globals);
g_list_free_full (display_wayland->on_has_globals_closures, g_free);
G_OBJECT_CLASS (gdk_wayland_display_parent_class)->dispose (object);
}

View File

@ -74,6 +74,13 @@ struct _GdkWaylandDisplay
GList *async_roundtrips;
/* Keep track of the ID's of the known globals and their corresponding
* names. This way we can check whether an interface is known, and
* remove globals given its ID. This table is not expected to be very
* large, meaning the lookup by interface name time is insignificant. */
GHashTable *known_globals;
GList *on_has_globals_closures;
struct wl_cursor_theme *scaled_cursor_themes[GDK_WAYLAND_THEME_SCALES_COUNT];
gchar *cursor_theme_name;
int cursor_theme_size;