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, ®istry_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