[PATCH spice-gtk] Fix non-tablet non-vd-agent Wayland relative mouse motion RH#1285378

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



https://bugzilla.redhat.com/show_bug.cgi?id=1285378

Tested with Xwayland from Fedora 27 Beta (Fedora 26 Xwayland does not work).
Or Weston trunk requires this pending patchset:
        [PATCH weston 1/3] libweston: Preserve initial mouse pointer placement
        https://lists.freedesktop.org/archives/wayland-devel/2017-October/035377.html
        [PATCH weston 2/3] compositor-x11: Cleanup XCB_LEAVE_NOTIFY handling
        https://lists.freedesktop.org/archives/wayland-devel/2017-October/035378.html
        [PATCH weston 3/3] compositor-x11: Support relative mouse motion in fullscreen
        https://lists.freedesktop.org/archives/wayland-devel/2017-October/035379.html

Tested with some downloaded MS-Windows 8 beta after removing tablet virtual
device. Fedora 26 with removed virtual tablet device does not reproduce the
problem as it still runs in SPICE_MOUSE_MODE_CLIENT mode.  Fedora 26 does
reproduce it after adding <mouse mode='server'/> to its <graphics> section.

This is based on a patch by Christophe Fergeau.

Signed-off-by: Marie Stephanie Alesna <istephielicious@xxxxxxxxx>
---
 configure.ac            |  18 ++++
 src/Makefile.am         |  33 +++++++
 src/spice-widget-priv.h |  16 ++++
 src/spice-widget.c      | 236 ++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 303 insertions(+)

diff --git a/configure.ac b/configure.ac
index 8fd525b..b9086a0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -158,12 +158,30 @@ old_CFLAGS="$CFLAGS"
 CFLAGS="$CFLAGS $GTK_CFLAGS"
 LIBS="$LIBS $GTK_LIBS"
 AC_CHECK_FUNCS(gdk_event_get_scancode)
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <gdk/gdk.h>]],
+  [#ifndef GDK_WINDOWING_WAYLAND
+   uNdEfInEd;
+   #endif])],
+  [have_wayland=true],
+  [have_wayland=false])
+AM_CONDITIONAL([HAVE_WAYLAND],[$have_wayland])
 LIBS="$old_LIBS"
 CFLAGS="$old_CFLAGS"
 
 PKG_CHECK_EXISTS([gtk+-x11-$with_gtk], [PKG_CHECK_MODULES(X11, x11)])
 AC_CHECK_HEADERS([X11/XKBlib.h])
 
