[Spice-commits] 4 commits - meson.build meson_options.txt src/meson.build src/spice-widget.c src/spice-widget-priv.h src/wayland-extensions.c src/wayland-extensions.h

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Mon May 11 08:56:34 UTC 2020


 meson.build              |   20 ++++
 meson_options.txt        |    4 
 src/meson.build          |   67 +++++++++++++
 src/spice-widget-priv.h  |    1 
 src/spice-widget.c       |  107 +++++++++++++++++----
 src/wayland-extensions.c |  235 +++++++++++++++++++++++++++++++++++++++++++++++
 src/wayland-extensions.h |   32 ++++++
 7 files changed, 444 insertions(+), 22 deletions(-)

New commits:
commit dd7015d57ca936cc81060b1e2a09d3afc1478d4a
Author: Francesco Giudici <fgiudici at redhat.com>
Date:   Mon Apr 20 10:25:03 2020 +0200

    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 at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/src/spice-widget.c b/src/spice-widget.c
index 1bf6581..5cef966 100644
--- a/src/spice-widget.c
+++ b/src/spice-widget.c
@@ -33,7 +33,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>
@@ -697,6 +703,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
@@ -899,7 +910,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);
@@ -1054,15 +1065,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
@@ -1079,6 +1124,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;
@@ -1203,7 +1256,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);
@@ -1212,7 +1266,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,
@@ -1223,6 +1277,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;
@@ -1859,21 +1919,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);
commit 0fe70950524c28d383f34876c66107117581c72f
Author: Francesco Giudici <fgiudici at redhat.com>
Date:   Mon Apr 20 14:37:57 2020 +0200

    spice-gtk: save mouse button state on mouse click
    
    This will be used later to couple it with relative mouse movement under
    Wayland: we just get the mouse movement from the Wayland protocols
    callback.
    
    Signed-off-by: Francesco Giudici <fgiudici at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/src/spice-widget-priv.h b/src/spice-widget-priv.h
index 70c24af..99b3da6 100644
--- a/src/spice-widget-priv.h
+++ b/src/spice-widget-priv.h
@@ -95,6 +95,7 @@ struct _SpiceDisplayPrivate {
     SpiceSmartcardChannel   *smartcard;
 
     enum SpiceMouseMode     mouse_mode;
+    int                     mouse_button_mask;
     int                     mouse_grab_active;
     bool                    mouse_have_pointer;
     GdkCursor               *mouse_cursor;
diff --git a/src/spice-widget.c b/src/spice-widget.c
index 2947e8a..1bf6581 100644
--- a/src/spice-widget.c
+++ b/src/spice-widget.c
@@ -1953,7 +1953,21 @@ static int button_gdk_to_spice(guint gdk)
     };
 
     if (gdk < SPICE_N_ELEMENTS(map)) {
-        return map [ gdk ];
+        return map[gdk];
+    }
+    return 0;
+}
+
+static int button_gdk_to_spice_mask(guint gdk)
+{
+    static const int map[] = {
+        [1] = SPICE_MOUSE_BUTTON_MASK_LEFT,
+        [2] = SPICE_MOUSE_BUTTON_MASK_MIDDLE,
+        [3] = SPICE_MOUSE_BUTTON_MASK_RIGHT,
+    };
+
+    if (gdk < SPICE_N_ELEMENTS(map)) {
+        return map[gdk];
     }
     return 0;
 }
@@ -2159,11 +2173,17 @@ static gboolean button_event(GtkWidget *widget, GdkEventButton *button)
         spice_inputs_channel_button_press(d->inputs,
                                           button_gdk_to_spice(button->button),
                                           button_mask_gdk_to_spice(button->state));
+        /* Save the mouse button mask to couple it with Wayland movement */
+        d->mouse_button_mask = button_mask_gdk_to_spice(button->state);
+        d->mouse_button_mask |= button_gdk_to_spice_mask(button->button);
         break;
     case GDK_BUTTON_RELEASE:
         spice_inputs_channel_button_release(d->inputs,
                                             button_gdk_to_spice(button->button),
                                             button_mask_gdk_to_spice(button->state));
+        /* Save the mouse button mask to couple it with Wayland movement */
+        d->mouse_button_mask = button_mask_gdk_to_spice(button->state);
+        d->mouse_button_mask ^= button_gdk_to_spice_mask(button->button);
         break;
     default:
         break;
