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]: 698dde1958
[XML spec]: https://github.com/wayland-project/wayland-protocols/blob/master/stable/xdg-shell/xdg-shell.xml
This commit is contained in:
Niels De Graef 2019-03-01 09:56:24 +01:00 committed by Niels De Graef
parent c461f22bef
commit 67bb17e4fa
9 changed files with 164 additions and 26 deletions

View file

@ -1319,7 +1319,7 @@ dnl **** Wayland ****
translit(dnm, m, l) AM_CONDITIONAL(USE_WAYLAND, true) translit(dnm, m, l) AM_CONDITIONAL(USE_WAYLAND, true)
AC_PATH_PROG([wayland_scanner], [wayland-scanner]) AC_PATH_PROG([wayland_scanner], [wayland-scanner])
AG_GST_CHECK_FEATURE(WAYLAND, [wayland sink], wayland , [ 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 if test "x$wayland_scanner" != "x"; then
HAVE_WAYLAND="yes" HAVE_WAYLAND="yes"
AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, `$PKG_CONFIG --variable=pkgdatadir wayland-protocols`) AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, `$PKG_CONFIG --variable=pkgdatadir wayland-protocols`)

View file

@ -6,7 +6,9 @@ BUILT_SOURCES = \
linux-dmabuf-unstable-v1-protocol.c \ linux-dmabuf-unstable-v1-protocol.c \
linux-dmabuf-unstable-v1-client-protocol.h \ linux-dmabuf-unstable-v1-client-protocol.h \
fullscreen-shell-unstable-v1-protocol.c \ 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 = \ libgstwaylandsink_la_SOURCES = \
gstwaylandsink.c \ gstwaylandsink.c \
@ -20,7 +22,8 @@ libgstwaylandsink_la_SOURCES = \
nodist_libgstwaylandsink_la_SOURCES = \ nodist_libgstwaylandsink_la_SOURCES = \
viewporter-protocol.c \ viewporter-protocol.c \
linux-dmabuf-unstable-v1-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 = \ libgstwaylandsink_la_CFLAGS = \
$(GST_PLUGINS_BAD_CFLAGS) \ $(GST_PLUGINS_BAD_CFLAGS) \

View file

@ -659,6 +659,16 @@ render_last_buffer (GstWaylandSink * sink, gboolean redraw)
gst_wl_window_render (sink->window, wlbuffer, info); 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 static GstFlowReturn
gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) 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 */ /* if we were not provided a window, create one ourselves */
sink->window = gst_wl_window_new_toplevel (sink->display, sink->window = gst_wl_window_new_toplevel (sink->display,
&sink->video_info, sink->fullscreen, &sink->render_lock); &sink->video_info, sink->fullscreen, &sink->render_lock);
g_signal_connect_object (sink->window, "closed",
G_CALLBACK (on_window_closed), sink, 0);
} }
} }

View file

