/**
* @file xdg-shell.c
* @brief Desktop shell surface provider module for VLC media player
*/
/*****************************************************************************
* Copyright © 2014, 2017 Rémi Denis-Courmont
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <assert.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#include <wayland-client.h>
#include <wayland-cursor.h>
#ifdef XDG_SHELL
# include "xdg-shell-client-protocol.h"
# include "xdg-decoration-client-protocol.h"
#else
# define xdg_wm_base wl_shell
# define xdg_wm_base_add_listener(s, l, q) (void)0
# define xdg_wm_base_destroy wl_shell_destroy
# define xdg_wm_base_get_xdg_surface wl_shell_get_shell_surface
# define xdg_wm_base_pong wl_shell_pong
# define xdg_surface wl_shell_surface
# define xdg_surface_listener wl_shell_surface_listener
# define xdg_surface_add_listener wl_shell_surface_add_listener
# define xdg_surface_destroy wl_shell_surface_destroy
# define xdg_surface_get_toplevel(s) (s)
# define xdg_surface_set_window_geometry(s,x,y,w,h) (void)0
# define xdg_toplevel wl_shell_surface
# define xdg_toplevel_add_listener(s, l, q) (void)0
# define xdg_toplevel_destroy(s) (void)0
# define xdg_toplevel_set_title wl_shell_surface_set_title
# define xdg_toplevel_set_app_id(s, i) (void)0
# define xdg_toplevel_set_fullscreen(s, o) \
wl_shell_surface_set_fullscreen(s, 1, 0, o)
# define xdg_toplevel_unset_fullscreen wl_shell_surface_set_toplevel
#endif
#include <vlc_common.h>
#include <vlc_threads.h>
#include <vlc_plugin.h>
#include <vlc_window.h>
#include "input.h"
#include "output.h"
typedef struct
{
struct wl_registry *registry;
struct wl_compositor *compositor;
struct wl_shm *shm;
struct xdg_wm_base *wm_base;
struct xdg_surface *surface;
struct xdg_toplevel *toplevel;
#ifdef XDG_SHELL
struct zxdg_decoration_manager_v1 *deco_manager;
struct zxdg_toplevel_decoration_v1 *deco;
#endif
uint32_t default_output;
struct
{
unsigned width;
unsigned height;
} set;
struct
{
unsigned width;
unsigned height;
struct
{
unsigned width;
unsigned height;
bool fullscreen;
} latch;
vlc_sem_t configured;
} wm;
struct
{
unsigned width;
unsigned height;
} bounds;
struct output_list *outputs;
struct wl_list seats;
struct wl_cursor_theme *cursor_theme;
struct wl_cursor *cursor;
struct wl_surface *cursor_surface;
vlc_mutex_t lock;
vlc_thread_t thread;
} vout_window_sys_t;
static void cleanup_wl_display_read(void *data)
{
struct wl_display *display = data;
wl_display_cancel_read(display);
}
/** Background thread for Wayland shell events handling */
static void *Thread(void *data)
{
vlc_thread_set_name("vlc-wayland-evt");
vlc_window_t *wnd = data;
vout_window_sys_t *sys = wnd->sys;
struct wl_display *display = wnd->display.wl;
struct pollfd ufd[1];
int canc = vlc_savecancel();
vlc_cleanup_push(cleanup_wl_display_read, display);
ufd[0].fd = wl_display_get_fd(display);
ufd[0].events = POLLIN;
for (;;)
{
int timeout;
while (wl_display_prepare_read(display) != 0)
wl_display_dispatch_pending(display);
wl_display_flush(display);
timeout = seat_next_timeout(&sys->seats);
vlc_restorecancel(canc);
int val = poll(ufd, 1, timeout);
canc = vlc_savecancel();
if (val == 0)
seat_timeout(&sys->seats);
wl_display_read_events(display);
wl_display_dispatch_pending(display);
}
vlc_assert_unreachable();
vlc_cleanup_pop();
//vlc_restorecancel(canc);
//return NULL;
}
static void ResizeAck(vlc_window_t *wnd, unsigned width, unsigned height,
void *data)
{
#ifdef XDG_SHELL
vout_window_sys_t *sys = wnd->sys;
uint32_t *serial = data;
if (serial != NULL)
xdg_surface_ack_configure(sys->surface, *serial);
xdg_surface_set_window_geometry(sys->surface, 0, 0, width, height);
#else
(void) wnd; (void) width; (void) height; (void) data;
#endif
}
static void ReportSize(vlc_window_t *wnd, void *data)
{
vout_window_sys_t *sys = wnd->sys;
/* Zero wm.width or zero wm.height means the client should choose.
* DO NOT REPORT those values to video output... */
unsigned width, height;
if (sys->wm.width != 0 && sys->wm.height != 0)
{
width = sys->wm.width;
height = sys->wm.height;
}
else if (sys->bounds.width != 0 && sys->bounds.height != 0
&& (sys->bounds.width < sys->set.width
|| sys->bounds.height < sys->set.height))
{
width = sys->bounds.width;
height = sys->bounds.height;
}
else
{
width = sys->set.width;
height = sys->set.height;
}
wnd->owner.cbs->resized(wnd, width, height, ResizeAck, data);
}
static void Resize(vlc_window_t *wnd, unsigned width, unsigned height)
{
vout_window_sys_t *sys = wnd->sys;
#ifdef XDG_SHELL
/* The minimum size must be smaller or equal to the maximum size
* at _all_ times. This gets a bit cumbersome. */
xdg_toplevel_set_min_size(sys->toplevel, 0, 0);
xdg_toplevel_set_max_size(sys->toplevel, width, height);
xdg_toplevel_set_min_size(sys->toplevel, width, height);
#endif
vlc_mutex_lock(&sys->lock);
sys->set.width = width;
sys->set.height = height;
ReportSize(wnd, NULL);
vlc_mutex_unlock(&sys->lock);
wl_display_flush(wnd->display.wl);
}
static void Close(vlc_window_t *);
static void UnsetFullscreen(vlc_window_t *wnd)
{
vout_window_sys_t *sys = wnd->sys;
xdg_toplevel_unset_fullscreen(sys->toplevel);
wl_display_flush(wnd->display.wl);
}
static void SetFullscreen(vlc_window_t *wnd, const char *idstr)
{
vout_window_sys_t *sys = wnd->sys;
struct wl_output *output = NULL;
if (idstr != NULL)
output = output_find_by_name(sys->outputs, idstr);
else
if (sys->default_output != 0)
output = output_find_by_id(sys->outputs, sys->default_output);
xdg_toplevel_set_fullscreen(sys->toplevel, output);
wl_display_flush(wnd->display.wl);
}
#ifdef XDG_SHELL
static void SetDecoration(vlc_window_t *wnd, bool decorated)
{
vout_window_sys_t *sys = wnd->sys;
const uint_fast32_t deco_mode = decorated
? ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE
: ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
if (sys->deco != NULL)
zxdg_toplevel_decoration_v1_set_mode(sys->deco, deco_mode);
else
if (deco_mode != ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE)
msg_Err(wnd, "server-side decoration not supported");
}
#endif
static int Enable(vlc_window_t *wnd, const vlc_window_cfg_t *restrict cfg)
{
vout_window_sys_t *sys = wnd->sys;
struct wl_display *display = wnd->display.wl;
if (cfg->is_fullscreen)
xdg_toplevel_set_fullscreen(sys->toplevel, NULL);
else
xdg_toplevel_unset_fullscreen(sys->toplevel);
#ifdef XDG_SHELL
SetDecoration(wnd, cfg->is_decorated);
#else
if (cfg->is_decorated)
return VLC_EGENERIC;
#endif
Resize(wnd, cfg->width, cfg->height);
wl_surface_commit(wnd->handle.wl);
wl_display_flush(display);
#ifdef XDG_SHELL
vlc_sem_wait(&sys->wm.configured);
#endif
return VLC_SUCCESS;
}
static const struct vlc_window_operations ops = {
.enable = Enable,
.resize = Resize,
.destroy = Close,
.unset_fullscreen = UnsetFullscreen,
.set_fullscreen = SetFullscreen,
};
#ifdef XDG_SHELL
static void xdg_toplevel_configure_cb(void *data,
struct xdg_toplevel *toplevel,
int32_t width, int32_t height,
struct wl_array *states)
{
vlc_window_t *wnd = data;
vout_window_sys_t *sys = wnd->sys;
const uint32_t *state;
msg_Dbg(wnd, "new configuration: %"PRId32"x%"PRId32, width, height);
sys->wm.latch.width = width;
sys->wm.latch.height = height;
sys->wm.latch.fullscreen = false;
wl_array_for_each(state, states)
{
msg_Dbg(wnd, " - state 0x%04"PRIX32, *state);
switch (*state)
{
case XDG_TOPLEVEL_STATE_FULLSCREEN:
sys->wm.latch.fullscreen = true;
break;
}
}
(void) toplevel;
}
static void xdg_toplevel_close_cb(void *data, struct xdg_toplevel *toplevel)
{
vlc_window_t *wnd = data;
vlc_window_ReportClose(wnd);
(void) toplevel;
}
#ifdef XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION
static void xdg_toplevel_configure_bounds_cb(
void *data, struct xdg_toplevel* toplevel,
int32_t width, int32_t height
){
// TODO for tiling window managers
vlc_window_t *wnd = data;
vout_window_sys_t *sys = wnd->sys;
msg_Dbg(wnd, "window configure bounds: %" PRId32 "x%" PRId32,
width, height);
(void)toplevel;
sys->bounds.width = width;
sys->bounds.height = height;
}
#endif
static const struct xdg_toplevel_listener xdg_toplevel_cbs =
{
xdg_toplevel_configure_cb,
xdg_toplevel_close_cb,
#ifdef XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION
xdg_toplevel_configure_bounds_cb,
#endif
#ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION
NULL, /* We don't support wm_capabilities for now */
#endif
};
static void xdg_surface_configure_cb(void *data, struct xdg_surface *surface,
uint32_t serial)
{
vlc_window_t *wnd = data;
vout_window_sys_t *sys = wnd->sys;
if (sys->wm.latch.fullscreen)
vlc_window_ReportFullscreen(wnd, NULL);
else
vlc_window_ReportWindowed(wnd);
vlc_mutex_lock(&sys->lock);
sys->wm.width = sys->wm.latch.width;
sys->wm.height = sys->wm.latch.height;
ReportSize(wnd, &serial);
vlc_mutex_unlock(&sys->lock);
vlc_sem_post(&sys->wm.configured);
(void) surface;
}
static const struct xdg_surface_listener xdg_surface_cbs =
{
xdg_surface_configure_cb,
};
static void xdg_wm_base_ping_cb(void *data, struct xdg_wm_base *wm_base,
uint32_t serial)
{
(void) data;
xdg_wm_base_pong(wm_base, serial);
}
static const struct xdg_wm_base_listener xdg_wm_base_cbs =
{
xdg_wm_base_ping_cb,
};
static void xdg_toplevel_decoration_configure_cb(void *data,
struct zxdg_toplevel_decoration_v1 *deco,
uint32_t mode)
{
vlc_window_t *wnd = data;
msg_Dbg(wnd, "new decoration mode: %"PRIu32, mode);
(void) deco;
}
static const struct zxdg_toplevel_decoration_v1_listener
xdg_toplevel_decoration_cbs =
{
xdg_toplevel_decoration_configure_cb,
};
#else
static void wl_shell_surface_configure_cb(void *data,
struct wl_shell_surface *toplevel,
uint32_t edges,
int32_t width, int32_t height)
{
vlc_window_t *wnd = data;
msg_Dbg(wnd, "new configuration: %"PRId32"x%"PRId32, width, height);
vlc_window_ReportSize(wnd, width, height);
(void) toplevel; (void) edges;
}
static void wl_shell_surface_ping_cb(void *data,
struct wl_shell_surface *surface,
uint32_t serial)
{
(void) data;
wl_shell_surface_pong(surface, serial);
}
static void wl_shell_surface_popup_done_cb(void *data,
struct wl_shell_surface *surface)
{
(void) data; (void) surface;
}
static const struct wl_shell_surface_listener wl_shell_surface_cbs =
{
wl_shell_surface_ping_cb,
wl_shell_surface_configure_cb,
wl_shell_surface_popup_done_cb,
};
#define xdg_surface_cbs wl_shell_surface_cbs
#endif
static void register_wl_compositor(void *data, struct wl_registry *registry,
uint32_t name, uint32_t vers)
{
vlc_window_t *wnd = data;
vout_window_sys_t *sys = wnd->sys;
if (likely(sys->compositor == NULL))
sys->compositor = wl_registry_bind(registry, name,
&wl_compositor_interface, vers);
}
static void register_wl_output(void *data, struct wl_registry *registry,
uint32_t name, uint32_t vers)
{
vlc_window_t *wnd = data;
vout_window_sys_t *sys = wnd->sys;
output_create(sys->outputs, registry, name, vers);
}
static void register_wl_seat(void *data, struct wl_registry *registry,
uint32_t name, uint32_t vers)
{
vlc_window_t *wnd = data;
vout_window_sys_t *sys = wnd->sys;
seat_create(wnd, registry, name, vers, &sys->seats);
}
#ifndef XDG_SHELL
static void register_wl_shell(void *data, struct wl_registry *registry,
uint32_t name, uint32_t vers)
{
vlc_window_t *wnd = data;
vout_window_sys_t *sys = wnd->sys;
if (likely(sys->wm_base == NULL))
sys->wm_base = wl_registry_bind(registry, name, &wl_shell_interface,
vers);
}
#endif
static void register_wl_shm(void *data, struct wl_registry *registry,
uint32_t name, uint32_t vers)
{
vlc_window_t *wnd = data;
vout_window_sys_t *sys = wnd->sys;
if (likely(sys->shm == NULL))
sys->shm = wl_registry_bind(registry, name, &wl_shm_interface, vers);
}
#ifdef XDG_SHELL
static void register_xdg_wm_base(void *data, struct wl_registry *registry,
uint32_t name, uint32_t vers)
{
vlc_window_t *wnd = data;
vout_window_sys_t *sys = wnd->sys;
if (likely(sys->wm_base == NULL))
sys->wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface,
vers);
}
static void register_xdg_decoration_manager(void *data,
struct wl_registry *registry,
uint32_t name, uint32_t vers)
{
vlc_window_t *wnd = data;
vout_window_sys_t *sys = wnd->sys;
if (likely(sys->deco_manager == NULL))
sys->deco_manager = wl_registry_bind(registry, name,
&zxdg_decoration_manager_v1_interface, vers);
}
#endif
struct registry_handler
{
const char *iface;
void (*global)(void *, struct wl_registry *, uint32_t, uint32_t);
uint32_t max_version;
};
#ifdef XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION
#define VLC_XDG_WM_BASE_VERSION XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION
#else
#define VLC_XDG_WM_BASE_VERSION 1
#endif
static const struct registry_handler global_handlers[] =
{
{ "wl_compositor", register_wl_compositor, 2 },
{ "wl_output", register_wl_output, 4 },
{ "wl_seat", register_wl_seat, UINT32_C(-1) },
#ifndef XDG_SHELL
{ "wl_shell", register_wl_shell, 1 },
#endif
{ "wl_shm", register_wl_shm, 1 },
#ifdef XDG_SHELL
{ "xdg_wm_base", register_xdg_wm_base, VLC_XDG_WM_BASE_VERSION },
{ "zxdg_decoration_manager_v1", register_xdg_decoration_manager, 1 },
#endif
};
static int rghcmp(const void *a, const void *b)
{
const char *iface = a;
const struct registry_handler *handler = b;
return strcmp(iface, handler->iface);
}
static void registry_global_cb(void *data, struct wl_registry *registry,
uint32_t name, const char *iface, uint32_t vers)
{
vlc_window_t *wnd = data;
const struct registry_handler *h;
msg_Dbg(wnd, "global %3"PRIu32": %s version %"PRIu32, name, iface, vers);
h = bsearch(iface, global_handlers, ARRAY_SIZE(global_handlers),
sizeof (*h), rghcmp);
if (h != NULL)
{
uint32_t version = (vers < h->max_version) ? vers : h->max_version;
h->global(data, registry, name, version);
}
}
static void registry_global_remove_cb(void *data, struct wl_registry *registry,
uint32_t name)
{
vlc_window_t *wnd = data;
vout_window_sys_t *sys = wnd->sys;
msg_Dbg(wnd, "global remove %3"PRIu32, name);
if (seat_destroy_one(&sys->seats, name) == 0)
return;
struct wl_output *output = output_find_by_id(sys->outputs, name);
if (output != NULL) {
output_destroy(sys->outputs, output);
return;
}
(void) registry;
}
static const struct wl_registry_listener registry_cbs =
{
registry_global_cb,
registry_global_remove_cb,
};
struct wl_surface *window_get_cursor(vlc_window_t *wnd, int32_t *restrict hsx,
int32_t *restrict hsy)
{
vout_window_sys_t *sys = wnd->sys;
if (unlikely(sys->cursor == NULL))
return NULL;
assert(sys->cursor->image_count > 0);
/* TODO? animated cursor (more than one image) */
struct wl_cursor_image *img = sys->cursor->images[0];
struct wl_surface *surface = sys->cursor_surface;
if (likely(surface != NULL))
{
wl_surface_attach(surface, wl_cursor_image_get_buffer(img), 0, 0);
wl_surface_damage(surface, 0, 0, img->width, img->height);
wl_surface_commit(surface);
}
*hsx = img->hotspot_x;
*hsy = img->hotspot_y;
return surface;
}
/**
* Creates a Wayland shell surface.
*/
static int Open(vlc_window_t *wnd)
{
vout_window_sys_t *sys = malloc(sizeof (*sys));
if (unlikely(sys == NULL))
return VLC_ENOMEM;
sys->compositor = NULL;
sys->shm = NULL;
sys->wm_base = NULL;
sys->surface = NULL;
sys->toplevel = NULL;
sys->cursor_theme = NULL;
sys->cursor = NULL;
#ifdef XDG_SHELL
sys->deco_manager = NULL;
sys->deco = NULL;
#endif
sys->default_output = var_InheritInteger(wnd, "wl-output");
sys->wm.width = 0;
sys->wm.height = 0;
sys->wm.latch.width = 0;
sys->wm.latch.height = 0;
sys->wm.latch.fullscreen = false;
sys->set.width = 0;
sys->set.height = 0;
sys->bounds.width = 0;
sys->bounds.height = 0;
sys->outputs = output_list_create(wnd);
wl_list_init(&sys->seats);
sys->cursor_theme = NULL;
sys->cursor_surface = NULL;
vlc_mutex_init(&sys->lock);
vlc_sem_init(&sys->wm.configured, 0);
wnd->sys = sys;
wnd->handle.wl = NULL;
/* Connect to the display server */
char *dpy_name = var_InheritString(wnd, "wl-display");
struct wl_display *display = wl_display_connect(dpy_name);
free(dpy_name);
if (display == NULL)
{
output_list_destroy(sys->outputs);
free(sys);
return VLC_EGENERIC;
}
/* Find the interesting singleton(s) */
sys->registry = wl_display_get_registry(display);
if (sys->registry == NULL)
goto error;
wl_registry_add_listener(sys->registry, ®istry_cbs, wnd);
wl_display_roundtrip(display); /* complete registry enumeration */
if (sys->compositor == NULL || sys->wm_base == NULL)
goto error;
/* Create a surface */
struct wl_surface *surface = wl_compositor_create_surface(sys->compositor);
if (surface == NULL)
goto error;
xdg_wm_base_add_listener(sys->wm_base, &xdg_wm_base_cbs, NULL);
struct xdg_surface *xdg_surface =
xdg_wm_base_get_xdg_surface(sys->wm_base, surface);
if (xdg_surface == NULL)
goto error;
sys->surface = xdg_surface;
xdg_surface_add_listener(xdg_surface, &xdg_surface_cbs, wnd);
struct xdg_toplevel *toplevel = xdg_surface_get_toplevel(xdg_surface);
if (toplevel == NULL)
goto error;
sys->toplevel = toplevel;
xdg_toplevel_add_listener(toplevel, &xdg_toplevel_cbs, wnd);
char *title = var_InheritString(wnd, "video-title");
xdg_toplevel_set_title(toplevel,
(title != NULL) ? title : _("VLC media player"));
free(title);
char *app_id = var_InheritString(wnd, "app-id");
if (app_id != NULL)
{
xdg_toplevel_set_app_id(toplevel, app_id);
free(app_id);
}
if (sys->shm != NULL)
{
sys->cursor_theme = wl_cursor_theme_load(NULL, 32, sys->shm);
if (sys->cursor_theme != NULL)
sys->cursor = wl_cursor_theme_get_cursor(sys->cursor_theme,
"left_ptr");
sys->cursor_surface = wl_compositor_create_surface(sys->compositor);
}
if (sys->cursor == NULL)
msg_Err(wnd, "failed to load cursor");
#ifdef XDG_SHELL
if (sys->deco_manager != NULL)
sys->deco = zxdg_decoration_manager_v1_get_toplevel_decoration(
sys->deco_manager, toplevel);
if (sys->deco != NULL)
zxdg_toplevel_decoration_v1_add_listener(sys->deco,
&xdg_toplevel_decoration_cbs,
wnd);
#endif
wnd->type = VLC_WINDOW_TYPE_WAYLAND;
wnd->handle.wl = surface;
wnd->display.wl = display;
wnd->ops = &ops;
if (vlc_clone(&sys->thread, Thread, wnd))
goto error;
return VLC_SUCCESS;
error:
seat_destroy_all(&sys->seats);
output_list_destroy(sys->outputs);
#ifdef XDG_SHELL
if (sys->deco != NULL)
zxdg_toplevel_decoration_v1_destroy(sys->deco);
if (sys->deco_manager != NULL)
zxdg_decoration_manager_v1_destroy(sys->deco_manager);
#endif
if (sys->cursor_surface != NULL)
wl_surface_destroy(sys->cursor_surface);
if (sys->cursor_theme != NULL)
wl_cursor_theme_destroy(sys->cursor_theme);
if (sys->toplevel != NULL)
xdg_toplevel_destroy(sys->toplevel);
if (sys->surface != NULL)
xdg_surface_destroy(sys->surface);
if (sys->wm_base != NULL)
xdg_wm_base_destroy(sys->wm_base);
if (wnd->handle.wl != NULL)
wl_surface_destroy(wnd->handle.wl);
if (sys->shm != NULL)
wl_shm_destroy(sys->shm);
if (sys->compositor != NULL)
wl_compositor_destroy(sys->compositor);
if (sys->registry != NULL)
wl_registry_destroy(sys->registry);
wl_display_disconnect(display);
free(sys);
return VLC_EGENERIC;
}
/**
* Destroys a XDG shell surface.
*/
static void Close(vlc_window_t *wnd)
{
vout_window_sys_t *sys = wnd->sys;
vlc_cancel(sys->thread);
vlc_join(sys->thread, NULL);
seat_destroy_all(&sys->seats);
output_list_destroy(sys->outputs);
#ifdef XDG_SHELL
if (sys->deco != NULL)
zxdg_toplevel_decoration_v1_destroy(sys->deco);
if (sys->deco_manager != NULL)
zxdg_decoration_manager_v1_destroy(sys->deco_manager);
#endif
if (sys->cursor_surface != NULL)
wl_surface_destroy(sys->cursor_surface);
if (sys->cursor_theme != NULL)
wl_cursor_theme_destroy(sys->cursor_theme);
xdg_toplevel_destroy(sys->toplevel);
xdg_surface_destroy(sys->surface);
xdg_wm_base_destroy(sys->wm_base);
wl_surface_destroy(wnd->handle.wl);
if (sys->shm != NULL)
wl_shm_destroy(sys->shm);
wl_compositor_destroy(sys->compositor);
wl_registry_destroy(sys->registry);
wl_display_disconnect(wnd->display.wl);
free(sys);
}
#define DISPLAY_TEXT N_("Wayland display")
#define DISPLAY_LONGTEXT N_( \
"Video will be rendered with this Wayland display. " \
"If empty, the default display will be used.")
#define OUTPUT_TEXT N_("Fullscreen output")
#define OUTPUT_LONGTEXT N_( \
"Fullscreen mode with use the output with this name by default.")
vlc_module_begin()
#ifdef XDG_SHELL
set_shortname(N_("XDG shell"))
set_description(N_("XDG shell surface"))
#else
set_shortname(N_("WL shell"))
set_description(N_("Wayland shell surface"))
#endif
set_subcategory(SUBCAT_VIDEO_VOUT)
#ifdef XDG_SHELL
set_capability("vout window", 20)
#else
set_capability("vout window", 10)
#endif
set_callback(Open)
add_string("wl-display", NULL, DISPLAY_TEXT, DISPLAY_LONGTEXT)
add_integer("wl-output", 0, OUTPUT_TEXT, OUTPUT_LONGTEXT)
change_integer_range(0, UINT32_MAX)
change_volatile()
vlc_module_end()
¤ Dauer der Verarbeitung: 0.9 Sekunden
¤
*© Formatika GbR, Deutschland
|
|