diff options
author | Keyu Tao <me@taoky.moe> | 2024-04-08 03:27:43 +0800 |
---|---|---|
committer | Keyu Tao <me@taoky.moe> | 2024-04-09 03:29:54 +0800 |
commit | 18927222cd57dbb59e9ad6788773855c499d6243 (patch) | |
tree | 810cb82b56ac7663cdd0411671bb14f9a26599c9 | |
parent | b906739f0e318f80002fd8e6d74ceaa7df19c75d (diff) |
wayland: use separate event queue thread for widgets
-rw-r--r-- | src/spice-widget.c | 6 | ||||
-rw-r--r-- | src/wayland-extensions.c | 119 | ||||
-rw-r--r-- | src/wayland-extensions.h | 1 |
3 files changed, 107 insertions, 19 deletions
diff --git a/src/spice-widget.c b/src/spice-widget.c index 6311115..8a269bc 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 64b5139..3a30360 100644 --- a/src/wayland-extensions.c +++ b/src/wayland-extensions.c @@ -24,6 +24,8 @@ #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" @@ -50,24 +52,6 @@ gtk_wl_registry_bind(GtkWidget *widget, } 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, uint32_t name, @@ -109,12 +93,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 bf34044..1bd9a65 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 *, |