diff --git a/src/spice-widget.c b/src/spice-widget.c index 63111158355ba28d55920f636950cba9be7dedf9..8a269bc2955ed7138c3b3eff2bab1ea80d2254cc 100644 --- a/src/spice-widget.c +++ b/src/spice-widget.c @@ -479,6 +479,12 @@ static void spice_display_finalize(GObject *obj) DISPLAY_DEBUG(display, "Finalize spice display"); +#ifdef HAVE_WAYLAND_PROTOCOLS + GtkWidget *widget = GTK_WIDGET(display); + if GDK_IS_WAYLAND_DISPLAY(gtk_widget_get_display(widget)) + spice_wayland_extensions_finalize(widget); +#endif + g_clear_pointer(&d->grabseq, spice_grab_sequence_free); g_clear_pointer(&d->activeseq, g_free); diff --git a/src/wayland-extensions.c b/src/wayland-extensions.c index 64b5139a41467e3e8439cb6d44ecb66c80a9a787..dec69ce4ea4d0cfc44f436497686c05ab21735f6 100644 --- a/src/wayland-extensions.c +++ b/src/wayland-extensions.c @@ -24,16 +24,18 @@ #include <gtk/gtk.h> #include <gdk/gdkwayland.h> +#include <wayland-client-core.h> +#include <glib-unix.h> #include "pointer-constraints-unstable-v1-client-protocol.h" #include "relative-pointer-unstable-v1-client-protocol.h" #include "wayland-extensions.h" static void * -gtk_wl_registry_bind(GtkWidget *widget, - uint32_t name, - const struct wl_interface *interface, - uint32_t version) +registry_bind_gtk(GtkWidget *widget, + uint32_t name, + const struct wl_interface *interface, + uint32_t version) { GdkDisplay *gdk_display = gtk_widget_get_display(widget); struct wl_display *display; @@ -49,24 +51,6 @@ gtk_wl_registry_bind(GtkWidget *widget, return wl_registry_bind(registry, name, interface, version); } -static void -gtk_wl_registry_add_listener(GtkWidget *widget, const struct wl_registry_listener *listener) -{ - GdkDisplay *gdk_display = gtk_widget_get_display(widget); - struct wl_display *display; - struct wl_registry *registry; - - if (!GDK_IS_WAYLAND_DISPLAY(gdk_display)) { - return; - } - - display = gdk_wayland_display_get_wl_display(gdk_display); - registry = wl_display_get_registry(display); - wl_registry_add_listener(registry, listener, widget); - wl_display_roundtrip(display); -} - - static void registry_handle_global(void *data, struct wl_registry *registry, @@ -76,24 +60,26 @@ registry_handle_global(void *data, { GtkWidget *widget = GTK_WIDGET(data); - if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) { + if (g_strcmp0(interface, "zwp_relative_pointer_manager_v1") == 0) { struct zwp_relative_pointer_manager_v1 *relative_pointer_manager; - relative_pointer_manager = gtk_wl_registry_bind(widget, name, - &zwp_relative_pointer_manager_v1_interface, - 1); + relative_pointer_manager = registry_bind_gtk(widget, name, + &zwp_relative_pointer_manager_v1_interface, + 1); g_object_set_data_full(G_OBJECT(widget), "zwp_relative_pointer_manager_v1", relative_pointer_manager, (GDestroyNotify)zwp_relative_pointer_manager_v1_destroy); - } else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) { + g_object_set_data(G_OBJECT(widget), "zwp_relative_pointer_v1_name", GUINT_TO_POINTER(name)); + } else if (g_strcmp0(interface, "zwp_pointer_constraints_v1") == 0) { struct zwp_pointer_constraints_v1 *pointer_constraints; - pointer_constraints = gtk_wl_registry_bind(widget, name, - &zwp_pointer_constraints_v1_interface, - 1); + pointer_constraints = registry_bind_gtk(widget, name, + &zwp_pointer_constraints_v1_interface, + 1); g_object_set_data_full(G_OBJECT(widget), "zwp_pointer_constraints_v1", pointer_constraints, (GDestroyNotify)zwp_pointer_constraints_v1_destroy); + g_object_set_data(G_OBJECT(widget), "zwp_pointer_constraints_v1_name", GUINT_TO_POINTER(name)); } } @@ -102,6 +88,25 @@ registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { + GtkWidget *widget = GTK_WIDGET(data); + + struct zwp_relative_pointer_manager_v1 *relative_pointer_manager; + uint32_t relative_pointer_manager_name = 0; + relative_pointer_manager = g_object_get_data(G_OBJECT(widget), "zwp_relative_pointer_manager_v1"); + relative_pointer_manager_name = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(widget), "zwp_relative_pointer_v1_name")); + if (relative_pointer_manager && relative_pointer_manager_name == name) { + g_object_set_data_full(G_OBJECT(widget), "zwp_relative_pointer_manager_v1", NULL, NULL); + g_object_steal_data(G_OBJECT(widget), "zwp_relative_pointer_v1_name"); + } + + struct zwp_pointer_constraints_v1 *pointer_constraints; + uint32_t pointer_constraints_name = 0; + pointer_constraints = g_object_get_data(G_OBJECT(widget), "zwp_pointer_constraints_v1"); + pointer_constraints_name = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(widget), "zwp_pointer_constraints_v1_name")); + if (pointer_constraints && pointer_constraints_name == name) { + g_object_set_data_full(G_OBJECT(widget), "zwp_pointer_constraints_v1", NULL, NULL); + g_object_steal_data(G_OBJECT(widget), "zwp_pointer_constraints_v1_name"); + } } static const struct wl_registry_listener registry_listener = { @@ -109,12 +114,109 @@ static const struct wl_registry_listener registry_listener = { registry_handle_global_remove }; +static gpointer +spice_wayland_thread_run(gpointer data) +{ + GtkWidget *widget = GTK_WIDGET(data); + struct wl_display *display = gdk_wayland_display_get_wl_display(gtk_widget_get_display(widget)); + struct wl_event_queue *queue = wl_display_create_queue(display); + gint *control_fds = g_object_get_data(G_OBJECT(widget), "control_fds"); + gint control_read_fd = control_fds[0]; + GPollFD pollfd[2] = { + { wl_display_get_fd(display), G_IO_IN, 0 }, + { control_read_fd, G_IO_HUP, 0 } + }; + while (1) { + while (wl_display_prepare_read_queue(display, queue) != 0) { + wl_display_dispatch_queue_pending(display, queue); + } + wl_display_flush(display); + if (g_poll(pollfd, 2, -1) == -1) { + wl_display_cancel_read(display); + goto error; + break; + } + if (pollfd[1].revents & G_IO_HUP) { + // The write end of the pipe is closed, exit the thread + wl_display_cancel_read(display); + break; + } + if (wl_display_read_events(display) == -1) { + goto error; + break; + } + wl_display_dispatch_queue_pending(display, queue); + } + return NULL; +error: + g_warning("Failed to run event queue in spice-wayland-thread"); + return NULL; +} + void spice_wayland_extensions_init(GtkWidget *widget) { g_return_if_fail(GTK_IS_WIDGET(widget)); - gtk_wl_registry_add_listener(widget, ®istry_listener); + GdkDisplay *gdk_display = gtk_widget_get_display(widget); + if (!GDK_IS_WAYLAND_DISPLAY(gdk_display)) { + return; + } + struct wl_display *display = gdk_wayland_display_get_wl_display(gdk_display); + struct wl_event_queue *queue = wl_display_create_queue(display); + struct wl_display *display_wrapper = wl_proxy_create_wrapper(display); + wl_proxy_set_queue((struct wl_proxy *)display_wrapper, queue); + struct wl_registry *registry = wl_display_get_registry(display_wrapper); + wl_registry_add_listener(registry, ®istry_listener, widget); + + wl_display_roundtrip_queue(display, queue); + wl_display_roundtrip_queue(display, queue); + + g_object_set_data_full(G_OBJECT(widget), + "wl_display_wrapper", + display_wrapper, + (GDestroyNotify)wl_proxy_wrapper_destroy); + g_object_set_data_full(G_OBJECT(widget), + "wl_event_queue", + queue, + (GDestroyNotify)wl_event_queue_destroy); + g_object_set_data_full(G_OBJECT(widget), + "wl_registry", + registry, + (GDestroyNotify)wl_registry_destroy); + + // control_fds is used to communicate between the main thread and the created event queue thread + // When the write end of the pipe is closed, the event queue thread will exit + gint *control_fds = g_new(gint, 2); + GError *error = NULL; + if (!g_unix_open_pipe(control_fds, O_CLOEXEC, &error)) { + g_warning("Failed to create control pipe: %s", error->message); + g_error_free(error); + return; + } + g_object_set_data(G_OBJECT(widget), "control_fds", control_fds); + + GThread *thread = g_thread_new("spice-wayland-thread", + spice_wayland_thread_run, + widget); + g_object_set_data(G_OBJECT(widget), + "spice-wayland-thread", + thread); +} + +void +spice_wayland_extensions_finalize(GtkWidget *widget) +{ + g_return_if_fail(GTK_IS_WIDGET(widget)); + + gint *control_fds = g_object_get_data(G_OBJECT(widget), "control_fds"); + if (control_fds == NULL) { + return; + } + gint control_write_fd = control_fds[1]; + close(control_write_fd); + g_thread_join(g_object_get_data(G_OBJECT(widget), "spice-wayland-thread")); + g_free(control_fds); } diff --git a/src/wayland-extensions.h b/src/wayland-extensions.h index bf34044387e15676813210a5832809a5e772ecd5..1bd9a65129bf326cbe718f8ede5c76fd19aabaf4 100644 --- a/src/wayland-extensions.h +++ b/src/wayland-extensions.h @@ -20,6 +20,7 @@ #include <gtk/gtk.h> void spice_wayland_extensions_init(GtkWidget *widget); +void spice_wayland_extensions_finalize(GtkWidget *widget); int spice_wayland_extensions_enable_relative_pointer(GtkWidget *widget, void (*cb)(void *, struct zwp_relative_pointer_v1 *,