+if $have_wayland; then
+  AC_PATH_PROG([WAYLAND_SCANNER],[wayland-scanner],[no])
+  AS_IF([test $WAYLAND_SCANNER = "no"],
+       [AC_MSG_ERROR([Could not find wayland-scanner in your PATH, required for parsing wayland extension protocols])])
+  AC_SUBST([WAYLAND_SCANNER])
+  PKG_CHECK_EXISTS([gtk+-wayland-$with_gtk])
+  PKG_CHECK_MODULES(WAYLAND_PROTOCOLS, [wayland-protocols >= 1.7],
+                   [ac_wayland_protocols_pkgdatadir=`$PKG_CONFIG --variable=pkgdatadir wayland-protocols`])
+  AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, $ac_wayland_protocols_pkgdatadir)
+fi
+
 AC_ARG_WITH([pnp-ids-path],
   AC_HELP_STRING([--with-pnp-ids-path],
                  [Specify the path to pnp.ids @<:@default=(internal)@:>@]),
diff --git a/src/Makefile.am b/src/Makefile.am
index 4b6e46d..48f52d7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -113,6 +113,17 @@ SPICE_GTK_LIBADD_COMMON =		\
 	$(LIBM)				\
 	$(NULL)
 
+wayland_built_sources = 					\
+	relative-pointer-unstable-v1-client-protocol.h		\
+	relative-pointer-unstable-v1-protocol.c			\
+	pointer-constraints-unstable-v1-client-protocol.h	\
+	pointer-constraints-unstable-v1-protocol.c		\
+	$(NULL)
+BUILT_SOURCES += $(wayland_built_sources)
+if HAVE_WAYLAND
+CLEANFILES += $(wayland_built_sources)
+endif
+
 SPICE_GTK_SOURCES_COMMON =		\
 	spice-util.c			\
 	spice-util-priv.h		\
@@ -151,6 +162,9 @@ EXTRA_libspice_client_gtk_3_0_la_DEPENDENCIES = $(GTK_SYMBOLS_FILE)
 libspice_client_gtk_3_0_la_LDFLAGS = $(SPICE_GTK_LDFLAGS_COMMON)
 libspice_client_gtk_3_0_la_LIBADD = $(SPICE_GTK_LIBADD_COMMON)
 libspice_client_gtk_3_0_la_SOURCES = $(SPICE_GTK_SOURCES_COMMON)
+if HAVE_WAYLAND
+libspice_client_gtk_3_0_la_SOURCES += $(wayland_built_sources)
+endif
 nodist_libspice_client_gtk_3_0_la_SOURCES = $(nodist_SPICE_GTK_SOURCES_COMMON)
 
 libspice_client_gtkincludedir = $(includedir)/spice-client-gtk-3.0
@@ -593,3 +607,22 @@ update-gtk-sym-file: $(libspice_client_gtkinclude_HEADERS) $(nodist_libspice_cli
 update-symbol-files: update-map-file update-glib-sym-file update-gtk-sym-file
 
 -include $(top_srcdir)/git.mk
+
+.SECONDEXPANSION:
+
+define protostability
+$(shell echo $1 | sed 's/.*\(\<unstable\>\|\<stable\>\).*/\1/')
+endef
+
+define protoname
+$(shell echo $1 | sed 's/\([a-z\-]\+\)-[a-z]\+-v[0-9]\+/\1/')
+endef
+
+%-protocol.c : $(WAYLAND_PROTOCOLS_DATADIR)/$$(call protostability,$$*)/$$(call protoname,$$*)/$$*.xml
+	$(AM_V_GEN)$(WAYLAND_SCANNER) code $< $@
+%-client-protocol.h : $(WAYLAND_PROTOCOLS_DATADIR)/$$(call protostability,$$*)/$$(call protoname,$$*)/$$*.xml
+	$(AM_V_GEN)$(WAYLAND_SCANNER) client-header $< $@
+%-protocol.c : $(srcdir)/wayland/protocol/%.xml
+	$(AM_V_GEN)$(WAYLAND_SCANNER) code $< $@
+%-client-protocol.h : $(srcdir)/wayland/protocol/%.xml
+	$(AM_V_GEN)$(WAYLAND_SCANNER) client-header $< $@
diff --git a/src/spice-widget-priv.h b/src/spice-widget-priv.h
index 1189cbb..85d39b6 100644
--- a/src/spice-widget-priv.h
+++ b/src/spice-widget-priv.h
@@ -28,6 +28,12 @@
 #include <epoxy/egl.h>
 #endif
 
+#ifdef GDK_WINDOWING_WAYLAND
+#include <gdk/gdkwayland.h>
+#include "pointer-constraints-unstable-v1-client-protocol.h"
+#include "relative-pointer-unstable-v1-client-protocol.h"
+#endif
+
 #include "spice-widget.h"
 #include "spice-common.h"
 #include "spice-gtk-session.h"
@@ -151,6 +157,16 @@ struct _SpiceDisplayPrivate {
         SpiceGlScanout      scanout;
     } egl;
 #endif // HAVE_EGL
+#ifdef GDK_WINDOWING_WAYLAND
+    struct zwp_pointer_constraints_v1 *pointer_constraints;
+    struct zwp_relative_pointer_manager_v1 *relative_pointer_manager;
+
+    struct zwp_locked_pointer_v1 *locked_pointer;
+
+    struct zwp_relative_pointer_v1 *relative_pointer;
+
+    double dx_frac, dy_frac;
+#endif
 };
 
 int      spice_cairo_image_create                 (SpiceDisplay *display);
diff --git a/src/spice-widget.c b/src/spice-widget.c
index 5365222..b062c51 100644
--- a/src/spice-widget.c
+++ b/src/spice-widget.c
@@ -441,6 +441,18 @@ static void spice_display_dispose(GObject *obj)
         d->key_delayed_id = 0;
     }
 
+#ifdef GDK_WINDOWING_WAYLAND
+    if (d->relative_pointer_manager != NULL) {
+        zwp_relative_pointer_manager_v1_destroy(d->relative_pointer_manager);
+        d->relative_pointer_manager = NULL;
+    }
+
+    if (d->pointer_constraints != NULL) {
+        zwp_pointer_constraints_v1_destroy(d->pointer_constraints);
+        d->pointer_constraints = NULL;
+    }
+#endif
+
     G_OBJECT_CLASS(spice_display_parent_class)->dispose(obj);
 }
 