commit 59d5c92c74da0f452b9104191bb752c33d26ec77
Author: Francesco Giudici <fgiudici at redhat.com>
Date:   Tue Mar 28 15:07:16 2017 +0200

    wayland: add wayland-extensions functions
    
    add utilities to lock the pointer to a window and to get relative mouse
    movement in Wayland. This is made possible thanks to the wayland
    protocols introduced in the previous commit.
    
    Code freely taken and adapted from Christophe Fergeau branch:
    https://gitlab.freedesktop.org/teuf/spice-gtk/-/tree/wayland
    
    Signed-off-by: Francesco Giudici <fgiudici at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/src/meson.build b/src/meson.build
index 0f507d5..6efe411 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -369,6 +369,11 @@ if spice_gtk_has_gtk
         message('@0@ @1@: @2@ -> @3@'.format(prog_scanner, output_type, xml_path, output_file))
       endforeach
     endforeach
+
+    spice_client_gtk_sources += [
+      'wayland-extensions.c',
+      'wayland-extensions.h',
+    ]
   endif
 
   #
diff --git a/src/wayland-extensions.c b/src/wayland-extensions.c
new file mode 100644
index 0000000..64b5139
--- /dev/null
+++ b/src/wayland-extensions.c
@@ -0,0 +1,235 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2017 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <stdint.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <gdk/gdkwayland.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)
+{
+    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)
+{
+    GtkWidget *widget = GTK_WIDGET(data);
+
+    if (strcmp(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);
+        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) {
+        struct zwp_pointer_constraints_v1 *pointer_constraints;
+        pointer_constraints = gtk_wl_registry_bind(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);
+    }
+}
+
+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
+};
+
+void
+spice_wayland_extensions_init(GtkWidget *widget)
+{
+    g_return_if_fail(GTK_IS_WIDGET(widget));
+
+    gtk_wl_registry_add_listener(widget, &registry_listener);
+}
+
+
+static GdkDevice *
+spice_gdk_window_get_pointing_device(GdkWindow *window)
+{
+    GdkDisplay *gdk_display = gdk_window_get_display(window);
+
+    return gdk_seat_get_pointer(gdk_display_get_default_seat(gdk_display));
+}
+
+static struct zwp_relative_pointer_v1_listener relative_pointer_listener;
+
+// NOTE this API works only on a single widget per application
+int
+spice_wayland_extensions_enable_relative_pointer(GtkWidget *widget,
+                                                 void (*cb)(void *,
+                                                            struct zwp_relative_pointer_v1 *,
+                                                            uint32_t, uint32_t,
+                                                            wl_fixed_t, wl_fixed_t, wl_fixed_t, wl_fixed_t))
+{
+    struct zwp_relative_pointer_v1 *relative_pointer;
+
+    g_return_val_if_fail(GTK_IS_WIDGET(widget), -1);
+
+    relative_pointer = g_object_get_data(G_OBJECT(widget), "zwp_relative_pointer_v1");
+
+    if (relative_pointer == NULL) {
+        struct zwp_relative_pointer_manager_v1 *relative_pointer_manager;
+        GdkWindow *window = gtk_widget_get_window(widget);
+        struct wl_pointer *pointer;
+
+        relative_pointer_manager = g_object_get_data(G_OBJECT(widget), "zwp_relative_pointer_manager_v1");
+        if (relative_pointer_manager == NULL)
+            return -1;
+
+        pointer = gdk_wayland_device_get_wl_pointer(spice_gdk_window_get_pointing_device(window));
+        relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(relative_pointer_manager,
+                                                                                pointer);
+
+        relative_pointer_listener.relative_motion = cb;
+        zwp_relative_pointer_v1_add_listener(relative_pointer,
+                                             &relative_pointer_listener,
+                                             widget);
+
+        g_object_set_data_full(G_OBJECT(widget),
+                               "zwp_relative_pointer_v1",
+                               relative_pointer,
+                               (GDestroyNotify)zwp_relative_pointer_v1_destroy);
+    }
+
+    return 0;
+}
+
+int spice_wayland_extensions_disable_relative_pointer(GtkWidget *widget)
+{
+    g_return_val_if_fail(GTK_IS_WIDGET(widget), -1);
+
+    /* This will call zwp_relative_pointer_v1_destroy() and stop relative
+     * movement */
+    g_object_set_data(G_OBJECT(widget), "zwp_relative_pointer_v1", NULL);
+
+    return 0;
+}
+
+static struct zwp_locked_pointer_v1_listener locked_pointer_listener;
+
+// NOTE this API works only on a single widget per application
+int
+spice_wayland_extensions_lock_pointer(GtkWidget *widget,
+                                      void (*lock_cb)(void *, struct zwp_locked_pointer_v1 *),
+                                      void (*unlock_cb)(void *, struct zwp_locked_pointer_v1 *))
+{
+    struct zwp_pointer_constraints_v1 *pointer_constraints;
+    struct zwp_locked_pointer_v1 *locked_pointer;
+    GdkWindow *window;
+    struct wl_pointer *pointer;
+
+    g_return_val_if_fail(GTK_IS_WIDGET(widget), -1);
+
+    pointer_constraints = g_object_get_data(G_OBJECT(widget), "zwp_pointer_constraints_v1");
+    locked_pointer = g_object_get_data(G_OBJECT(widget), "zwp_locked_pointer_v1");
+    if (locked_pointer != NULL) {
+        /* A previous lock already in place */
+        return 0;
+    }
+
+    window = gtk_widget_get_window(widget);
+    pointer = gdk_wayland_device_get_wl_pointer(spice_gdk_window_get_pointing_device(window));
+    locked_pointer = zwp_pointer_constraints_v1_lock_pointer(pointer_constraints,
+                                                             gdk_wayland_window_get_wl_surface(window),
+                                                             pointer,
+                                                             NULL,
+                                                             ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
+    if (lock_cb || unlock_cb) {
+        locked_pointer_listener.locked = lock_cb;
+        locked_pointer_listener.unlocked = unlock_cb;
+        zwp_locked_pointer_v1_add_listener(locked_pointer,
+                                           &locked_pointer_listener,
+                                           widget);
+    }
+    g_object_set_data_full(G_OBJECT(widget),
+                           "zwp_locked_pointer_v1",
+                           locked_pointer,
+                           (GDestroyNotify)zwp_locked_pointer_v1_destroy);
+
+    return 0;
+}
+
+int
+spice_wayland_extensions_unlock_pointer(GtkWidget *widget)
+{
+    g_return_val_if_fail(GTK_IS_WIDGET(widget), -1);
+
+    g_object_set_data(G_OBJECT(widget), "zwp_locked_pointer_v1", NULL);
+
+    return 0;
+}
diff --git a/src/wayland-extensions.h b/src/wayland-extensions.h
new file mode 100644
index 0000000..bf34044
--- /dev/null
+++ b/src/wayland-extensions.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2017 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#pragma once
+
+#include <gtk/gtk.h>
+
+void spice_wayland_extensions_init(GtkWidget *widget);
+int spice_wayland_extensions_enable_relative_pointer(GtkWidget *widget,
+                                                     void (*cb)(void *,
+                                                                struct zwp_relative_pointer_v1 *,
+                                                                uint32_t, uint32_t,
+                                                                wl_fixed_t, wl_fixed_t, wl_fixed_t, wl_fixed_t));
+int spice_wayland_extensions_disable_relative_pointer(GtkWidget *widget);
+int spice_wayland_extensions_lock_pointer(GtkWidget *widget,
+                                          void (*lock_cb)(void *, struct zwp_locked_pointer_v1 *),
+                                          void (*unlock_cb)(void *, struct zwp_locked_pointer_v1 *));
+int spice_wayland_extensions_unlock_pointer(GtkWidget *widget);
commit d70044ab49728524c68796dd371d2bf7d94d27e5
Author: Francesco Giudici <fgiudici at redhat.com>
Date:   Thu Feb 27 11:55:15 2020 +0100

    meson: add wayland protocols
    
    Generate wayland protocols: these will be used later for locking the
    mouse pointer and getting relative mouse movements when using the mouse
    in server mode in Wayland.
    The meson build steps have been freely taken and adapted from Weston
    source code:
    https://gitlab.freedesktop.org/wayland/weston/-/blob/master/protocol/meson.build
    
    Signed-off-by: Francesco Giudici <fgiudici at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/meson.build b/meson.build
index 1b3e08d..405c589 100644
--- a/meson.build
+++ b/meson.build
@@ -31,6 +31,7 @@ spice_gtk_config_data = configuration_data()
 spice_gtk_include = [include_directories('.')]
 spice_glib_deps = []
 spice_gtk_deps = []
+spice_wayland_deps = []
 spice_acl_deps = []
 spice_protocol_version = '0.14.2'
 
@@ -154,6 +155,25 @@ if d.found()
   spice_gtk_has_gtk = true
 endif
 
+# wayland protocols
+spice_gtk_has_wayland_protocols = false
+# Check if gtk is enabled and supports the wayland backend
+if host_machine.system() != 'windows' and spice_gtk_has_gtk and compiler.has_header('gtk-3.0/gdk/gdkwayland.h')
+  d = dependency('wayland-protocols', version: '>= 1.17', required: get_option('wayland-protocols'))
+  if d.found()
+    spice_gtk_config_data.set('HAVE_WAYLAND_PROTOCOLS', '1')
+    dir_wp_base = d.get_pkgconfig_variable('pkgdatadir')
+    dep_scanner = dependency('wayland-scanner', native: true)
+    prog_scanner = find_program(dep_scanner.get_pkgconfig_variable('wayland_scanner'))
+
+    wayland_libs_version_required = '1.17.0'
+    spice_wayland_deps += dependency('wayland-server', version : '>= @0@'.format(wayland_libs_version_required))
+    spice_wayland_deps += dependency('wayland-cursor', version : '>= @0@'.format(wayland_libs_version_required))
+    spice_wayland_deps += dependency('wayland-client', version : '>= @0@'.format(wayland_libs_version_required))
+    spice_gtk_has_wayland_protocols = true
+  endif
+endif
+
 # webdav
 spice_gtk_has_phodav = false
 d = dependency('libphodav-2.0', required: get_option('webdav'))
diff --git a/meson_options.txt b/meson_options.txt
index 88ca9b4..4bffbfa 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -2,6 +2,10 @@ option('gtk',
     type : 'feature',
     description: 'Enable gtk+')
 
+option('wayland-protocols',
+    type : 'feature',
+    description: 'Enable wayland protocols')
+
 option('webdav',
     type : 'feature',
     description: 'Enable webdav support')
diff --git a/src/meson.build b/src/meson.build
index dc78c94..0f507d5 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -313,6 +313,64 @@ if spice_gtk_has_gtk
                                               command : cmd)
   endforeach
 
