libs: wayland: add support for XDG-shell protocol
authorNiels De Graef <niels.degraef@barco.com>
Mon, 14 Jan 2019 10:30:48 +0000 (11:30 +0100)
committerVíctor Manuel Jáquez Leal <vjaquez@igalia.com>
Sat, 16 Feb 2019 22:55:42 +0000 (23:55 +0100)
[wl_shell] is officially [deprecated], so provide support for the
XDG-shell protocol should be provided by all desktop-like compositors.
(In case they don't, we can of course fall back to wl_shell).

Note that the XML file is directly provided by the `wayland-protocols`
dependency and generates the protocol marshalling code.

[wl_shell]: https://people.freedesktop.org/~whot/wayland-doxygen/wayland/Client/group__iface__wl__shell.html
[deprecated]: https://github.com/wayland-project/wayland/commit/698dde195837f3d0844b2725ba4ea8ce9ee7518c

configure.ac
gst-libs/gst/vaapi/Makefile.am
gst-libs/gst/vaapi/gstvaapidisplay_wayland.c
gst-libs/gst/vaapi/gstvaapidisplay_wayland_priv.h
gst-libs/gst/vaapi/gstvaapiwindow_wayland.c
gst-libs/gst/vaapi/meson.build
gst/vaapi/gstvaapisink.c
meson.build

index 0bdb5bb..cf382d7 100644 (file)
@@ -466,13 +466,18 @@ fi
 dnl Check for Wayland
 USE_WAYLAND=0
 if test "x$enable_wayland" = "xyes"; then
-  PKG_CHECK_MODULES([WAYLAND], [wayland-client >= $WAYLAND_REQ],
+  PKG_CHECK_MODULES([WAYLAND], [wayland-client >= $WAYLAND_REQ, wayland-protocols >= 1.15],
     [
       USE_WAYLAND=1
       saved_CPPFLAGS="$CPPFLAGS"
       CPPFLAGS="$CPPFLAGS $WAYLAND_CFLAGS"
       AC_CHECK_HEADERS([wayland-client.h], [], [USE_WAYLAND=0])
       CPPFLAGS="$saved_CPPFLAGS"
+
+      AC_CHECK_PROGS(WAYLAND_SCANNER, wayland-scanner, [USE_WAYLAND=0])
+
+      WAYLAND_PROTOCOLS_DATADIR="`$PKG_CONFIG --variable=pkgdatadir wayland-protocols`"
+      AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, $WAYLAND_PROTOCOLS_DATADIR)
     ], [:])
 fi
 
index 976466e..50ca973 100644 (file)
@@ -314,9 +314,28 @@ libgstvaapi_egl_source_priv_h =                    \
        ogl_compat.h                            \
        $(NULL)
 
+BUILT_SOURCES=
+CLEANFILES=
+
+# Generate the necessary files for XDG-shell
+if USE_WAYLAND
+xdg_shell_protocol_spec = $(WAYLAND_PROTOCOLS_DATADIR)/stable/xdg-shell/xdg-shell.xml
+xdg_shell_header = xdg-shell-client-protocol.h
+xdg_shell_source = xdg-shell-client-protocol.c
+
+$(xdg_shell_header): $(xdg_shell_protocol_spec)
+       $(AM_V_GEN) $(WAYLAND_SCANNER) client-header < $< > $@
+$(xdg_shell_source): $(xdg_shell_protocol_spec)
+       $(AM_V_GEN) $(WAYLAND_SCANNER) private-code < $< > $@
+
+BUILT_SOURCES += $(xdg_shell_header) $(xdg_shell_source)
+CLEANFILES += $(BUILT_SOURCES)
+endif
+
 libgstvaapi_wayland_source_c =                 \
        gstvaapidisplay_wayland.c               \
        gstvaapiwindow_wayland.c                \
+       $(xdg_shell_source)                     \
        $(NULL)
 
 libgstvaapi_wayland_source_h =                 \
@@ -327,6 +346,7 @@ libgstvaapi_wayland_source_h =                      \
 libgstvaapi_wayland_source_priv_h =            \
        gstvaapicompat.h                        \
        gstvaapidisplay_wayland_priv.h          \
+       $(xdg_shell_header)                     \
        $(NULL)
 
 libgstvaapi_la_SOURCES =                       \
index 9c40c90..660c99e 100644 (file)
@@ -95,6 +95,17 @@ static const struct wl_output_listener output_listener = {
 };
 
 static void
+handle_xdg_wm_base_ping (void *user_data, struct xdg_wm_base *xdg_wm_base,
+    uint32_t serial)
+{
+  xdg_wm_base_pong (xdg_wm_base, serial);
+}
+
+static const struct xdg_wm_base_listener xdg_wm_base_listener = {
+  handle_xdg_wm_base_ping
+};
+
+static void
 registry_handle_global (void *data,
     struct wl_registry *registry,
     uint32_t id, const char *interface, uint32_t version)
@@ -106,7 +117,11 @@ registry_handle_global (void *data,
         wl_registry_bind (registry, id, &wl_compositor_interface, 1);
   else if (strcmp (interface, "wl_shell") == 0)
     priv->wl_shell = wl_registry_bind (registry, id, &wl_shell_interface, 1);
-  else if (strcmp (interface, "wl_output") == 0) {
+  else if (strcmp (interface, "xdg_wm_base") == 0) {
+    priv->xdg_wm_base =
+        wl_registry_bind (registry, id, &xdg_wm_base_interface, 1);
+    xdg_wm_base_add_listener (priv->xdg_wm_base, &xdg_wm_base_listener, priv);
+  } else if (strcmp (interface, "wl_output") == 0) {
     priv->output = wl_registry_bind (registry, id, &wl_output_interface, 1);
     wl_output_add_listener (priv->output, &output_listener, priv);
   }
@@ -142,10 +157,14 @@ gst_vaapi_display_wayland_setup (GstVaapiDisplay * display)
     return FALSE;
   }
 
+  if (priv->xdg_wm_base)
+    return TRUE;
+
   if (!priv->wl_shell) {
     GST_ERROR ("failed to bind wl_shell interface");
     return FALSE;
   }
+
   return TRUE;
 }
 
@@ -192,6 +211,7 @@ gst_vaapi_display_wayland_close_display (GstVaapiDisplay * display)
 
   g_clear_pointer (&priv->output, wl_output_destroy);
   g_clear_pointer (&priv->wl_shell, wl_shell_destroy);
+  g_clear_pointer (&priv->xdg_wm_base, xdg_wm_base_destroy);
   g_clear_pointer (&priv->compositor, wl_compositor_destroy);
   g_clear_pointer (&priv->registry, wl_registry_destroy);
 
index 54bf9b1..b09adfb 100644 (file)
@@ -24,6 +24,8 @@
 #ifndef GST_VAAPI_DISPLAY_WAYLAND_PRIV_H
 #define GST_VAAPI_DISPLAY_WAYLAND_PRIV_H
 
+#include "xdg-shell-client-protocol.h"
+
 #include <gst/vaapi/gstvaapidisplay_wayland.h>
 #include "gstvaapidisplay_priv.h"
 
@@ -61,6 +63,7 @@ struct _GstVaapiDisplayWaylandPrivate
   struct wl_display *wl_display;
   struct wl_compositor *compositor;
   struct wl_shell *wl_shell;
+  struct xdg_wm_base *xdg_wm_base;
   struct wl_output *output;
   struct wl_registry *registry;
   guint width;
index f2efb89..adb8a75 100644 (file)
@@ -98,6 +98,8 @@ frame_state_free (FrameState * frame)
 
 struct _GstVaapiWindowWaylandPrivate
 {
+  struct xdg_surface *xdg_surface;
+  struct xdg_toplevel *xdg_toplevel;
   struct wl_shell_surface *wl_shell_surface;
   struct wl_surface *surface;
   struct wl_region *opaque_region;
@@ -137,10 +139,79 @@ struct _GstVaapiWindowWaylandClass
 G_DEFINE_TYPE_WITH_PRIVATE (GstVaapiWindowWayland, gst_vaapi_window_wayland,
     GST_TYPE_VAAPI_WINDOW);
 
+/* Object signals */
+enum
+{
+  SIZE_CHANGED,
+  N_SIGNALS
+};
+
+static guint signals[N_SIGNALS];
+
+static void
+handle_xdg_toplevel_configure (void *data, struct xdg_toplevel *xdg_toplevel,
+    int32_t width, int32_t height, struct wl_array *states)
+{
+  GstVaapiWindow *window = GST_VAAPI_WINDOW (data);
+  const uint32_t *state;
+
+  GST_DEBUG ("Got XDG-toplevel::reconfigure, [width x height] = [%d x %d]",
+      width, height);
+
+  wl_array_for_each (state, states) {
+    switch (*state) {
+      case XDG_TOPLEVEL_STATE_FULLSCREEN:
+      case XDG_TOPLEVEL_STATE_MAXIMIZED:
+      case XDG_TOPLEVEL_STATE_RESIZING:
+      case XDG_TOPLEVEL_STATE_ACTIVATED:
+        break;
+    }
+  }
+
+  if (width > 0 && height > 0) {
+    gst_vaapi_window_set_size (window, width, height);
+    g_signal_emit (window, signals[SIZE_CHANGED], 0, width, height);
+  }
+}
+
+static void
+handle_xdg_toplevel_close (void *data, struct xdg_toplevel *xdg_toplevel)
+{
+}
+
+static const struct xdg_toplevel_listener xdg_toplevel_listener = {
+  handle_xdg_toplevel_configure,
+  handle_xdg_toplevel_close,
+};
+
 static gboolean
 gst_vaapi_window_wayland_show (GstVaapiWindow * window)
 {
-  GST_WARNING ("unimplemented GstVaapiWindowWayland::show()");
+  GstVaapiWindowWaylandPrivate *priv =
+      GST_VAAPI_WINDOW_WAYLAND_GET_PRIVATE (window);
+
+  if (priv->xdg_surface == NULL) {
+    GST_FIXME ("GstVaapiWindowWayland::show() unimplemented for wl_shell");
+    return TRUE;
+  }
+
+  if (priv->xdg_toplevel != NULL) {
+    GST_DEBUG ("XDG toplevel already mapped");
+    return TRUE;
+  }
+
+  /* Create a toplevel window out of it */
+  priv->xdg_toplevel = xdg_surface_get_toplevel (priv->xdg_surface);
+  g_return_val_if_fail (priv->xdg_toplevel, FALSE);
+  xdg_toplevel_set_title (priv->xdg_toplevel, "VA-API Wayland window");
+  wl_proxy_set_queue ((struct wl_proxy *) priv->xdg_toplevel,
+      priv->event_queue);
+
+  xdg_toplevel_add_listener (priv->xdg_toplevel, &xdg_toplevel_listener,
+      window);
+
+  /* Commit the xdg_surface state as top-level window */
+  wl_surface_commit (priv->surface);
 
   return TRUE;
 }
@@ -148,7 +219,18 @@ gst_vaapi_window_wayland_show (GstVaapiWindow * window)
 static gboolean
 gst_vaapi_window_wayland_hide (GstVaapiWindow * window)
 {
-  GST_WARNING ("unimplemented GstVaapiWindowWayland::hide()");
+  GstVaapiWindowWaylandPrivate *priv =
+      GST_VAAPI_WINDOW_WAYLAND_GET_PRIVATE (window);
+
+  if (priv->xdg_surface == NULL) {
+    GST_FIXME ("GstVaapiWindowWayland::hide() unimplemented for wl_shell");
+    return TRUE;
+  }
+
+  if (priv->xdg_toplevel != NULL) {
+    g_clear_pointer (&priv->xdg_toplevel, xdg_toplevel_destroy);
+    wl_surface_commit (priv->surface);
+  }
 
   return TRUE;
 }
@@ -236,6 +318,17 @@ static const struct wl_shell_surface_listener shell_surface_listener = {
   handle_popup_done
 };
 
+static void
+handle_xdg_surface_configure (void *data, struct xdg_surface *xdg_surface,
+    uint32_t serial)
+{
+  xdg_surface_ack_configure (xdg_surface, serial);
+}
+
+static const struct xdg_surface_listener xdg_surface_listener = {
+  handle_xdg_surface_configure,
+};
+
 static gboolean
 gst_vaapi_window_wayland_set_fullscreen (GstVaapiWindow * window,
     gboolean fullscreen)
@@ -248,6 +341,16 @@ gst_vaapi_window_wayland_set_fullscreen (GstVaapiWindow * window,
     return TRUE;
   }
 
+  /* XDG-shell */
+  if (priv->xdg_toplevel != NULL) {
+    if (fullscreen)
+      xdg_toplevel_set_fullscreen (priv->xdg_toplevel, NULL);
+    else
+      xdg_toplevel_unset_fullscreen (priv->xdg_toplevel);
+    return TRUE;
+  }
+
+  /* wl_shell fallback */
   if (!fullscreen)
     wl_shell_surface_set_toplevel (priv->wl_shell_surface);
   else {
@@ -270,7 +373,8 @@ gst_vaapi_window_wayland_create (GstVaapiWindow * window,
   GST_DEBUG ("create window, size %ux%u", *width, *height);
 
   g_return_val_if_fail (priv_display->compositor != NULL, FALSE);
-  g_return_val_if_fail (priv_display->wl_shell != NULL, FALSE);
+  g_return_val_if_fail (priv_display->xdg_wm_base || priv_display->wl_shell,
+      FALSE);
 
   GST_VAAPI_WINDOW_LOCK_DISPLAY (window);
   priv->event_queue = wl_display_create_queue (priv_display->wl_display);
@@ -285,18 +389,33 @@ gst_vaapi_window_wayland_create (GstVaapiWindow * window,
     return FALSE;
   wl_proxy_set_queue ((struct wl_proxy *) priv->surface, priv->event_queue);
 
-  GST_VAAPI_WINDOW_LOCK_DISPLAY (window);
-  priv->wl_shell_surface = wl_shell_get_shell_surface (priv_display->wl_shell,
-      priv->surface);
-  GST_VAAPI_WINDOW_UNLOCK_DISPLAY (window);
-  if (!priv->wl_shell_surface)
-    return FALSE;
-  wl_proxy_set_queue ((struct wl_proxy *) priv->wl_shell_surface,
-      priv->event_queue);
+  /* Prefer XDG-shell over deprecated wl_shell (if available) */
+  if (priv_display->xdg_wm_base) {
+    /* Create the XDG surface. We make the toplevel on VaapiWindow::show() */
+    GST_VAAPI_WINDOW_LOCK_DISPLAY (window);
+    priv->xdg_surface = xdg_wm_base_get_xdg_surface (priv_display->xdg_wm_base,
+        priv->surface);
+    GST_VAAPI_WINDOW_UNLOCK_DISPLAY (window);
+    if (!priv->xdg_surface)
+      return FALSE;
+    wl_proxy_set_queue ((struct wl_proxy *) priv->xdg_surface,
+        priv->event_queue);
+    xdg_surface_add_listener (priv->xdg_surface, &xdg_surface_listener, window);
+  } else {
+    /* Fall back to wl_shell */
+    GST_VAAPI_WINDOW_LOCK_DISPLAY (window);
+    priv->wl_shell_surface = wl_shell_get_shell_surface (priv_display->wl_shell,
+        priv->surface);
+    GST_VAAPI_WINDOW_UNLOCK_DISPLAY (window);
+    if (!priv->wl_shell_surface)
+      return FALSE;
+    wl_proxy_set_queue ((struct wl_proxy *) priv->wl_shell_surface,
+        priv->event_queue);
 
-  wl_shell_surface_add_listener (priv->wl_shell_surface,
-      &shell_surface_listener, priv);
-  wl_shell_surface_set_toplevel (priv->wl_shell_surface);
+    wl_shell_surface_add_listener (priv->wl_shell_surface,
+        &shell_surface_listener, priv);
+    wl_shell_surface_set_toplevel (priv->wl_shell_surface);
+  }
 
   priv->poll = gst_poll_new (TRUE);
   gst_poll_fd_init (&priv->pollfd);
@@ -332,6 +451,7 @@ gst_vaapi_window_wayland_finalize (GObject * object)
   if (priv->event_queue)
     wl_display_roundtrip_queue (wl_display, priv->event_queue);
 
+  g_clear_pointer (&priv->xdg_surface, xdg_surface_destroy);
   g_clear_pointer (&priv->wl_shell_surface, wl_shell_surface_destroy);
   g_clear_pointer (&priv->surface, wl_surface_destroy);
   g_clear_pointer (&priv->event_queue, wl_event_queue_destroy);
@@ -550,6 +670,10 @@ gst_vaapi_window_wayland_class_init (GstVaapiWindowWaylandClass * klass)
   window_class->set_fullscreen = gst_vaapi_window_wayland_set_fullscreen;
   window_class->unblock = gst_vaapi_window_wayland_unblock;
   window_class->unblock_cancel = gst_vaapi_window_wayland_unblock_cancel;
+
+  signals[SIZE_CHANGED] = g_signal_new ("size-changed",
+      G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
+      G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
 }
 
 static void
index 6a289af..c5c40f7 100644 (file)
@@ -183,10 +183,24 @@ if USE_EGL
 endif
 
 if USE_WAYLAND
+  # The XDG shell interface needs to be generated first
+  wayland_protocols_basedir = wayland_protocols_dep.get_pkgconfig_variable('pkgdatadir')
+  xdg_shell_xml_spec = join_paths(wayland_protocols_basedir, 'stable', 'xdg-shell', 'xdg-shell.xml')
+  xdg_shell_header = custom_target('vaapi-xdg-shell-client-header',
+      command: [ wayland_scanner_bin, 'client-header', '@INPUT@', '@OUTPUT@' ],
+      input: xdg_shell_xml_spec,
+      output: 'xdg-shell-client-protocol.h')
+  xdg_shell_code = custom_target('vaapi-xdg-shell-client-code',
+      command: [ wayland_scanner_bin, 'private-code', '@INPUT@', '@OUTPUT@' ],
+      input: xdg_shell_xml_spec,
+      output: 'xdg-shell-client-protocol.c')
+
   gstlibvaapi_sources += [
       'gstvaapidisplay_wayland.c',
       'gstvaapiwindow_wayland.c',
-    ]
+      xdg_shell_header,
+      xdg_shell_code,
+  ]
   gstlibvaapi_headers += [
       'gstvaapidisplay_wayland.h',
       'gstvaapiwindow_wayland.h',
@@ -209,7 +223,7 @@ if USE_GLX
   gstlibvaapi_deps  += [libva_x11_dep, x11_dep, gl_dep, libdl_dep]
 endif
 if USE_WAYLAND
-  gstlibvaapi_deps  += [libva_wayland_dep, wayland_client_dep]
+  gstlibvaapi_deps  += [libva_wayland_dep, wayland_client_dep, wayland_protocols_dep]
 endif
 if USE_X11
   gstlibvaapi_deps  += [libva_x11_dep, x11_dep, xrandr_dep, xrender_dep]
index 158ade9..06d5d67 100644 (file)
@@ -533,6 +533,17 @@ gst_vaapisink_backend_x11 (void)
 #include <gst/vaapi/gstvaapidisplay_wayland.h>
 #include <gst/vaapi/gstvaapiwindow_wayland.h>
 
+static void
+on_window_wayland_size_changed (GstVaapiWindowWayland * window, gint width,
+    gint height, gpointer user_data)
+{
+  GstVaapiSink *sink = GST_VAAPISINK (user_data);
+
+  GST_DEBUG ("Wayland window size changed to: %dx%d", width, height);
+  gst_vaapisink_reconfigure_window (sink);
+  gst_vaapisink_show_frame (GST_VIDEO_SINK_CAST (sink), NULL);
+}
+
 static gboolean
 gst_vaapisink_wayland_create_window (GstVaapiSink * sink, guint width,
     guint height)
@@ -544,6 +555,10 @@ gst_vaapisink_wayland_create_window (GstVaapiSink * sink, guint width,
   sink->window = gst_vaapi_window_wayland_new (display, width, height);
   if (!sink->window)
     return FALSE;
+
+  g_signal_connect_object (sink->window, "size-changed",
+      G_CALLBACK (on_window_wayland_size_changed), sink, 0);
+
   return TRUE;
 }
 
index af372a3..b32b774 100644 (file)
@@ -65,6 +65,8 @@ gl_dep = dependency('gl', required: false)
 glesv2_dep = dependency('glesv2', required: false)
 libdl_dep = cc.find_library('dl', required: false)
 wayland_client_dep = dependency('wayland-client', version: libwayland_req, required: false)
+wayland_protocols_dep = dependency('wayland-protocols', version: '>= 1.15', required: false)
+wayland_scanner_bin = find_program('wayland-scanner', required: false)
 x11_dep = dependency('x11', required: false)
 xrandr_dep = dependency('xrandr', required: false)
 xrender_dep = dependency('xrender', required: false)
@@ -92,7 +94,7 @@ USE_H264_FEI_ENCODER = USE_ENCODERS and cc.has_header('va/va_fei_h264.h', depend
 USE_DRM = libva_drm_dep.found() and libdrm_dep.found() and libudev_dep.found() and get_option('with_drm') != 'no'
 USE_EGL = gmodule_dep.found() and egl_dep.found() and GLES_VERSION_MASK != 0 and get_option('with_egl') != 'no'
 USE_GLX = libva_x11_dep.found() and x11_dep.found() and gl_dep.found() and libdl_dep.found() and get_option('with_glx') != 'no'
-USE_WAYLAND = libva_wayland_dep.found() and wayland_client_dep.found() and get_option('with_wayland') != 'no'
+USE_WAYLAND = libva_wayland_dep.found() and wayland_client_dep.found() and wayland_protocols_dep.found() and wayland_scanner_bin.found() and get_option('with_wayland') != 'no'
 USE_X11 = libva_x11_dep.found() and x11_dep.found() and get_option('with_x11') != 'no'
 
 driverdir = libva_dep.get_pkgconfig_variable('driverdir')