@@ -625,6 +637,76 @@ drawing_area_realize(GtkWidget *area, gpointer user_data)
 #endif
 }
 
+#ifdef GDK_WINDOWING_WAYLAND
+static void *
+gtk_wl_registry_bind(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;
+    struct wl_registry *registry;
+
+    if (!GDK_IS_WAYLAND_DISPLAY(gdk_display)) {
+        return NULL;
+    }
+
+    display = gdk_wayland_display_get_wl_display(gdk_display);
+    registry = wl_display_get_registry(display);
+
+    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,
+                        uint32_t name,
+                        const char *interface,
+                        uint32_t version)
+{
+    SpiceDisplay *display = SPICE_DISPLAY(data);
+    SpiceDisplayPrivate *priv = display->priv;
+
+    if (strcmp (interface, "zwp_relative_pointer_manager_v1") == 0) {
+        priv->relative_pointer_manager = gtk_wl_registry_bind(GTK_WIDGET(display), name,
+                                                              &zwp_relative_pointer_manager_v1_interface,
+                                                              1);
+    } else if (strcmp (interface, "zwp_pointer_constraints_v1") == 0) {
+        priv->pointer_constraints = gtk_wl_registry_bind(GTK_WIDGET(display), name,
+                                                         &zwp_pointer_constraints_v1_interface,
+                                                         1);
+    }
+}
+
+static void
+registry_handle_global_remove (void *data,
+                               struct wl_registry *registry,
+                               uint32_t name)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+  registry_handle_global,
+  registry_handle_global_remove
+};
+#endif
+
 static void spice_display_init(SpiceDisplay *display)
 {
     GtkWidget *widget = GTK_WIDGET(display);
@@ -684,6 +766,10 @@ G_GNUC_END_IGNORE_DEPRECATIONS
 
     d->grabseq = spice_grab_sequence_new_from_string("Control_L+Alt_L");
     d->activeseq = g_new0(gboolean, d->grabseq->nkeysyms);
+
+#ifdef GDK_WINDOWING_WAYLAND
+    gtk_wl_registry_add_listener(widget, &registry_listener);
+#endif
 }
 
 static GObject *
@@ -1032,6 +1118,11 @@ error:
 }
 #endif
 
+#ifdef GDK_WINDOWING_WAYLAND
+static int spice_display_wayland_lock_pointer(SpiceDisplay *display);
+static void spice_display_wayland_constrain_pointer(SpiceDisplay *display);
+#endif
+
 static gboolean do_pointer_grab(SpiceDisplay *display)
 {
     SpiceDisplayPrivate *d = display->priv;
@@ -1079,6 +1170,12 @@ static gboolean do_pointer_grab(SpiceDisplay *display)
                      blank,
                      GDK_CURRENT_TIME);
 #endif
+#ifdef GDK_WINDOWING_WAYLAND
+    if (GDK_IS_WAYLAND_DISPLAY (gdk_window_get_display(window))) {
+        spice_display_wayland_lock_pointer(display);
+        spice_display_wayland_constrain_pointer(display);
+    }
+#endif
     G_GNUC_END_IGNORE_DEPRECATIONS
     grab_successful = (status == GDK_GRAB_SUCCESS);
     if (!grab_successful) {
@@ -1179,6 +1276,11 @@ static void mouse_wrap(SpiceDisplay *display, GdkEventMotion *motion)
 
 }
 
+#ifdef GDK_WINDOWING_WAYLAND
+static int spice_display_wayland_unlock_pointer(SpiceDisplay *display);
+static void spice_display_wayland_unconstrain_pointer(SpiceDisplay *display);
+#endif
+
 static void ungrab_pointer(G_GNUC_UNUSED SpiceDisplay *display)
 {
     G_GNUC_BEGIN_IGNORE_DEPRECATIONS
@@ -1190,6 +1292,10 @@ static void ungrab_pointer(G_GNUC_UNUSED SpiceDisplay *display)
 #else
     gdk_pointer_ungrab(GDK_CURRENT_TIME);
 #endif
+#ifdef GDK_WINDOWING_WAYLAND
+    spice_display_wayland_unlock_pointer(display);
+    spice_display_wayland_unconstrain_pointer(display);
+#endif
     G_GNUC_END_IGNORE_DEPRECATIONS
 }
 
