Blob Blame History Raw
From 16cdb3a8cdd60da9eef12d1d10a01d4eed21a5a7 Mon Sep 17 00:00:00 2001
From: Francesco Giudici <fgiudici@redhat.com>
Date: Mon, 20 Apr 2020 10:25:03 +0200
Subject: [PATCH 8/9] wayland: fix mouse lock in server mode

We can now properly manage mouse pointer lock by using the wayland protocols.
Includes fix from Frediano Ziglio to manage switching from mouse in
server mode to client mode without releasing the pointer
(see https://gitlab.freedesktop.org/fziglio/spice-gtk/-/commit/c14b047e45)

Signed-off-by: Francesco Giudici <fgiudici@redhat.com>
Acked-by: Frediano Ziglio <fziglio@redhat.com>
(cherry picked from commit dd7015d57ca936cc81060b1e2a09d3afc1478d4a)
---
 src/spice-widget.c | 85 +++++++++++++++++++++++++++++++++++-----------
 1 file changed, 66 insertions(+), 19 deletions(-)

diff --git a/src/spice-widget.c b/src/spice-widget.c
index 7700f47..6cfc72f 100644
--- a/src/spice-widget.c
+++ b/src/spice-widget.c
@@ -34,7 +34,13 @@
 #endif
 #ifdef GDK_WINDOWING_WAYLAND
 #include <gdk/gdkwayland.h>
+#ifdef HAVE_WAYLAND_PROTOCOLS
+#include "pointer-constraints-unstable-v1-client-protocol.h"
+#include "relative-pointer-unstable-v1-client-protocol.h"
+#include "wayland-extensions.h"
 #endif
+#endif
+
 #ifdef G_OS_WIN32
 #include <windows.h>
 #include <dinput.h>
@@ -698,6 +704,11 @@ static void spice_display_init(SpiceDisplay *display)
 
     d->grabseq = spice_grab_sequence_new_from_string("Control_L+Alt_L");
     d->activeseq = g_new0(gboolean, d->grabseq->nkeysyms);
+
+#ifdef HAVE_WAYLAND_PROTOCOLS
+    if GDK_IS_WAYLAND_DISPLAY(gtk_widget_get_display(widget))
+        spice_wayland_extensions_init(widget);
+#endif
 }
 
 static void
@@ -900,7 +911,7 @@ static void ungrab_keyboard(SpiceDisplay *display)
      * We simply issue a gdk_seat_ungrab() followed immediately by another
      * gdk_seat_grab() on the pointer if the pointer grab is to be kept.
      */
-    if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
+    if (GDK_IS_WAYLAND_DISPLAY(gtk_widget_get_display(GTK_WIDGET(display)))) {
         SpiceDisplayPrivate *d = display->priv;
 
         gdk_seat_ungrab(seat);
@@ -1055,15 +1066,49 @@ error:
 }
 #endif
 
+#ifdef HAVE_WAYLAND_PROTOCOLS
+static void
+relative_pointer_handle_relative_motion(void *data,
+                                        struct zwp_relative_pointer_v1 *pointer,
+                                        uint32_t time_hi,
+                                        uint32_t time_lo,
+                                        wl_fixed_t dx_w,
+                                        wl_fixed_t dy_w,
+                                        wl_fixed_t dx_unaccel_w,
+                                        wl_fixed_t dy_unaccel_w)
+{
+    SpiceDisplay *display = SPICE_DISPLAY(data);
+    GtkWidget *widget = GTK_WIDGET(display);
+    SpiceDisplayPrivate *d = display->priv;
+
+    if (!d->inputs)
+        return;
+    if (d->disable_inputs)
+        return;
+    /* mode changed to client in the meantime */
+    if (d->mouse_mode != SPICE_MOUSE_MODE_SERVER) {
+        spice_wayland_extensions_disable_relative_pointer(widget);
+        spice_wayland_extensions_unlock_pointer(widget);
+        return;
+    }
+
+    spice_inputs_channel_motion(d->inputs,
+                                wl_fixed_to_int(dx_unaccel_w),
+                                wl_fixed_to_int(dy_unaccel_w),
+                                d->mouse_button_mask);
+}
+#endif
+
 static gboolean do_pointer_grab(SpiceDisplay *display)
 {
+    GtkWidget *widget = GTK_WIDGET(display);
     SpiceDisplayPrivate *d = display->priv;
-    GdkWindow *window = GDK_WINDOW(gtk_widget_get_window(GTK_WIDGET(display)));
+    GdkWindow *window = GDK_WINDOW(gtk_widget_get_window(widget));
     GdkGrabStatus status;
     GdkCursor *blank = spice_display_get_blank_cursor(display);
     gboolean grab_successful = FALSE;
 
-    if (!gtk_widget_get_realized(GTK_WIDGET(display)))
+    if (!gtk_widget_get_realized(widget))
         goto end;
 
 #ifdef G_OS_WIN32
@@ -1080,6 +1125,14 @@ static gboolean do_pointer_grab(SpiceDisplay *display)
                            NULL,
                            NULL,
                            NULL);