+  #
+  # Wayland protocols
+  #
+  if spice_gtk_has_wayland_protocols
+
+    generated_protocols = [
+      [ 'pointer-constraints', 'v1' ],
+      [ 'relative-pointer', 'v1' ],
+     #[ 'input-method', 'v1' ],
+     #[ 'input-timestamps', 'v1' ],
+     #[ 'fullscreen-shell', 'v1' ],
+     #[ 'linux-dmabuf', 'v1' ],
+     #[ 'linux-explicit-synchronization', 'v1' ],
+     #[ 'presentation-time', 'stable' ],
+     #[ 'tablet', 'v2' ],
+     #[ 'text-input', 'v1' ],
+     #[ 'viewporter', 'stable' ],
+     #[ 'xdg-output', 'v1' ],
+     #[ 'xdg-shell', 'v6' ],
+     #[ 'xdg-shell', 'stable' ],
+    ]
+
+    foreach proto: generated_protocols
+      proto_name = proto[0]
+      if proto[1] == 'internal'
+        base_file = proto_name
+        xml_path = '@0 at .xml'.format(proto_name)
+      elif proto[1] == 'stable'
+        base_file = proto_name
+        xml_path = '@0@/stable/@1@/@1 at .xml'.format(dir_wp_base, base_file)
+      else
+        base_file = '@0 at -unstable-@1@'.format(proto_name, proto[1])
+        xml_path = '@0@/unstable/@1@/@2 at .xml'.format(dir_wp_base, proto_name, base_file)
+      endif
+
+      foreach output_type: [ 'client-header', 'server-header', 'private-code' ]
+        if output_type == 'client-header'
+          output_file = '@0 at -client-protocol.h'.format(base_file)
+        elif output_type == 'server-header'
+          output_file = '@0 at -server-protocol.h'.format(base_file)
+        else
+          output_file = '@0 at -protocol.c'.format(base_file)
+          if dep_scanner.version().version_compare('< 1.14.91')
+              output_type = 'code'
+          endif
+        endif
+
+        spice_client_gtk_sources += custom_target(
+          '@0@ @1@'.format(base_file, output_type),
+          command: [ prog_scanner, output_type, '@INPUT@', '@OUTPUT@' ],
+          input: xml_path,
+          output: output_file,
+        )
+        message('@0@ @1@: @2@ -> @3@'.format(prog_scanner, output_type, xml_path, output_file))
+      endforeach
+    endforeach
+  endif
+
   #
   # libspice-client-gtk.so
   #
@@ -338,11 +396,11 @@ if spice_gtk_has_gtk
                                  install : true,
                                  link_args : [spice_gtk_version_script],
                                  link_depends : spice_client_gtk_syms,
-                                 dependencies : [spice_client_glib_dep, spice_gtk_deps])
+                                 dependencies : [spice_client_glib_dep, spice_gtk_deps, spice_wayland_deps])
 
   spice_client_gtk_dep = declare_dependency(sources : spice_widget_enums[1],
                                             link_with : spice_client_gtk_lib,
-                                            dependencies : [spice_client_glib_dep, spice_gtk_deps])
+                                            dependencies : [spice_client_glib_dep, spice_gtk_deps, spice_wayland_deps])
 
   if spice_gtk_has_introspection
     #


More information about the Spice-commits mailing list