@ -16,7 +16,8 @@ if use_wayland
['/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', ['/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml',
'linux-dmabuf-unstable-v1-protocol.c', 'linux-dmabuf-unstable-v1-client-protocol.h'], 'linux-dmabuf-unstable-v1-protocol.c', 'linux-dmabuf-unstable-v1-client-protocol.h'],
['/unstable/fullscreen-shell/fullscreen-shell-unstable-v1.xml', ['/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 = [] protocols_files = []

View file

@ -90,6 +90,9 @@ gst_wl_display_finalize (GObject * gobject)
if (self->wl_shell) if (self->wl_shell)
wl_shell_destroy (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) if (self->fullscreen_shell)
zwp_fullscreen_shell_v1_release (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; 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 static void
registry_handle_global (void *data, struct wl_registry *registry, registry_handle_global (void *data, struct wl_registry *registry,
uint32_t id, const char *interface, uint32_t version) 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); wl_registry_bind (registry, id, &wl_subcompositor_interface, 1);
} else if (g_strcmp0 (interface, "wl_shell") == 0) { } else if (g_strcmp0 (interface, "wl_shell") == 0) {
self->wl_shell = wl_registry_bind (registry, id, &wl_shell_interface, 1); 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) { } else if (g_strcmp0 (interface, "zwp_fullscreen_shell_v1") == 0) {
self->fullscreen_shell = wl_registry_bind (registry, id, self->fullscreen_shell = wl_registry_bind (registry, id,
&zwp_fullscreen_shell_v1_interface, 1); &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"); 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 /* 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. * In this case is correct to continue.
*/ */
g_warning ("Could not bind to wl_shell or zwp_fullscreen_shell, video " g_warning ("Could not bind to either wl_shell, xdg_wm_base or "
"display may not work properly."); "zwp_fullscreen_shell, video display may not work properly.");
} }
self->thread = g_thread_try_new ("GstWlDisplay", gst_wl_display_thread_run, self->thread = g_thread_try_new ("GstWlDisplay", gst_wl_display_thread_run,

View file

@ -55,6 +55,7 @@ struct _GstWlDisplay
struct wl_compositor *compositor; struct wl_compositor *compositor;
struct wl_subcompositor *subcompositor; struct wl_subcompositor *subcompositor;
struct wl_shell *wl_shell; struct wl_shell *wl_shell;
struct xdg_wm_base *xdg_wm_base;
struct zwp_fullscreen_shell_v1 *fullscreen_shell; struct zwp_fullscreen_shell_v1 *fullscreen_shell;
struct wl_shm *shm; struct wl_shm *shm;
struct wp_viewporter *viewporter; struct wp_viewporter *viewporter;

View file

@ -31,10 +31,69 @@
GST_DEBUG_CATEGORY_EXTERN (gstwayland_debug); GST_DEBUG_CATEGORY_EXTERN (gstwayland_debug);
#define GST_CAT_DEFAULT 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); G_DEFINE_TYPE (GstWlWindow, gst_wl_window, G_TYPE_OBJECT);
static void gst_wl_window_finalize (GObject * gobject); 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 static void
handle_ping (void *data, struct wl_shell_surface *wl_shell_surface, handle_ping (void *data, struct wl_shell_surface *wl_shell_surface,
uint32_t serial) uint32_t serial)
@ -74,6 +133,9 @@ gst_wl_window_class_init (GstWlWindowClass * klass)
{ {
GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = gst_wl_window_finalize; 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 static void
@ -89,6 +151,11 @@ gst_wl_window_finalize (GObject * gobject)
if (self->wl_shell_surface) if (self->wl_shell_surface)
wl_shell_surface_destroy (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) if (self->video_viewport)
wp_viewport_destroy (self->video_viewport); wp_viewport_destroy (self->video_viewport);
@ -157,12 +224,19 @@ gst_wl_window_ensure_fullscreen (GstWlWindow * window, gboolean fullscreen)
if (!window) if (!window)
return; return;
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) if (fullscreen)
wl_shell_surface_set_fullscreen (window->wl_shell_surface, wl_shell_surface_set_fullscreen (window->wl_shell_surface,
WL_SHELL_SURFACE_FULLSCREEN_METHOD_SCALE, 0, NULL); WL_SHELL_SURFACE_FULLSCREEN_METHOD_SCALE, 0, NULL);
else else
wl_shell_surface_set_toplevel (window->wl_shell_surface); wl_shell_surface_set_toplevel (window->wl_shell_surface);
} }
}
GstWlWindow * GstWlWindow *
gst_wl_window_new_toplevel (GstWlDisplay * display, const GstVideoInfo * info, gst_wl_window_new_toplevel (GstWlDisplay * display, const GstVideoInfo * info,
@ -173,28 +247,51 @@ gst_wl_window_new_toplevel (GstWlDisplay * display, const GstVideoInfo * info,
window = gst_wl_window_new_internal (display, render_lock); 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 */ /* go toplevel */
window->wl_shell_surface = wl_shell_get_shell_surface (display->wl_shell, window->wl_shell_surface = wl_shell_get_shell_surface (display->wl_shell,
window->area_surface); window->area_surface);
if (!window->wl_shell_surface) {
GST_ERROR ("Unable to get wl_shell_surface");
goto error;
}
if (window->wl_shell_surface) {
wl_shell_surface_add_listener (window->wl_shell_surface, wl_shell_surface_add_listener (window->wl_shell_surface,
&wl_shell_surface_listener, window); &wl_shell_surface_listener, window);
gst_wl_window_ensure_fullscreen (window, fullscreen); gst_wl_window_ensure_fullscreen (window, fullscreen);
} else {
GST_ERROR ("Unable to get wl_shell_surface");
g_object_unref (window);
return NULL;
}
} else if (display->fullscreen_shell) { } else if (display->fullscreen_shell) {
zwp_fullscreen_shell_v1_present_surface (display->fullscreen_shell, zwp_fullscreen_shell_v1_present_surface (display->fullscreen_shell,
window->area_surface, ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_ZOOM, window->area_surface, ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_ZOOM,
NULL); NULL);
} else { } else {
GST_ERROR ("Unable to use wl_shell or zwp_fullscreen_shell."); GST_ERROR ("Unable to use either wl_shell, xdg_wm_base or "
g_object_unref (window); "zwp_fullscreen_shell.");
return NULL; goto error;
} }
/* set the initial size to be the same as the reported video size */ /* 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); gst_wl_window_set_render_rectangle (window, 0, 0, width, info->height);
return window; return window;
error:
g_object_unref (window);
return NULL;
} }
GstWlWindow * GstWlWindow *

View file

@ -53,6 +53,8 @@ struct _GstWlWindow
struct wl_subsurface *video_subsurface; struct wl_subsurface *video_subsurface;
struct wp_viewport *video_viewport; struct wp_viewport *video_viewport;
struct wl_shell_surface *wl_shell_surface; 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 */ /* the size and position of the area_(sub)surface */
GstVideoRectangle render_rectangle; GstVideoRectangle render_rectangle;

View 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')) 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')) 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')) wl_protocol_dep = dependency('wayland-protocols', version: wl_req, required: get_option('wayland'))