+
+#ifdef HAVE_WAYLAND_PROTOCOLS
+    if (GDK_IS_WAYLAND_DISPLAY(gtk_widget_get_display(widget))) {
+        spice_wayland_extensions_enable_relative_pointer(widget, relative_pointer_handle_relative_motion);
+        spice_wayland_extensions_lock_pointer(widget, NULL, NULL);
+    }
+#endif
+
     grab_successful = (status == GDK_GRAB_SUCCESS);
     if (!grab_successful) {
         d->mouse_grab_active = false;
@@ -1204,7 +1257,8 @@ static void ungrab_pointer(SpiceDisplay *display)
      * immediately by another gdk_seat_grab() on the keyboard if the
      * keyboard grab is to be kept.
      */
-    if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
+    if (GDK_IS_WAYLAND_DISPLAY(gtk_widget_get_display(GTK_WIDGET(display)))) {
+        GtkWidget *widget = GTK_WIDGET(display);
         SpiceDisplayPrivate *d = display->priv;
 
         gdk_seat_ungrab(seat);
@@ -1213,7 +1267,7 @@ static void ungrab_pointer(SpiceDisplay *display)
             GdkGrabStatus status;
 
             status = gdk_seat_grab(seat,
-                                   gtk_widget_get_window(GTK_WIDGET(display)),
+                                   gtk_widget_get_window(widget),
                                    GDK_SEAT_CAPABILITY_KEYBOARD,
                                    FALSE,
                                    NULL,
@@ -1224,6 +1278,12 @@ static void ungrab_pointer(SpiceDisplay *display)
                 g_warning("keyboard grab failed %u", status);
                 d->keyboard_grab_active = false;
             }
+#ifdef HAVE_WAYLAND_PROTOCOLS
+            if (d->mouse_mode == SPICE_MOUSE_MODE_SERVER) {
+                spice_wayland_extensions_disable_relative_pointer(widget);
+                spice_wayland_extensions_unlock_pointer(widget);
+            }
+#endif
         }
 
         return;
@@ -1860,21 +1920,8 @@ static gboolean leave_event(GtkWidget *widget, GdkEventCrossing *crossing G_GNUC
 
     DISPLAY_DEBUG(display, "%s", __FUNCTION__);
 
-    if (d->mouse_grab_active) {
-#ifdef GDK_WINDOWING_WAYLAND
-        /* On Wayland, there is no active pointer grab, so once the pointer
-         * has left the window, the events are routed to the window with
-         * pointer focus instead of ours, in which case we should just
-         * ungrab to avoid nasty side effects. */
-        if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
-            GdkWindow *window = gtk_widget_get_window(widget);
-
-            if (window == crossing->window)
-                try_mouse_ungrab(display);
-        }
-#endif
+    if (d->mouse_grab_active)
         return true;
-    }
 
     d->mouse_have_pointer = false;
     spice_gtk_session_set_mouse_has_pointer(d->gtk_session, false);
-- 
2.26.2