@@ -3182,3 +3288,133 @@ GdkPixbuf *spice_display_get_pixbuf(SpiceDisplay *display)
 
     return pixbuf;
 }
+
+#ifdef GDK_WINDOWING_WAYLAND
+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 = data;
+    SpiceDisplayPrivate *d = display->priv;
+    double dx_unaccel, dx;
+    double dy_unaccel, dy;
+
+    DISPLAY_DEBUG(display,
+        "dx_w: %f dy_w: %f dx_unaccel_w: %f dy_unaccel_w: %f",
+        wl_fixed_to_double(dx_w),
+        wl_fixed_to_double(dy_w),
+        wl_fixed_to_double(dx_unaccel_w),
+        wl_fixed_to_double(dy_unaccel_w));
+
+    dx_unaccel = wl_fixed_to_double(dx_unaccel_w);
+    dy_unaccel = wl_fixed_to_double(dy_unaccel_w);
+
+    /* Add left over fraction from last event. */
+    dx_unaccel += d->dx_frac;
+    dy_unaccel += d->dy_frac;
+
+    d->dx_frac = modf(dx_unaccel, &dx);
+    d->dy_frac = modf(dy_unaccel, &dy);
+
+    if (d->mouse_grab_active)
+	spice_inputs_channel_motion(d->inputs, (gint)dx, (gint)dy, 0);
+}
+
+static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
+    relative_pointer_handle_relative_motion,
+};
+
+static int spice_display_wayland_lock_pointer(SpiceDisplay *display)
+{
+    if (!display->priv->relative_pointer_manager)
+        return -1;
+    if (!display->priv->pointer_constraints)
+        return -1;
+
+    if (!display->priv->relative_pointer) {
+        GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(display));
+        struct zwp_relative_pointer_v1 *relative_pointer;
+        struct wl_pointer *pointer;
+
+        pointer = gdk_wayland_device_get_wl_pointer(spice_gdk_window_get_pointing_device(window));
+        relative_pointer =
+            zwp_relative_pointer_manager_v1_get_relative_pointer(
+                display->priv->relative_pointer_manager,
+                pointer);
+        zwp_relative_pointer_v1_add_listener(relative_pointer,
+                                             &relative_pointer_listener,
+                                             display);
+        display->priv->relative_pointer = relative_pointer;
+    }
+
+    return 0;
+}
+
+static int spice_display_wayland_unlock_pointer(SpiceDisplay *display)
+{
+    if (display->priv->relative_pointer != NULL) {
+        zwp_relative_pointer_v1_destroy(display->priv->relative_pointer);
+        display->priv->relative_pointer = NULL;
+    }
+
+    return 0;
+}
+
+static void
+locked_pointer_locked(void *data,
+                      struct zwp_locked_pointer_v1 *locked_pointer)
+{
+}
+
+static void
+locked_pointer_unlocked(void *data,
+                        struct zwp_locked_pointer_v1 *locked_pointer)
+{
+}
+
+static const struct zwp_locked_pointer_v1_listener locked_pointer_listener = {
+    locked_pointer_locked,
+    locked_pointer_unlocked,
+};
+
+static void
+spice_display_wayland_constrain_pointer(SpiceDisplay *display)
+{
+    struct zwp_locked_pointer_v1 *locked_pointer;
+    GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(display));
+    struct wl_pointer *pointer;
+
+
+    if (display->priv->locked_pointer)
+        return;
+
+    pointer = gdk_wayland_device_get_wl_pointer(spice_gdk_window_get_pointing_device(window));
+    locked_pointer =
+        zwp_pointer_constraints_v1_lock_pointer(display->priv->pointer_constraints,
+                                                gdk_wayland_window_get_wl_surface(window),
+                                                pointer,
+                                                NULL,
+                                                ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
+    zwp_locked_pointer_v1_add_listener(locked_pointer,
+                                       &locked_pointer_listener,
+                                       display);
+
+    display->priv->locked_pointer = locked_pointer;
+}
+
+static void
+spice_display_wayland_unconstrain_pointer(SpiceDisplay *display)
+{
+    if (display->priv->locked_pointer) {
+        zwp_locked_pointer_v1_destroy(display->priv->locked_pointer);
+        display->priv->locked_pointer = NULL;
+    }
+}
+
+#endif
_______________________________________________
Spice-devel mailing list
Spice-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.freedesktop.org/mailman/listinfo/spice-devel

[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]     [Monitors]