waylandsink: Implement XDG-shell stable support
authorNiels De Graef <niels.degraef@barco.com>
Fri, 1 Mar 2019 08:56:24 +0000 (09:56 +0100)
committerNiels De Graef <nielsdegraef@gmail.com>
Mon, 4 Mar 2019 10:58:54 +0000 (10:58 +0000)
[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 spec] is provided by the `wayland-protocols`
git repository, which is provided by the Wayland project.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/issues/897

[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
[XML spec]: https://github.com/wayland-project/wayland-protocols/blob/master/stable/xdg-shell/xdg-shell.xml

configure.ac
ext/wayland/Makefile.am
ext/wayland/gstwaylandsink.c
ext/wayland/meson.build
ext/wayland/wldisplay.c
ext/wayland/wldisplay.h
ext/wayland/wlwindow.c
ext/wayland/wlwindow.h
gst-libs/gst/wayland/meson.build

index 90a5bc2..68a144d 100644 (file)
@@ -1319,7 +1319,7 @@ dnl **** Wayland ****
 translit(dnm, m, l) AM_CONDITIONAL(USE_WAYLAND, true)
 AC_PATH_PROG([wayland_scanner], [wayland-scanner])
 AG_GST_CHECK_FEATURE(WAYLAND, [wayland sink], wayland , [
-  PKG_CHECK_MODULES(WAYLAND, wayland-client >= 1.11.0 libdrm >= 2.4.55 wayland-protocols >= 1.4, [
+  PKG_CHECK_MODULES(WAYLAND, wayland-client >= 1.11.0 libdrm >= 2.4.55 wayland-protocols >= 1.15, [
     if test "x$wayland_scanner" != "x"; then
       HAVE_WAYLAND="yes"
       AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, `$PKG_CONFIG --variable=pkgdatadir wayland-protocols`)
index a7fcc32..95bb97e 100644 (file)
@@ -6,7 +6,9 @@ BUILT_SOURCES = \
        linux-dmabuf-unstable-v1-protocol.c \
        linux-dmabuf-unstable-v1-client-protocol.h \
        fullscreen-shell-unstable-v1-protocol.c \
-       fullscreen-shell-unstable-v1-client-protocol.h
+       fullscreen-shell-unstable-v1-client-protocol.h \
+       xdg-shell-protocol.c \
+       xdg-shell-client-protocol.h
 
 libgstwaylandsink_la_SOURCES =  \
        gstwaylandsink.c \
@@ -20,7 +22,8 @@ libgstwaylandsink_la_SOURCES =  \
 nodist_libgstwaylandsink_la_SOURCES = \
        viewporter-protocol.c \
        linux-dmabuf-unstable-v1-protocol.c \
-       fullscreen-shell-unstable-v1-protocol.c
+       fullscreen-shell-unstable-v1-protocol.c \
+       xdg-shell-protocol.c
 
 libgstwaylandsink_la_CFLAGS = \
        $(GST_PLUGINS_BAD_CFLAGS) \
index 158c9af..78dd294 100644 (file)
@@ -659,6 +659,16 @@ render_last_buffer (GstWaylandSink * sink, gboolean redraw)
   gst_wl_window_render (sink->window, wlbuffer, info);
 }
 
+static void
+on_window_closed (GstWlWindow * window, gpointer user_data)
+{
+  GstWaylandSink *sink = GST_WAYLAND_SINK (user_data);
+
+  /* Handle window closure by posting an error on the bus */
+  GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
+      ("Output window was closed"), (NULL));
+}
+
 static GstFlowReturn
 gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
 {
@@ -688,6 +698,8 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
       /* if we were not provided a window, create one ourselves */
       sink->window = gst_wl_window_new_toplevel (sink->display,
           &sink->video_info, sink->fullscreen, &sink->render_lock);
+      g_signal_connect_object (sink->window, "closed",
+          G_CALLBACK (on_window_closed), sink, 0);
     }
   }
 
index 3246cae..fa6fbf4 100644 (file)
@@ -16,7 +16,8 @@ if use_wayland
         ['/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml',
          'linux-dmabuf-unstable-v1-protocol.c', 'linux-dmabuf-unstable-v1-client-protocol.h'],
         ['/unstable/fullscreen-shell/fullscreen-shell-unstable-v1.xml',
-         'fullscreen-shell-unstable-v1-protocol.c', 'fullscreen-shell-unstable-v1-client-protocol.h']
+         'fullscreen-shell-unstable-v1-protocol.c', 'fullscreen-shell-unstable-v1-client-protocol.h'],
+        ['/stable/xdg-shell/xdg-shell.xml', 'xdg-shell-protocol.c', 'xdg-shell-client-protocol.h'],
     ]
     protocols_files = []
 
index 0799019..9400095 100644 (file)
@@ -90,6 +90,9 @@ gst_wl_display_finalize (GObject * gobject)
   if (self->wl_shell)
     wl_shell_destroy (self->wl_shell);
 
+  if (self->xdg_wm_base)
+    xdg_wm_base_destroy (self->xdg_wm_base);
+
   if (self->fullscreen_shell)
     zwp_fullscreen_shell_v1_release (self->fullscreen_shell);
 
@@ -187,6 +190,17 @@ gst_wl_display_check_format_for_dmabuf (GstWlDisplay * display,
 }
 
 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)
 {
@@ -200,6 +214,10 @@ registry_handle_global (void *data, struct wl_registry *registry,
         wl_registry_bind (registry, id, &wl_subcompositor_interface, 1);
   } else if (g_strcmp0 (interface, "wl_shell") == 0) {
     self->wl_shell = wl_registry_bind (registry, id, &wl_shell_interface, 1);
+  } else if (g_strcmp0 (interface, "xdg_wm_base") == 0) {
+    self->xdg_wm_base =
+        wl_registry_bind (registry, id, &xdg_wm_base_interface, 1);
+    xdg_wm_base_add_listener (self->xdg_wm_base, &xdg_wm_base_listener, self);
   } else if (g_strcmp0 (interface, "zwp_fullscreen_shell_v1") == 0) {
     self->fullscreen_shell = wl_registry_bind (registry, id,
         &zwp_fullscreen_shell_v1_interface, 1);
@@ -331,13 +349,13 @@ gst_wl_display_new_existing (struct wl_display * display,
     g_warning ("Could not bind to zwp_linux_dmabuf_v1");
   }
 
-  if (!self->wl_shell && !self->fullscreen_shell) {
+  if (!self->wl_shell && !self->xdg_wm_base && !self->fullscreen_shell) {
     /* If wl_surface and wl_display are passed via GstContext
-     * wl_shell, zwp_fullscreen_shell are not used.
+     * wl_shell, xdg_shell and zwp_fullscreen_shell are not used.
      * In this case is correct to continue.
      */
-    g_warning ("Could not bind to wl_shell or zwp_fullscreen_shell, video "
-        "display may not work properly.");
+    g_warning ("Could not bind to either wl_shell, xdg_wm_base or "
+        "zwp_fullscreen_shell, video display may not work properly.");
   }
 
   self->thread = g_thread_try_new ("GstWlDisplay", gst_wl_display_thread_run,
index 020ed6e..4ecc0d6 100644 (file)
@@ -55,6 +55,7 @@ struct _GstWlDisplay
   struct wl_compositor *compositor;
   struct wl_subcompositor *subcompositor;
   struct wl_shell *wl_shell;
+  struct xdg_wm_base *xdg_wm_base;
   struct zwp_fullscreen_shell_v1 *fullscreen_shell;
   struct wl_shm *shm;
   struct wp_viewporter *viewporter;
index 547917b..afb5988 100644 (file)
 GST_DEBUG_CATEGORY_EXTERN (gstwayland_debug);
 #define GST_CAT_DEFAULT gstwayland_debug
 
+enum
+{
+  CLOSED,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
 G_DEFINE_TYPE (GstWlWindow, gst_wl_window, G_TYPE_OBJECT);
 
 static void gst_wl_window_finalize (GObject * gobject);
 
 static void
+handle_xdg_toplevel_close (void *data, struct xdg_toplevel *xdg_toplevel)
+{
+  GstWlWindow *window = data;
+
+  GST_DEBUG ("XDG toplevel got a \"close\" event.");
+  g_signal_emit (window, signals[CLOSED], 0);
+}
+
+static void
+handle_xdg_toplevel_configure (void *data, struct xdg_toplevel *xdg_toplevel,
+    int32_t width, int32_t height, struct wl_array *states)
+{
+  GstWlWindow *window = data;
+  const uint32_t *state;
+
+  GST_DEBUG ("XDG toplevel got a \"configure\" event, [ %d, %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)
+    return;
+
+  gst_wl_window_set_render_rectangle (window, 0, 0, width, height);
+}
+
+static const struct xdg_toplevel_listener xdg_toplevel_listener = {
+  handle_xdg_toplevel_configure,
+  handle_xdg_toplevel_close,
+};
+
+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 void
 handle_ping (void *data, struct wl_shell_surface *wl_shell_surface,
     uint32_t serial)
 {
@@ -74,6 +133,9 @@ gst_wl_window_class_init (GstWlWindowClass * klass)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
   gobject_class->finalize = gst_wl_window_finalize;
+
+  signals[CLOSED] = g_signal_new ("closed", G_TYPE_FROM_CLASS (gobject_class),
+      G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
 }
 
 static void
@@ -89,6 +151,11 @@ gst_wl_window_finalize (GObject * gobject)
   if (self->wl_shell_surface)
     wl_shell_surface_destroy (self->wl_shell_surface);
 
+  if (self->xdg_toplevel)
+    xdg_toplevel_destroy (self->xdg_toplevel);
+  if (self->xdg_surface)
+    xdg_surface_destroy (self->xdg_surface);
+
   if (self->video_viewport)
     wp_viewport_destroy (self->video_viewport);
 
@@ -157,11 +224,18 @@ gst_wl_window_ensure_fullscreen (GstWlWindow * window, gboolean fullscreen)
   if (!window)
     return;
 
-  if (fullscreen)
-    wl_shell_surface_set_fullscreen (window->wl_shell_surface,
-        WL_SHELL_SURFACE_FULLSCREEN_METHOD_SCALE, 0, NULL);
-  else
-    wl_shell_surface_set_toplevel (window->wl_shell_surface);
+  if (window->display->xdg_wm_base) {
+    if (fullscreen)
+      xdg_toplevel_set_fullscreen (window->xdg_toplevel, NULL);
+    else
+      xdg_toplevel_unset_fullscreen (window->xdg_toplevel);
+  } else {
+    if (fullscreen)
+      wl_shell_surface_set_fullscreen (window->wl_shell_surface,
+          WL_SHELL_SURFACE_FULLSCREEN_METHOD_SCALE, 0, NULL);
+    else
+      wl_shell_surface_set_toplevel (window->wl_shell_surface);
+  }
 }
 
 GstWlWindow *
@@ -173,28 +247,51 @@ gst_wl_window_new_toplevel (GstWlDisplay * display, const GstVideoInfo * info,
 
   window = gst_wl_window_new_internal (display, render_lock);
 
-  if (display->wl_shell) {
+  /* Check which protocol we will use (in order of preference) */
+  if (display->xdg_wm_base) {
+    /* First create the XDG surface */
+    window->xdg_surface = xdg_wm_base_get_xdg_surface (display->xdg_wm_base,
+        window->area_surface);
+    if (!window->xdg_surface) {
+      GST_ERROR ("Unable to get xdg_surface");
+      goto error;
+    }
+    xdg_surface_add_listener (window->xdg_surface, &xdg_surface_listener,
+        window);
+
+    /* Then the toplevel */
+    window->xdg_toplevel = xdg_surface_get_toplevel (window->xdg_surface);
+    if (!window->xdg_toplevel) {
+      GST_ERROR ("Unable to get xdg_toplevel");
+      goto error;
+    }
+    xdg_toplevel_add_listener (window->xdg_toplevel,
+        &xdg_toplevel_listener, window);
+
+    /* Finally, commit the xdg_surface state as toplevel */
+    wl_surface_commit (window->area_surface);
+
+    gst_wl_window_ensure_fullscreen (window, fullscreen);
+  } else if (display->wl_shell) {
     /* go toplevel */
     window->wl_shell_surface = wl_shell_get_shell_surface (display->wl_shell,
         window->area_surface);
-
-    if (window->wl_shell_surface) {
-      wl_shell_surface_add_listener (window->wl_shell_surface,
-          &wl_shell_surface_listener, window);
-      gst_wl_window_ensure_fullscreen (window, fullscreen);
-    } else {
+    if (!window->wl_shell_surface) {
       GST_ERROR ("Unable to get wl_shell_surface");
-      g_object_unref (window);
-      return NULL;
+      goto error;
     }
+
+    wl_shell_surface_add_listener (window->wl_shell_surface,
+        &wl_shell_surface_listener, window);
+    gst_wl_window_ensure_fullscreen (window, fullscreen);
   } else if (display->fullscreen_shell) {
     zwp_fullscreen_shell_v1_present_surface (display->fullscreen_shell,
         window->area_surface, ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_ZOOM,
         NULL);
   } else {
-    GST_ERROR ("Unable to use wl_shell or zwp_fullscreen_shell.");
-    g_object_unref (window);
-    return NULL;
+    GST_ERROR ("Unable to use either wl_shell, xdg_wm_base or "
+        "zwp_fullscreen_shell.");
+    goto error;
   }
 
   /* set the initial size to be the same as the reported video size */
@@ -203,6 +300,10 @@ gst_wl_window_new_toplevel (GstWlDisplay * display, const GstVideoInfo * info,
   gst_wl_window_set_render_rectangle (window, 0, 0, width, info->height);
 
   return window;
+
+error:
+  g_object_unref (window);
+  return NULL;
 }
 
 GstWlWindow *
index ce2fa93..861e324 100644 (file)
@@ -53,6 +53,8 @@ struct _GstWlWindow
   struct wl_subsurface *video_subsurface;
   struct wp_viewport *video_viewport;
   struct wl_shell_surface *wl_shell_surface;
+  struct xdg_surface *xdg_surface;
+  struct xdg_toplevel *xdg_toplevel;
 
   /* the size and position of the area_(sub)surface */
   GstVideoRectangle render_rectangle;
index 8bfefcb..b1ede41 100644 (file)
@@ -1,4 +1,4 @@
-wl_req = '>= 1.4'
+wl_req = '>= 1.15'
 wl_client_dep = dependency('wayland-client', version: wl_req, required: get_option('wayland'))
 libdrm_dep = dependency('libdrm', version: '>= 2.4.55', required: get_option('wayland'))
 wl_protocol_dep = dependency('wayland-protocols', version: wl_req, required: get_option('wayland'))