From f84394fa4957e1241c62b4e786d2578473b2fb5d Mon Sep 17 00:00:00 2001 From: Niels De Graef Date: Mon, 14 Jan 2019 11:30:48 +0100 Subject: [PATCH] libs: wayland: add support for XDG-shell protocol [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 | 7 +- gst-libs/gst/vaapi/Makefile.am | 20 +++ gst-libs/gst/vaapi/gstvaapidisplay_wayland.c | 22 +++- gst-libs/gst/vaapi/gstvaapidisplay_wayland_priv.h | 3 + gst-libs/gst/vaapi/gstvaapiwindow_wayland.c | 152 ++++++++++++++++++++-- gst-libs/gst/vaapi/meson.build | 18 ++- gst/vaapi/gstvaapisink.c | 15 +++ meson.build | 4 +- 8 files changed, 222 insertions(+), 19 deletions(-) diff --git a/configure.ac b/configure.ac index 0bdb5bb..cf382d7 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/gst-libs/gst/vaapi/Makefile.am b/gst-libs/gst/vaapi/Makefile.am index 976466e..50ca973 100644 --- a/gst-libs/gst/vaapi/Makefile.am +++ b/gst-libs/gst/vaapi/Makefile.am @@ -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 = \ diff --git a/gst-libs/gst/vaapi/gstvaapidisplay_wayland.c b/gst-libs/gst/vaapi/gstvaapidisplay_wayland.c index 9c40c90..660c99e 100644 --- a/gst-libs/gst/vaapi/gstvaapidisplay_wayland.c +++ b/gst-libs/gst/vaapi/gstvaapidisplay_wayland.c @@ -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); diff --git a/gst-libs/gst/vaapi/gstvaapidisplay_wayland_priv.h b/gst-libs/gst/vaapi/gstvaapidisplay_wayland_priv.h index 54bf9b1..b09adfb 100644 --- a/gst-libs/gst/vaapi/gstvaapidisplay_wayland_priv.h +++ b/gst-libs/gst/vaapi/gstvaapidisplay_wayland_priv.h @@ -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 #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; diff --git a/gst-libs/gst/vaapi/gstvaapiwindow_wayland.c b/gst-libs/gst/vaapi/gstvaapiwindow_wayland.c index f2efb89..adb8a75 100644 --- a/gst-libs/gst/vaapi/gstvaapiwindow_wayland.c +++ b/gst-libs/gst/vaapi/gstvaapiwindow_wayland.c @@ -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 diff --git a/gst-libs/gst/vaapi/meson.build b/gst-libs/gst/vaapi/meson.build index 6a289af..c5c40f7 100644 --- a/gst-libs/gst/vaapi/meson.build +++ b/gst-libs/gst/vaapi/meson.build @@ -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] diff --git a/gst/vaapi/gstvaapisink.c b/gst/vaapi/gstvaapisink.c index 158ade9..06d5d67 100644 --- a/gst/vaapi/gstvaapisink.c +++ b/gst/vaapi/gstvaapisink.c @@ -533,6 +533,17 @@ gst_vaapisink_backend_x11 (void) #include #include +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; } diff --git a/meson.build b/meson.build index af372a3..b32b774 100644 --- a/meson.build +++ b/meson.build @@ -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') -- 2.7.4