summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc-André Lureau <marcandre.lureau@gmail.com>2024-04-09 10:46:32 +0000
committerMarc-André Lureau <marcandre.lureau@gmail.com>2024-04-09 10:46:32 +0000
commitb78b74515ea0abb5026a33dbe3a1622d23d059cf (patch)
tree8012349443580ff1c5af3c8a73e69982ba0abecc
parentb906739f0e318f80002fd8e6d74ceaa7df19c75d (diff)
parente5eb2e4771b5331297967973c05a945578815ef2 (diff)
Merge branch 'fix-deference-invalid-widget-listener' into 'master'HEADmaster
wayland: use separate event queue for widgets, and code cleanup See merge request spice/spice-gtk!125
-rw-r--r--src/spice-widget.c6
-rw-r--r--src/wayland-extensions.c164
-rw-r--r--src/wayland-extensions.h1
3 files changed, 140 insertions, 31 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..dec69ce 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;
@@ -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,
@@ -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, &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 *,