debug-gui: pointer locking on Wayland
authorJosé Expósito <jose.exposito89@gmail.com>
Wed, 16 Jun 2021 20:53:45 +0000 (22:53 +0200)
committerJosé Expósito <jose.exposito89@gmail.com>
Tue, 3 Aug 2021 16:52:39 +0000 (16:52 +0000)
Use the pointer constraints protocol to lock the pointer on Wayland.

Signed-off-by: José Expósito <jose.exposito89@gmail.com>
.gitlab-ci.yml
.gitlab-ci/config.yml
meson.build
tools/libinput-debug-gui.c

index 46f5d18..a1707f3 100644 (file)
@@ -92,7 +92,7 @@ variables:
   UBUNTU_PACKAGES:  'git gcc g++ pkg-config meson check libudev-dev libevdev-dev doxygen graphviz python3-sphinx python3-recommonmark python3-sphinx-rtd-theme python3-pytest-xdist libwacom-dev libcairo2-dev libgtk-3-dev libglib2.0-dev libmtdev-dev'
   ARCH_PACKAGES:    'git gcc pkgconfig meson check libsystemd libevdev doxygen graphviz python-sphinx python-recommonmark python-sphinx_rtd_theme python-pytest-xdist libwacom gtk3 mtdev diffutils'
   ALPINE_PACKAGES:  'git gcc build-base pkgconfig meson check-dev eudev-dev libevdev-dev libwacom-dev cairo-dev gtk+3.0-dev mtdev-dev bash'
-  FREEBSD_PACKAGES: 'git pkgconf meson libepoll-shim libudev-devd libevdev libwacom gtk3 libmtdev bash'
+  FREEBSD_PACKAGES: 'git pkgconf meson libepoll-shim libudev-devd libevdev libwacom gtk3 libmtdev bash wayland'
   ############################ end of package lists #############################
 
   # these tags should be updated each time the list of packages is updated
index 8d42abc..1104aa7 100644 (file)
@@ -149,6 +149,7 @@ distributions:
       - gtk3
       - libmtdev
       - bash
+      - wayland
     build:
       extra_variables:
         - "MESON_ARGS: '-Dtests=false -Ddocumentation=false' # doxygen drags down too many deps"
index 9db3283..cdc21c6 100644 (file)
@@ -559,12 +559,39 @@ if get_option('debug-gui')
 
        dep_cairo = dependency('cairo')
        dep_glib = dependency('glib-2.0')
+       dep_wayland_client = dependency('wayland-client', required : false)
+       dep_wayland_protocols = dependency('wayland-protocols', required : false)
 
        debug_gui_sources = [ 'tools/libinput-debug-gui.c' ]
