From 67bb17e4fae76f8f662fdd69bba6cdaaf37a90fa Mon Sep 17 00:00:00 2001 From: Niels De Graef Date: Fri, 1 Mar 2019 09:56:24 +0100 Subject: [PATCH] waylandsink: Implement XDG-shell stable support [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 | 2 +- ext/wayland/Makefile.am | 7 +- ext/wayland/gstwaylandsink.c | 12 +++ ext/wayland/meson.build | 3 +- ext/wayland/wldisplay.c | 26 +++++- ext/wayland/wldisplay.h | 1 + ext/wayland/wlwindow.c | 135 +++++++++++++++++++++++++++---- ext/wayland/wlwindow.h | 2 + gst-libs/gst/wayland/meson.build | 2 +- 9 files changed, 164 insertions(+), 26 deletions(-) diff --git a/configure.ac b/configure.ac index 90a5bc25ce..68a144d8aa 100644 --- a/configure.ac +++ b/configure.ac @@ -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`) diff --git a/ext/wayland/Makefile.am b/ext/wayland/Makefile.am index a7fcc32786..95bb97e7fa 100644 --- a/ext/wayland/Makefile.am +++ b/ext/wayland/Makefile.am @@ -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) \ diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c index 158c9af884..78dd294a01 100644 --- a/ext/wayland/gstwaylandsink.c +++ b/ext/wayland/gstwaylandsink.c @@ -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); } } diff --git a/ext/wayland/meson.build b/ext/wayland/meson.build index 3246cae9b0..fa6fbf42f3 100644 --- a/ext/wayland/meson.build +++ b/ext/wayland/meson.build @@ -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 = [] diff --git a/ext/wayland/wldisplay.c b/ext/wayland/wldisplay.c index 079901979b..9400095567 100644 --- a/ext/wayland/wldisplay.c +++ b/ext/wayland/wldisplay.c @@ -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); @@ -186,6 +189,17 @@ gst_wl_display_check_format_for_dmabuf (GstWlDisplay * display, return FALSE; } +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, diff --git a/ext/wayland/wldisplay.h b/ext/wayland/wldisplay.h index 020ed6ea3b..4ecc0d6593 100644 --- a/ext/wayland/wldisplay.h +++ b/ext/wayland/wldisplay.h @@ -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; diff --git a/ext/wayland/wlwindow.c b/ext/wayland/wlwindow.c index 547917baaa..afb5988787 100644 --- a/ext/wayland/wlwindow.c +++ b/ext/wayland/wlwindow.c @@ -31,10 +31,69 @@ 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 * diff --git a/ext/wayland/wlwindow.h b/ext/wayland/wlwindow.h index ce2fa939a7..861e324add 100644 --- a/ext/wayland/wlwindow.h +++ b/ext/wayland/wlwindow.h @@ -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; diff --git a/gst-libs/gst/wayland/meson.build b/gst-libs/gst/wayland/meson.build index 8bfefcb3b1..b1ede41f1a 100644 --- a/gst-libs/gst/wayland/meson.build +++ b/gst-libs/gst/wayland/meson.build @@ -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'))