summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeyu Tao <me@taoky.moe>2024-04-08 03:27:43 +0800
committerKeyu Tao <me@taoky.moe>2024-04-09 03:29:54 +0800
commit18927222cd57dbb59e9ad6788773855c499d6243 (patch)
tree810cb82b56ac7663cdd0411671bb14f9a26599c9
parentb906739f0e318f80002fd8e6d74ceaa7df19c75d (diff)
wayland: use separate event queue thread for widgets
-rw-r--r--src/spice-widget.c6
-rw-r--r--src/wayland-extensions.c119
-rw-r--r--src/wayland-extensions.h1
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, &registry_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, &registry_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 *,