+
+       if dep_wayland_client.found() and dep_wayland_protocols.found()
+               wayland_scanner = find_program('wayland-scanner')
+               wlproto_dir = dep_wayland_protocols.get_pkgconfig_variable('pkgdatadir')
+
+               proto_name = 'pointer-constraints-unstable-v1'
+               input = files(join_paths(wlproto_dir, 'unstable/pointer-constraints/@0@.xml'.format(proto_name)))
+
+               wayland_headers = custom_target('@0@ client header'.format(proto_name),
+                       input: input,
+                       output: '@0@-client-protocol.h'.format(proto_name),
+                       command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
+               )
+
+               wayland_sources = custom_target('@0@ source'.format(proto_name),
+                       input: input,
+                       output: '@0@-protocol.c'.format(proto_name),
+                       command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
+               )
+
+               debug_gui_sources += [ wayland_headers, wayland_sources ]
+       endif
+
        deps_debug_gui = [
                        dep_gtk,
                        dep_cairo,
                        dep_glib,
+                       dep_wayland_client,
+                       dep_wayland_protocols,
                        ] + deps_tools
        executable('libinput-debug-gui',
                   debug_gui_sources,
index 22afa6a..460fd3c 100644 (file)
 
 #include "shared.h"
 
+#ifdef GDK_WINDOWING_WAYLAND
+       #include <wayland-client.h>
+       #include "pointer-constraints-unstable-v1-client-protocol.h"
+       #if HAVE_GTK4
+               #include <gdk/wayland/gdkwayland.h>
+       #else
+               #include <gdk/gdkwayland.h>
+       #endif
+#endif
+
 #define clip(val_, min_, max_) min((max_), max((min_), (val_)))
 
 enum touch_state {
@@ -100,6 +110,16 @@ struct window {
        /* abs position */
        struct point abs;
 
+       /* Wayland and X11 pointer locking */
+       struct {
+               bool locked;
+
+#ifdef GDK_WINDOWING_WAYLAND
+               struct zwp_pointer_constraints_v1 *wayland_pointer_constraints;
+               struct zwp_locked_pointer_v1 *wayland_locked_pointer;
+#endif
+       } lock_pointer;
+
        /* scroll bar positions */
        struct {
                double vx, vy;
@@ -181,6 +201,125 @@ struct window {
        struct libinput_device *devices[50];
 };
 
+#ifdef GDK_WINDOWING_WAYLAND
+static void
+wayland_registry_global(void *data,
+                       struct wl_registry *registry,
+                       uint32_t name,
+                       const char *interface,
+                       uint32_t version)
+{
+       struct window *w = data;
+
+       if (!g_strcmp0(interface, "zwp_pointer_constraints_v1")) {
+               w->lock_pointer.wayland_pointer_constraints =
+                       wl_registry_bind(registry,
+                                        name,
+                                        &zwp_pointer_constraints_v1_interface,
+                                        1);
+        }
+}
+
+static void
+wayland_registry_global_remove(void *data,
+                              struct wl_registry *wl_registry,
+                              uint32_t name)
+{
+
+}
+
+struct wl_registry_listener registry_listener = {
+       wayland_registry_global,
+       wayland_registry_global_remove
+};
+
+static bool
+wayland_lock_pointer(struct window *w)
+{
+       GdkDisplay *gdk_display;
+       GdkSeat *gdk_seat;
+       GdkDevice *gdk_device;
+       struct wl_display *display;
+       struct wl_registry *registry;
+       struct wl_pointer *wayland_pointer;
+       struct wl_surface *surface;
+
+       w->lock_pointer.wayland_pointer_constraints = NULL;
+
+       gdk_display = gdk_display_get_default();
+       display = gdk_wayland_display_get_wl_display(gdk_display);
+
+       gdk_seat = gdk_display_get_default_seat(gdk_display);
+       gdk_device = gdk_seat_get_pointer(gdk_seat);
+       wayland_pointer = gdk_wayland_device_get_wl_pointer(gdk_device);
+
+       registry = wl_display_get_registry(display);
+       wl_registry_add_listener(registry, &registry_listener, w);
+       wl_display_roundtrip(display);
+
+       if (!w->lock_pointer.wayland_pointer_constraints)
+               return false;
+
+#if HAVE_GTK4
+       GtkNative *window = gtk_widget_get_native(w->win);
+       GdkSurface *gdk_surface = gtk_native_get_surface(window);
+       surface = gdk_wayland_surface_get_wl_surface(gdk_surface);
+#else
+       GdkWindow *window = gtk_widget_get_window(w->win);
+       surface = gdk_wayland_window_get_wl_surface(window);
+#endif
+
+       w->lock_pointer.wayland_locked_pointer =
+               zwp_pointer_constraints_v1_lock_pointer(w->lock_pointer.wayland_pointer_constraints,
+                                                       surface,
+                                                       wayland_pointer,
+                                                       NULL,
+                                                       ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
+
+       return true;
+}
+
+static void
+wayland_unlock_pointer(struct window *w)
+{
+       w->lock_pointer.wayland_pointer_constraints = NULL;
+       zwp_locked_pointer_v1_destroy(w->lock_pointer.wayland_locked_pointer);
+}
+
+static inline bool
+backend_is_wayland(void)
+{
+       return GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default());
+}
+#endif /* GDK_WINDOWING_WAYLAND */
+
+static bool
+window_lock_pointer(struct window *w)
+{
+       w->lock_pointer.locked = false;
+
+#ifdef GDK_WINDOWING_WAYLAND
+       if (backend_is_wayland())
+               w->lock_pointer.locked = wayland_lock_pointer(w);
+#endif
+
+       return w->lock_pointer.locked;
+}
+
+static void
+window_unlock_pointer(struct window *w)
+{
+       if (!w->lock_pointer.locked)
+               return;
+
+       w->lock_pointer.locked = false;
+
+#ifdef GDK_WINDOWING_WAYLAND
+       if (backend_is_wayland())
+               wayland_unlock_pointer(w);
+#endif
+}
+
 LIBINPUT_ATTRIBUTE_PRINTF(1, 2)
 static inline void
 msg(const char *fmt, ...)
@@ -852,6 +991,8 @@ map_event_cb(GtkDrawingArea *widget, int width, int height, gpointer data)
                                       NULL);
 
        gtk_widget_set_cursor_from_name(w->win, "none");
+
+       window_lock_pointer(w);
 }
 #else
 static void
@@ -859,7 +1000,6 @@ map_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
 {
        struct window *w = data;
        GdkDisplay *display;
-       GdkSeat *seat;
        GdkWindow *window;
 
        window_place_ui_elements(widget, w);
@@ -873,16 +1013,7 @@ map_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
                              gdk_cursor_new_for_display(display,
                                                         GDK_BLANK_CURSOR));
 
-       seat = gdk_display_get_default_seat(display);
-       gdk_seat_grab(seat,
-                     window,
-                     GDK_SEAT_CAPABILITY_ALL_POINTING,
-                     FALSE, /* owner-events */
-                     NULL, /* cursor */
-                     NULL, /* triggering event */
-                     NULL, /* prepare_func */
-                     NULL /* prepare_func_data */
-                    );
+       window_lock_pointer(w);
 }
 #endif
 
@@ -1781,6 +1912,7 @@ main(int argc, char **argv)
        w.event_loop = g_main_loop_new(NULL, FALSE);
        g_main_loop_run(w.event_loop);
 
+       window_unlock_pointer(&w);
        window_cleanup(&w);
        libinput_unref(li);