From 68f9ee7e0b3fdddfa42fa11a15d9ae84460d5e19 Mon Sep 17 00:00:00 2001 From: Rostislav Pehlivanov Date: Sun, 1 Oct 2017 21:16:49 +0100 Subject: wayland_common: rewrite from scratch The wayland code was written more than 4 years ago when wayland wasn't even at version 1.0. This commit rewrites everything in a more modern way, switches to using the new xdg v6 shell interface which solves a lot of bugs and makes mpv tiling-friedly, adds support for drag and drop, adds support for touchscreens, adds support for KDE's server decorations protocol, and finally adds support for the new idle-inhibitor protocol. It does not yet use the frame callback as a main rendering loop driver, this will happen with a later commit. --- video/out/opengl/context_wayland.c | 193 ++-- video/out/vo.h | 2 +- video/out/vulkan/context_wayland.c | 73 +- video/out/wayland/server-decoration.xml | 94 ++ video/out/wayland_common.c | 1720 +++++++++++++++++-------------- video/out/wayland_common.h | 180 ++-- waftools/checks/custom.py | 11 +- waftools/checks/generic.py | 8 +- waftools/generators/sources.py | 26 +- wscript | 9 + wscript_build.py | 23 + 11 files changed, 1281 insertions(+), 1058 deletions(-) create mode 100644 video/out/wayland/server-decoration.xml diff --git a/video/out/opengl/context_wayland.c b/video/out/opengl/context_wayland.c index 6ddc550306..2d3c22abc1 100644 --- a/video/out/opengl/context_wayland.c +++ b/video/out/opengl/context_wayland.c @@ -16,197 +16,170 @@ * License along with mpv. If not, see . */ +#include +#include +#include + #include "video/out/wayland_common.h" #include "context.h" #include "egl_helpers.h" #include "utils.h" -static void egl_resize(struct vo_wayland_state *wl) -{ - int32_t x = wl->window.sh_x; - int32_t y = wl->window.sh_y; - int32_t width = wl->window.sh_width; - int32_t height = wl->window.sh_height; - int32_t scale = 1; - - if (!wl->egl_context.egl_window) - return; - - if (wl->display.current_output) - scale = wl->display.current_output->scale; - - // get the real size of the window - // this improves moving the window while resizing it - wl_egl_window_get_attached_size(wl->egl_context.egl_window, - &wl->window.width, - &wl->window.height); - - MP_VERBOSE(wl, "resizing %dx%d -> %dx%d\n", wl->window.width, - wl->window.height, - width, - height); +struct priv { + GL gl; + EGLDisplay egl_display; + EGLContext egl_context; + EGLSurface egl_surface; + EGLConfig egl_config; + struct wl_egl_window *egl_window; +}; - if (x != 0) - x = wl->window.width - width; +static void resize(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv; + struct vo_wayland_state *wl = ctx->vo->wl; - if (y != 0) - y = wl->window.height - height; + MP_VERBOSE(wl, "Handling resizing on the egl side\n"); - wl_surface_set_buffer_scale(wl->window.video_surface, scale); - wl_egl_window_resize(wl->egl_context.egl_window, scale*width, scale*height, x, y); + const int32_t width = wl->scaling*mp_rect_w(wl->geometry); + const int32_t height = wl->scaling*mp_rect_h(wl->geometry); - wl->window.width = width; - wl->window.height = height; + wl_surface_set_buffer_scale(wl->surface, wl->scaling); + wl_egl_window_resize(p->egl_window, width, height, 0, 0); - /* set size for mplayer */ - wl->vo->dwidth = scale*wl->window.width; - wl->vo->dheight = scale*wl->window.height; - wl->vo->want_redraw = true; + wl->vo->dwidth = width; + wl->vo->dheight = height; } -static void waylandgl_swap_buffers(struct ra_ctx *ctx) +static void wayland_egl_swap_buffers(struct ra_ctx *ctx) { - struct vo_wayland_state *wl = ctx->vo->wayland; - vo_wayland_wait_events(ctx->vo, 0); - eglSwapBuffers(wl->egl_context.egl.dpy, wl->egl_context.egl_surface); + struct priv *p = ctx->priv; + eglSwapBuffers(p->egl_display, p->egl_surface); } -static bool egl_create_context(struct ra_ctx *ctx, struct vo_wayland_state *wl) +static bool egl_create_context(struct ra_ctx *ctx) { - GL *gl = ctx->priv = talloc_zero(ctx, GL); + struct priv *p = ctx->priv = talloc_zero(ctx, struct priv); + struct vo_wayland_state *wl = ctx->vo->wl; - if (!(wl->egl_context.egl.dpy = eglGetDisplay(wl->display.display))) + if (!(p->egl_display = eglGetDisplay(wl->display))) return false; - if (eglInitialize(wl->egl_context.egl.dpy, NULL, NULL) != EGL_TRUE) + if (eglInitialize(p->egl_display, NULL, NULL) != EGL_TRUE) return false; - if (!mpegl_create_context(ctx, wl->egl_context.egl.dpy, - &wl->egl_context.egl.ctx, - &wl->egl_context.egl.conf)) + if (!mpegl_create_context(ctx, p->egl_display, &p->egl_context, + &p->egl_config)) return false; - eglMakeCurrent(wl->egl_context.egl.dpy, NULL, NULL, wl->egl_context.egl.ctx); + eglMakeCurrent(p->egl_display, NULL, NULL, p->egl_context); - mpegl_load_functions(gl, wl->log); + mpegl_load_functions(&p->gl, wl->log); struct ra_gl_ctx_params params = { - .swap_buffers = waylandgl_swap_buffers, + .swap_buffers = wayland_egl_swap_buffers, .native_display_type = "wl", - .native_display = wl->display.display, + .native_display = wl->display, }; - if (!ra_gl_ctx_init(ctx, gl, params)) + if (!ra_gl_ctx_init(ctx, &p->gl, params)) return false; + vo_wayland_set_cb_exec(ctx->vo, NULL, NULL); + return true; } -static void egl_create_window(struct vo_wayland_state *wl) +static void egl_create_window(struct ra_ctx *ctx) { - wl->egl_context.egl_window = wl_egl_window_create(wl->window.video_surface, - wl->window.width, - wl->window.height); - - wl->egl_context.egl_surface = eglCreateWindowSurface(wl->egl_context.egl.dpy, - wl->egl_context.egl.conf, - wl->egl_context.egl_window, - NULL); - - eglMakeCurrent(wl->egl_context.egl.dpy, - wl->egl_context.egl_surface, - wl->egl_context.egl_surface, - wl->egl_context.egl.ctx); - - wl_display_dispatch_pending(wl->display.display); - - /** - * - * - * The main change is that if the swap interval is 0 then Mesa won't install a - * frame callback so that eglSwapBuffers can be executed as often as necessary. - * Instead it will do a sync request after the swap buffers. It will block for - * sync complete event in get_back_bo instead of the frame callback. The - * compositor is likely to send a release event while processing the new buffer - * attach and this makes sure we will receive that before deciding whether to - * allocate a new buffer. - */ - - eglSwapInterval(wl->egl_context.egl.dpy, 0); + struct priv *p = ctx->priv; + struct vo_wayland_state *wl = ctx->vo->wl; + + p->egl_window = wl_egl_window_create(wl->surface, mp_rect_w(wl->geometry), + mp_rect_h(wl->geometry)); + + p->egl_surface = eglCreateWindowSurface(p->egl_display, p->egl_config, + p->egl_window, NULL); + + eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface, p->egl_context); + + eglSwapInterval(p->egl_display, 0); + + wl_display_roundtrip(wl->display); } -static bool waylandgl_reconfig(struct ra_ctx *ctx) +static bool wayland_egl_reconfig(struct ra_ctx *ctx) { - struct vo_wayland_state * wl = ctx->vo->wayland; + struct priv *p = ctx->priv; - if (!vo_wayland_config(ctx->vo)) + if (!vo_wayland_reconfig(ctx->vo)) return false; - if (!wl->egl_context.egl_window) - egl_create_window(wl); + if (!p->egl_window) + egl_create_window(ctx); return true; } -static void waylandgl_uninit(struct ra_ctx *ctx) +static void wayland_egl_uninit(struct ra_ctx *ctx) { - struct vo_wayland_state *wl = ctx->vo->wayland; + struct priv *p = ctx->priv; ra_gl_ctx_uninit(ctx); - if (wl->egl_context.egl.ctx) { + if (p->egl_context) { eglReleaseThread(); - if (wl->egl_context.egl_window) - wl_egl_window_destroy(wl->egl_context.egl_window); - eglDestroySurface(wl->egl_context.egl.dpy, wl->egl_context.egl_surface); - eglMakeCurrent(wl->egl_context.egl.dpy, NULL, NULL, EGL_NO_CONTEXT); - eglDestroyContext(wl->egl_context.egl.dpy, wl->egl_context.egl.ctx); + if (p->egl_window) + wl_egl_window_destroy(p->egl_window); + eglDestroySurface(p->egl_display, p->egl_surface); + eglMakeCurrent(p->egl_display, NULL, NULL, EGL_NO_CONTEXT); + eglDestroyContext(p->egl_display, p->egl_context); + p->egl_context = NULL; } - eglTerminate(wl->egl_context.egl.dpy); - wl->egl_context.egl.ctx = NULL; + eglTerminate(p->egl_display); vo_wayland_uninit(ctx->vo); } -static int waylandgl_control(struct ra_ctx *ctx, int *events, int request, +static int wayland_egl_control(struct ra_ctx *ctx, int *events, int request, void *data) { - struct vo_wayland_state *wl = ctx->vo->wayland; + struct vo_wayland_state *wl = ctx->vo->wl; int r = vo_wayland_control(ctx->vo, events, request, data); if (*events & VO_EVENT_RESIZE) { - egl_resize(wl); + resize(ctx); ra_gl_ctx_resize(ctx->swapchain, wl->vo->dwidth, wl->vo->dheight, 0); } return r; } -static void wayland_wakeup(struct ra_ctx *ctx) +static void wayland_egl_wakeup(struct ra_ctx *ctx) { vo_wayland_wakeup(ctx->vo); } -static void wayland_wait_events(struct ra_ctx *ctx, int64_t until_time_us) +static void wayland_egl_wait_events(struct ra_ctx *ctx, int64_t until_time_us) { vo_wayland_wait_events(ctx->vo, until_time_us); } -static bool waylandgl_init(struct ra_ctx *ctx) +static bool wayland_egl_init(struct ra_ctx *ctx) { if (!vo_wayland_init(ctx->vo)) return false; - return egl_create_context(ctx, ctx->vo->wayland); + return egl_create_context(ctx); } const struct ra_ctx_fns ra_ctx_wayland_egl = { .type = "opengl", .name = "wayland", - .reconfig = waylandgl_reconfig, - .control = waylandgl_control, - .wakeup = wayland_wakeup, - .wait_events = wayland_wait_events, - .init = waylandgl_init, - .uninit = waylandgl_uninit, + .reconfig = wayland_egl_reconfig, + .control = wayland_egl_control, + .wakeup = wayland_egl_wakeup, + .wait_events = wayland_egl_wait_events, + .init = wayland_egl_init, + .uninit = wayland_egl_uninit, }; diff --git a/video/out/vo.h b/video/out/vo.h index 2a0c3ef626..50f6e4942e 100644 --- a/video/out/vo.h +++ b/video/out/vo.h @@ -374,7 +374,7 @@ struct vo { struct vo_x11_state *x11; struct vo_w32_state *w32; struct vo_cocoa_state *cocoa; - struct vo_wayland_state *wayland; + struct vo_wayland_state *wl; struct mp_hwdec_devices *hwdec_devs; struct input_ctx *input_ctx; struct osd_state *osd; diff --git a/video/out/vulkan/context_wayland.c b/video/out/vulkan/context_wayland.c index a77f17a4f2..67a3383ca3 100644 --- a/video/out/vulkan/context_wayland.c +++ b/video/out/vulkan/context_wayland.c @@ -26,7 +26,7 @@ struct priv { struct mpvk_ctx vk; }; -static void wayland_uninit(struct ra_ctx *ctx) +static void wayland_vk_uninit(struct ra_ctx *ctx) { struct priv *p = ctx->priv; @@ -35,7 +35,7 @@ static void wayland_uninit(struct ra_ctx *ctx) vo_wayland_uninit(ctx->vo); } -static bool wayland_init(struct ra_ctx *ctx) +static bool wayland_vk_init(struct ra_ctx *ctx) { struct priv *p = ctx->priv = talloc_zero(ctx, struct priv); struct mpvk_ctx *vk = &p->vk; @@ -48,13 +48,10 @@ static bool wayland_init(struct ra_ctx *ctx) if (!vo_wayland_init(ctx->vo)) goto error; - if (!vo_wayland_config(ctx->vo)) - goto error; - VkWaylandSurfaceCreateInfoKHR wlinfo = { .sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, - .display = ctx->vo->wayland->display.display, - .surface = ctx->vo->wayland->window.video_surface, + .display = ctx->vo->wl->display, + .surface = ctx->vo->wl->surface, }; VkResult res = vkCreateWaylandSurfaceKHR(vk->inst, &wlinfo, MPVK_ALLOCATOR, @@ -73,63 +70,55 @@ static bool wayland_init(struct ra_ctx *ctx) if (!ra_vk_ctx_init(ctx, vk, VK_PRESENT_MODE_MAILBOX_KHR)) goto error; + vo_wayland_set_cb_exec(ctx->vo, NULL, NULL); + return true; error: - wayland_uninit(ctx); + wayland_vk_uninit(ctx); return false; } -static bool resize(struct ra_ctx *ctx) +static void resize(struct ra_ctx *ctx) { - struct vo_wayland_state *wl = ctx->vo->wayland; - int32_t width = wl->window.sh_width; - int32_t height = wl->window.sh_height; - int32_t scale = 1; - - if (wl->display.current_output) - scale = wl->display.current_output->scale; + struct vo_wayland_state *wl = ctx->vo->wl; - MP_VERBOSE(wl, "resizing %dx%d -> %dx%d\n", wl->window.width, - wl->window.height, - width, - height); + MP_VERBOSE(wl, "Handling resizing on the vk side\n"); - wl_surface_set_buffer_scale(wl->window.video_surface, scale); - int err = ra_vk_ctx_resize(ctx->swapchain, scale*width, scale*height); + const int32_t width = wl->scaling*mp_rect_w(wl->geometry); + const int32_t height = wl->scaling*mp_rect_h(wl->geometry); - wl->window.width = width; - wl->window.height = height; + wl_surface_set_buffer_scale(wl->surface, wl->scaling); - wl->vo->dwidth = scale*wl->window.width; - wl->vo->dheight = scale*wl->window.height; - wl->vo->want_redraw = true; - - return err; + wl->vo->dwidth = width; + wl->vo->dheight = height; } -static bool wayland_reconfig(struct ra_ctx *ctx) +static bool wayland_vk_reconfig(struct ra_ctx *ctx) { - vo_wayland_config(ctx->vo); - return resize(ctx); + if (!vo_wayland_reconfig(ctx->vo)) + return false; + + return true; } -static int wayland_control(struct ra_ctx *ctx, int *events, int request, void *arg) +static int wayland_vk_control(struct ra_ctx *ctx, int *events, int request, void *arg) { int ret = vo_wayland_control(ctx->vo, events, request, arg); if (*events & VO_EVENT_RESIZE) { - if (!resize(ctx)) + resize(ctx); + if (ra_vk_ctx_resize(ctx->swapchain, ctx->vo->dwidth, ctx->vo->dheight)) return VO_ERROR; } return ret; } -static void wayland_wakeup(struct ra_ctx *ctx) +static void wayland_vk_wakeup(struct ra_ctx *ctx) { vo_wayland_wakeup(ctx->vo); } -static void wayland_wait_events(struct ra_ctx *ctx, int64_t until_time_us) +static void wayland_vk_wait_events(struct ra_ctx *ctx, int64_t until_time_us) { vo_wayland_wait_events(ctx->vo, until_time_us); } @@ -137,10 +126,10 @@ static void wayland_wait_events(struct ra_ctx *ctx, int64_t until_time_us) const struct ra_ctx_fns ra_ctx_vulkan_wayland = { .type = "vulkan", .name = "wayland", - .reconfig = wayland_reconfig, - .control = wayland_control, - .wakeup = wayland_wakeup, - .wait_events = wayland_wait_events, - .init = wayland_init, - .uninit = wayland_uninit, + .reconfig = wayland_vk_reconfig, + .control = wayland_vk_control, + .wakeup = wayland_vk_wakeup, + .wait_events = wayland_vk_wait_events, + .init = wayland_vk_init, + .uninit = wayland_vk_uninit, }; diff --git a/video/out/wayland/server-decoration.xml b/video/out/wayland/server-decoration.xml new file mode 100644 index 0000000000..8bc106c7c4 --- /dev/null +++ b/video/out/wayland/server-decoration.xml @@ -0,0 +1,94 @@ + + + . + ]]> + + + This interface allows to coordinate whether the server should create + a server-side window decoration around a wl_surface representing a + shell surface (wl_shell_surface or similar). By announcing support + for this interface the server indicates that it supports server + side decorations. + + + + When a client creates a server-side decoration object it indicates + that it supports the protocol. The client is supposed to tell the + server whether it wants server-side decorations or will provide + client-side decorations. + + If the client does not create a server-side decoration object for + a surface the server interprets this as lack of support for this + protocol and considers it as client-side decorated. Nevertheless a + client-side decorated surface should use this protocol to indicate + to the server that it does not want a server-side deco. + + + + + + + + + + + + + This event is emitted directly after binding the interface. It contains + the default mode for the decoration. When a new server decoration object + is created this new object will be in the default mode until the first + request_mode is requested. + + The server may change the default mode at any time. + + + + + + + + + + + + + + + + + + + + + This event is emitted directly after the decoration is created and + represents the base decoration policy by the server. E.g. a server + which wants all surfaces to be client-side decorated will send Client, + a server which wants server-side decoration will send Server. + + The client can request a different mode through the decoration request. + The server will acknowledge this by another event with the same mode. So + even if a server prefers server-side decoration it's possible to force a + client-side decoration. + + The server may emit this event at any time. In this case the client can + again request a different mode. It's the responsibility of the server to + prevent a feedback loop. + + + + + diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c index fedebb3f8d..fb98308de2 100644 --- a/video/out/wayland_common.c +++ b/video/out/wayland_common.c @@ -1,8 +1,5 @@ /* * This file is part of mpv video player. - * Copyright © 2008 Kristian Høgsberg - * Copyright © 2012-2013 Collabora, Ltd. - * Copyright © 2013 Alexander Preisinger * * mpv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,80 +15,269 @@ * License along with mpv. If not, see . */ -#include -#include -#include -#include -#include -#include #include #include - -#include #include - -#include "config.h" -#include "misc/bstr.h" -#include "options/options.h" #include "common/msg.h" -#include "mpv_talloc.h" - -#include "wayland_common.h" - -#include "vo.h" -#include "win_state.h" +#include "input/input.h" +#include "input/keycodes.h" #include "osdep/io.h" #include "osdep/timer.h" +#include "win_state.h" +#include "wayland_common.h" -#include "input/input.h" -#include "input/event.h" -#include "input/keycodes.h" +// Generated from xdg-shell-unstable-v6.xml +#include "video/out/wayland/xdg-shell-v6.h" -static int lookupkey(int key); +// Generated from idle-inhibit-unstable-v1.xml +#include "video/out/wayland/idle-inhibit-v1.h" -static void hide_cursor(struct vo_wayland_state * wl); -static void show_cursor(struct vo_wayland_state * wl); -static void window_move(struct vo_wayland_state * wl, uint32_t serial); -static void window_set_title(struct vo_wayland_state * wl, const char *title); -static void schedule_resize(struct vo_wayland_state *wl, - uint32_t edges, - int32_t width, - int32_t height); +// Generated from server-decoration.xml +#include "video/out/wayland/srv-decor.h" -static void vo_wayland_fullscreen(struct vo *vo); +static void xdg_shell_ping(void *data, struct zxdg_shell_v6 *shell, uint32_t serial) +{ + zxdg_shell_v6_pong(shell, serial); +} -static const struct wl_callback_listener frame_listener; +static const struct zxdg_shell_v6_listener xdg_shell_listener = { + xdg_shell_ping, +}; + +static void set_cursor_visibility(struct vo_wayland_state *wl, int on) +{ + if (!wl->pointer) + return; + if (on) { + struct wl_cursor_image *image = wl->default_cursor->images[0]; + struct wl_buffer *buffer = wl_cursor_image_get_buffer(image); + if (!buffer) + return; + wl_pointer_set_cursor(wl->pointer, wl->pointer_id, wl->cursor_surface, + image->hotspot_x, image->hotspot_y); + wl_surface_attach(wl->cursor_surface, buffer, 0, 0); + wl_surface_damage(wl->cursor_surface, 0, 0, image->width, image->height); + wl_surface_commit(wl->cursor_surface); + } else { + wl_pointer_set_cursor(wl->pointer, wl->pointer_id, NULL, 0, 0); + } +} + +static void pointer_handle_enter(void *data, struct wl_pointer *pointer, + uint32_t serial, struct wl_surface *surface, + wl_fixed_t sx, wl_fixed_t sy) +{ + struct vo_wayland_state *wl = data; + + wl->pointer = pointer; + wl->pointer_id = serial; + + set_cursor_visibility(wl, 1); + mp_input_put_key(wl->vo->input_ctx, MP_KEY_MOUSE_ENTER); +} + +static void pointer_handle_leave(void *data, struct wl_pointer *pointer, + uint32_t serial, struct wl_surface *surface) +{ + struct vo_wayland_state *wl = data; + mp_input_put_key(wl->vo->input_ctx, MP_KEY_MOUSE_LEAVE); +} + +static void pointer_handle_motion(void *data, struct wl_pointer *pointer, + uint32_t time, wl_fixed_t sx, wl_fixed_t sy) +{ + struct vo_wayland_state *wl = data; + + wl->mouse_x = wl_fixed_to_int(sx) * wl->scaling; + wl->mouse_y = wl_fixed_to_int(sy) * wl->scaling; + + mp_input_set_mouse_pos(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y); +} + +static void window_move(struct vo_wayland_state *wl, uint32_t serial) +{ + if (wl->xdg_toplevel) + zxdg_toplevel_v6_move(wl->xdg_toplevel, wl->seat, serial); +} + +static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, uint32_t button, + uint32_t state) +{ + struct vo_wayland_state *wl = data; + + state = state == WL_POINTER_BUTTON_STATE_PRESSED ? MP_KEY_STATE_DOWN + : MP_KEY_STATE_UP; + + button = button == BTN_LEFT ? MP_MBTN_LEFT : + button == BTN_MIDDLE ? MP_MBTN_MID : MP_MBTN_RIGHT; + + mp_input_put_key(wl->vo->input_ctx, button | state); + + if (!mp_input_test_dragging(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y) && + (button == MP_MBTN_LEFT) && (state == MP_KEY_STATE_DOWN)) + window_move(wl, serial); +} + +static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value) +{ + struct vo_wayland_state *wl = data; + double val = wl_fixed_to_double(value)*0.1; + switch (axis) { + case WL_POINTER_AXIS_VERTICAL_SCROLL: + if (value > 0) + mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_DOWN, +val); + if (value < 0) + mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_UP, -val); + break; + case WL_POINTER_AXIS_HORIZONTAL_SCROLL: + if (value > 0) + mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_RIGHT, +val); + if (value < 0) + mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_LEFT, -val); + break; + } +} + +static const struct wl_pointer_listener pointer_listener = { + pointer_handle_enter, + pointer_handle_leave, + pointer_handle_motion, + pointer_handle_button, + pointer_handle_axis, +}; + +static int check_for_resize(struct vo_wayland_state *wl, wl_fixed_t x_w, wl_fixed_t y_w, + enum zxdg_toplevel_v6_resize_edge *edge) +{ + if (wl->touch_entries || wl->fullscreen) + return 0; + + const int edge_pixels = 64; + int pos[2] = { wl_fixed_to_double(x_w), wl_fixed_to_double(y_w) }; + int left_edge = pos[0] < edge_pixels; + int top_edge = pos[1] < edge_pixels; + int right_edge = pos[0] > (mp_rect_w(wl->geometry) - edge_pixels); + int bottom_edge = pos[1] > (mp_rect_h(wl->geometry) - edge_pixels); + + if (left_edge) { + *edge = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT; + if (top_edge) + *edge = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_LEFT; + else if (bottom_edge) + *edge = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_LEFT; + } else if (right_edge) { + *edge = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT; + if (top_edge) + *edge = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_RIGHT; + else if (bottom_edge) + *edge = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_RIGHT; + } else if (top_edge) { + *edge = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP; + } else if (bottom_edge) { + *edge = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM; + } else { + *edge = 0; + return 0; + } + + return 1; +} + +static void touch_handle_down(void *data, struct wl_touch *wl_touch, + uint32_t serial, uint32_t time, struct wl_surface *surface, + int32_t id, wl_fixed_t x_w, wl_fixed_t y_w) +{ + struct vo_wayland_state *wl = data; + + enum zxdg_toplevel_v6_resize_edge edge; + if (check_for_resize(wl, x_w, y_w, &edge)) { + wl->touch_entries = 0; + zxdg_toplevel_v6_resize(wl->xdg_toplevel, wl->seat, serial, edge); + return; + } else if (wl->touch_entries) { + wl->touch_entries = 0; + zxdg_toplevel_v6_move(wl->xdg_toplevel, wl->seat, serial); + return; + } + + wl->touch_entries = 1; + + wl->mouse_x = wl_fixed_to_int(x_w) * wl->scaling; + wl->mouse_y = wl_fixed_to_int(y_w) * wl->scaling; + + mp_input_set_mouse_pos(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y); + mp_input_put_key(wl->vo->input_ctx, MP_MBTN_LEFT | MP_KEY_STATE_DOWN); +} + +static void touch_handle_up(void *data, struct wl_touch *wl_touch, + uint32_t serial, uint32_t time, int32_t id) +{ + struct vo_wayland_state *wl = data; + + wl->touch_entries = 0; + + mp_input_put_key(wl->vo->input_ctx, MP_MBTN_LEFT | MP_KEY_STATE_UP); +} + +static void touch_handle_motion(void *data, struct wl_touch *wl_touch, + uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w) +{ + struct vo_wayland_state *wl = data; + + wl->mouse_x = wl_fixed_to_int(x_w) * wl->scaling; + wl->mouse_y = wl_fixed_to_int(y_w) * wl->scaling; + + mp_input_set_mouse_pos(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y); +} + +static void touch_handle_frame(void *data, struct wl_touch *wl_touch) +{ +} + +static void touch_handle_cancel(void *data, struct wl_touch *wl_touch) +{ +} + +static const struct wl_touch_listener touch_listener = { + touch_handle_down, + touch_handle_up, + touch_handle_motion, + touch_handle_frame, + touch_handle_cancel, +}; static const struct mp_keymap keymap[] = { - // special keys + /* Special keys */ {XKB_KEY_Pause, MP_KEY_PAUSE}, {XKB_KEY_Escape, MP_KEY_ESC}, {XKB_KEY_BackSpace, MP_KEY_BS}, {XKB_KEY_Tab, MP_KEY_TAB}, {XKB_KEY_Return, MP_KEY_ENTER}, {XKB_KEY_Menu, MP_KEY_MENU}, {XKB_KEY_Print, MP_KEY_PRINT}, - // cursor keys + /* Cursor keys */ {XKB_KEY_Left, MP_KEY_LEFT}, {XKB_KEY_Right, MP_KEY_RIGHT}, {XKB_KEY_Up, MP_KEY_UP}, {XKB_KEY_Down, MP_KEY_DOWN}, - // navigation block + /* Navigation keys */ {XKB_KEY_Insert, MP_KEY_INSERT}, {XKB_KEY_Delete, MP_KEY_DELETE}, {XKB_KEY_Home, MP_KEY_HOME}, {XKB_KEY_End, MP_KEY_END}, {XKB_KEY_Page_Up, MP_KEY_PAGE_UP}, {XKB_KEY_Page_Down, MP_KEY_PAGE_DOWN}, - // F-keys - {XKB_KEY_F1, MP_KEY_F+1}, {XKB_KEY_F2, MP_KEY_F+2}, - {XKB_KEY_F3, MP_KEY_F+3}, {XKB_KEY_F4, MP_KEY_F+4}, - {XKB_KEY_F5, MP_KEY_F+5}, {XKB_KEY_F6, MP_KEY_F+6}, - {XKB_KEY_F7, MP_KEY_F+7}, {XKB_KEY_F8, MP_KEY_F+8}, - {XKB_KEY_F9, MP_KEY_F+9}, {XKB_KEY_F10, MP_KEY_F+10}, - {XKB_KEY_F11, MP_KEY_F+11}, {XKB_KEY_F12, MP_KEY_F+12}, + /* F-keys */ + {XKB_KEY_F1, MP_KEY_F + 1}, {XKB_KEY_F2, MP_KEY_F + 2}, + {XKB_KEY_F3, MP_KEY_F + 3}, {XKB_KEY_F4, MP_KEY_F + 4}, + {XKB_KEY_F5, MP_KEY_F + 5}, {XKB_KEY_F6, MP_KEY_F + 6}, + {XKB_KEY_F7, MP_KEY_F + 7}, {XKB_KEY_F8, MP_KEY_F + 8}, + {XKB_KEY_F9, MP_KEY_F + 9}, {XKB_KEY_F10, MP_KEY_F +10}, + {XKB_KEY_F11, MP_KEY_F +11}, {XKB_KEY_F12, MP_KEY_F +12}, - // numpad independent of numlock - {XKB_KEY_KP_Subtract, '-'}, {XKB_KEY_KP_Add, '+'}, + /* Numpad independent of numlock */ + {XKB_KEY_KP_Subtract, '-'}, {XKB_KEY_KP_Add, '+'}, {XKB_KEY_KP_Multiply, '*'}, {XKB_KEY_KP_Divide, '/'}, {XKB_KEY_KP_Enter, MP_KEY_KPENTER}, - // numpad with numlock + /* Numpad with numlock */ {XKB_KEY_KP_0, MP_KEY_KP0}, {XKB_KEY_KP_1, MP_KEY_KP1}, {XKB_KEY_KP_2, MP_KEY_KP2}, {XKB_KEY_KP_3, MP_KEY_KP3}, {XKB_KEY_KP_4, MP_KEY_KP4}, {XKB_KEY_KP_5, MP_KEY_KP5}, @@ -99,7 +285,7 @@ static const struct mp_keymap keymap[] = { {XKB_KEY_KP_8, MP_KEY_KP8}, {XKB_KEY_KP_9, MP_KEY_KP9}, {XKB_KEY_KP_Decimal, MP_KEY_KPDEC}, {XKB_KEY_KP_Separator, MP_KEY_KPDEC}, - // numpad without numlock + /* Numpad without numlock */ {XKB_KEY_KP_Insert, MP_KEY_KPINS}, {XKB_KEY_KP_End, MP_KEY_KP1}, {XKB_KEY_KP_Down, MP_KEY_KP2}, {XKB_KEY_KP_Page_Down, MP_KEY_KP3}, {XKB_KEY_KP_Left, MP_KEY_KP4}, {XKB_KEY_KP_Begin, MP_KEY_KP5}, @@ -107,7 +293,7 @@ static const struct mp_keymap keymap[] = { {XKB_KEY_KP_Up, MP_KEY_KP8}, {XKB_KEY_KP_Page_Up, MP_KEY_KP9}, {XKB_KEY_KP_Delete, MP_KEY_KPDEL}, - // "Multimedia keyboard" keys + /* Multimedia keys */ {XKB_KEY_XF86MenuKB, MP_KEY_MENU}, {XKB_KEY_XF86AudioPlay, MP_KEY_PLAY}, {XKB_KEY_XF86AudioPause, MP_KEY_PAUSE}, {XKB_KEY_XF86AudioStop, MP_KEY_STOP}, @@ -124,137 +310,8 @@ static const struct mp_keymap keymap[] = { {0, 0} }; - -/** Wayland listeners **/ - -static void ssurface_handle_ping(void *data, - struct wl_shell_surface *shell_surface, - uint32_t serial) -{ - wl_shell_surface_pong(shell_surface, serial); -} - -static void ssurface_handle_configure(void *data, - struct wl_shell_surface *shell_surface, - uint32_t edges, - int32_t width, - int32_t height) -{ - struct vo_wayland_state *wl = data; - float win_aspect = wl->window.aspect; - if (!width || !height) - return; - if (!wl->window.is_fullscreen) - width = win_aspect * height; - schedule_resize(wl, edges, width, height); -} - -static void ssurface_handle_popup_done(void *data, - struct wl_shell_surface *shell_surface) -{ -} - -static const struct wl_shell_surface_listener shell_surface_listener = { - ssurface_handle_ping, - ssurface_handle_configure, - ssurface_handle_popup_done -}; - -static void output_handle_geometry(void *data, - struct wl_output *wl_output, - int32_t x, - int32_t y, - int32_t physical_width, - int32_t physical_height, - int32_t subpixel, - const char *make, - const char *model, - int32_t transform) -{ - struct vo_wayland_output *output = data; - output->make = make; - output->model = model; -} - -static void output_handle_mode(void *data, - struct wl_output *wl_output, - uint32_t flags, - int32_t width, - int32_t height, - int32_t refresh) -{ - struct vo_wayland_output *output = data; - - // only save current mode - if (!output || !(flags & WL_OUTPUT_MODE_CURRENT)) - return; - - output->width = width; - output->height = height; - output->flags = flags; - output->refresh_rate = refresh; -} - -static void output_handle_done(void* data, struct wl_output *wl_output) -{ -} - -static void output_handle_scale(void* data, struct wl_output *wl_output, - int32_t factor) -{ - struct vo_wayland_output *output = data; - output->scale = factor; -} - -static const struct wl_output_listener output_listener = { - output_handle_geometry, - output_handle_mode, - output_handle_done, - output_handle_scale -}; - - -/* SURFACE LISTENER */ - -static void surface_handle_enter(void *data, - struct wl_surface *wl_surface, - struct wl_output *output) -{ - struct vo_wayland_state *wl = data; - wl->display.current_output = NULL; - - struct vo_wayland_output *o; - wl_list_for_each(o, &wl->display.output_list, link) { - if (o->output == output) { - wl->display.current_output = o; - break; - } - } - - wl->window.events |= VO_EVENT_WIN_STATE | VO_EVENT_RESIZE; -} - -static void surface_handle_leave(void *data, - struct wl_surface *wl_surface, - struct wl_output *output) -{ - // window can be displayed at 2 output, but we only use the most recently - // entered and discard the previous one even if a part of the window is - // still visible on the previous entered output. - // Don't bother with a "leave" logic -} - -static const struct wl_surface_listener surface_listener = { - surface_handle_enter, - surface_handle_leave -}; - -/* KEYBOARD LISTENER */ -static void keyboard_handle_keymap(void *data, - struct wl_keyboard *wl_keyboard, - uint32_t format, - int32_t fd, - uint32_t size) +static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, + uint32_t format, int32_t fd, uint32_t size) { struct vo_wayland_state *wl = data; char *map_str; @@ -270,68 +327,97 @@ static void keyboard_handle_keymap(void *data, return; } - wl->input.xkb.keymap = xkb_keymap_new_from_string(wl->input.xkb.context, - map_str, - XKB_KEYMAP_FORMAT_TEXT_V1, - 0); + wl->xkb_keymap = xkb_keymap_new_from_string(wl->xkb_context, map_str, + XKB_KEYMAP_FORMAT_TEXT_V1, 0); munmap(map_str, size); close(fd); - if (!wl->input.xkb.keymap) { + if (!wl->xkb_keymap) { MP_ERR(wl, "failed to compile keymap\n"); return; } - wl->input.xkb.state = xkb_state_new(wl->input.xkb.keymap); - if (!wl->input.xkb.state) { + wl->xkb_state = xkb_state_new(wl->xkb_keymap); + if (!wl->xkb_state) { MP_ERR(wl, "failed to create XKB state\n"); - xkb_keymap_unref(wl->input.xkb.keymap); - wl->input.xkb.keymap = NULL; + xkb_keymap_unref(wl->xkb_keymap); + wl->xkb_keymap = NULL; return; } } -static void keyboard_handle_enter(void *data, - struct wl_keyboard *wl_keyboard, - uint32_t serial, - struct wl_surface *surface, +static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { } -static void keyboard_handle_leave(void *data, - struct wl_keyboard *wl_keyboard, - uint32_t serial, - struct wl_surface *surface) +static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, struct wl_surface *surface) { } -static void keyboard_handle_key(void *data, - struct wl_keyboard *wl_keyboard, - uint32_t serial, - uint32_t time, - uint32_t key, +static bool create_input(struct vo_wayland_state *wl) +{ + wl->xkb_context = xkb_context_new(0); + + if (!wl->xkb_context) { + MP_ERR(wl, "failed to initialize input: check xkbcommon\n"); + return 1; + } + + return 0; +} + +static int lookupkey(int key) +{ + const char *passthrough_keys = " -+*/<>`~!@#$%^&()_{}:;\"\',.?\\|=[]"; + + int mpkey = 0; + if ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z') || + (key >= '0' && key <= '9') || + (key > 0 && key < 256 && strchr(passthrough_keys, key))) + mpkey = key; + + if (!mpkey) + mpkey = lookup_keymap_table(keymap, key); + + return mpkey; +} + +static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { struct vo_wayland_state *wl = data; uint32_t code = code = key + 8; - xkb_keysym_t sym = xkb_state_key_get_one_sym(wl->input.xkb.state, code); + xkb_keysym_t sym = xkb_state_key_get_one_sym(wl->xkb_state, code); int mpmod = state == WL_KEYBOARD_KEY_STATE_PRESSED ? MP_KEY_STATE_DOWN : MP_KEY_STATE_UP; - static const char *mod_names[] = {XKB_MOD_NAME_SHIFT, XKB_MOD_NAME_CTRL, - XKB_MOD_NAME_ALT, XKB_MOD_NAME_LOGO, 0}; - static int mods[] = {MP_KEY_MODIFIER_SHIFT, MP_KEY_MODIFIER_CTRL, - MP_KEY_MODIFIER_ALT, MP_KEY_MODIFIER_META, 0}; + static const char *mod_names[] = { + XKB_MOD_NAME_SHIFT, + XKB_MOD_NAME_CTRL, + XKB_MOD_NAME_ALT, + XKB_MOD_NAME_LOGO, + 0, + }; + + static int mods[] = { + MP_KEY_MODIFIER_SHIFT, + MP_KEY_MODIFIER_CTRL, + MP_KEY_MODIFIER_ALT, + MP_KEY_MODIFIER_META, + 0, + }; for (int n = 0; mods[n]; n++) { - xkb_mod_index_t index = - xkb_keymap_mod_get_index(wl->input.xkb.keymap, mod_names[n]); - if (!xkb_state_mod_index_is_consumed(wl->input.xkb.state, code, index) - && xkb_state_mod_index_is_active(wl->input.xkb.state, index, + xkb_mod_index_t index = xkb_keymap_mod_get_index(wl->xkb_keymap, mod_names[n]); + if (!xkb_state_mod_index_is_consumed(wl->xkb_state, code, index) + && xkb_state_mod_index_is_active(wl->xkb_state, index, XKB_STATE_MODS_DEPRESSED)) mpmod |= mods[n]; } @@ -340,42 +426,29 @@ static void keyboard_handle_key(void *data, if (mpkey) { mp_input_put_key(wl->vo->input_ctx, mpkey | mpmod); } else { - char s[80]; + char s[128]; if (xkb_keysym_to_utf8(sym, s, sizeof(s)) > 0) mp_input_put_key_utf8(wl->vo->input_ctx, mpmod, bstr0(s)); } } -static void keyboard_handle_modifiers(void *data, - struct wl_keyboard *wl_keyboard, - uint32_t serial, - uint32_t mods_depressed, - uint32_t mods_latched, - uint32_t mods_locked, +static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, uint32_t mods_depressed, + uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { struct vo_wayland_state *wl = data; - xkb_state_update_mask(wl->input.xkb.state, - mods_depressed, - mods_latched, - mods_locked, - 0, 0, group); + xkb_state_update_mask(wl->xkb_state, mods_depressed, mods_latched, + mods_locked, 0, 0, group); } -static void keyboard_handle_repeat_info(void *data, - struct wl_keyboard *wl_keyboard, - int32_t rate, - int32_t delay) +static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard, + int32_t rate, int32_t delay) { struct vo_wayland_state *wl = data; - if (wl->vo->opts->native_keyrepeat) { - if (rate < 0 || delay < 0) { - MP_WARN(wl, "Invalid rate or delay values sent by compositor\n"); - return; - } + if (wl->vo->opts->native_keyrepeat) mp_input_set_repeat_info(wl->vo->input_ctx, rate, delay); - } } static const struct wl_keyboard_listener keyboard_listener = { @@ -384,786 +457,847 @@ static const struct wl_keyboard_listener keyboard_listener = { keyboard_handle_leave, keyboard_handle_key, keyboard_handle_modifiers, - keyboard_handle_repeat_info + keyboard_handle_repeat_info, }; -/* POINTER LISTENER */ -static void pointer_handle_enter(void *data, - struct wl_pointer *pointer, - uint32_t serial, - struct wl_surface *surface, - wl_fixed_t sx_w, - wl_fixed_t sy_w) +static void seat_handle_caps(void *data, struct wl_seat *seat, + enum wl_seat_capability caps) { struct vo_wayland_state *wl = data; - wl->cursor.serial = serial; - wl->cursor.pointer = pointer; + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !wl->pointer) { + wl->pointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(wl->pointer, &pointer_listener, wl); + } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && wl->pointer) { + wl_pointer_destroy(wl->pointer); + wl->pointer = NULL; + } - /* Release the left button on pointer enter again - * because after moving the shell surface no release event is sent */ - mp_input_put_key(wl->vo->input_ctx, MP_KEY_MOUSE_ENTER); - mp_input_put_key(wl->vo->input_ctx, MP_MBTN_LEFT | MP_KEY_STATE_UP); - show_cursor(wl); + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !wl->keyboard) { + wl->keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(wl->keyboard, &keyboard_listener, wl); + } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && wl->keyboard) { + wl_keyboard_destroy(wl->keyboard); + wl->keyboard = NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !wl->touch) { + wl->touch = wl_seat_get_touch(seat); + wl_touch_set_user_data(wl->touch, wl); + wl_touch_add_listener(wl->touch, &touch_listener, wl); + } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && wl->touch) { + wl_touch_destroy(wl->touch); + wl->touch = NULL; + } } -static void pointer_handle_leave(void *data, - struct wl_pointer *pointer, - uint32_t serial, - struct wl_surface *surface) +static const struct wl_seat_listener seat_listener = { + seat_handle_caps, +}; + +static void output_handle_geometry(void *data, struct wl_output *wl_output, + int32_t x, int32_t y, int32_t phys_width, + int32_t phys_height, int32_t subpixel, + const char *make, const char *model, + int32_t transform) { - struct vo_wayland_state *wl = data; - mp_input_put_key(wl->vo->input_ctx, MP_KEY_MOUSE_LEAVE); + struct vo_wayland_output *output = data; + output->make = make; + output->model = model; + output->geometry.x0 = x; + output->geometry.y0 = y; } -static void pointer_handle_motion(void *data, - struct wl_pointer *pointer, - uint32_t time, - wl_fixed_t sx_w, - wl_fixed_t sy_w) +static void output_handle_mode(void *data, struct wl_output *wl_output, + uint32_t flags, int32_t width, + int32_t height, int32_t refresh) { - int32_t scale = 1; - struct vo_wayland_state *wl = data; + struct vo_wayland_output *output = data; - if (wl->display.current_output) - scale = wl->display.current_output->scale; + /* Only save current mode */ + if (!output || !(flags & WL_OUTPUT_MODE_CURRENT)) + return; - wl->cursor.pointer = pointer; - wl->window.mouse_x = scale*wl_fixed_to_int(sx_w); - wl->window.mouse_y = scale*wl_fixed_to_int(sy_w); + output->width = width; + output->height = height; + output->geometry.x1 = width + output->geometry.x0; + output->geometry.y1 = height + output->geometry.y0; + output->flags = flags; + output->refresh_rate = (double)refresh * 0.001; +} - mp_input_set_mouse_pos(wl->vo->input_ctx, wl->window.mouse_x, - wl->window.mouse_y); +static void output_handle_done(void* data, struct wl_output *wl_output) +{ } -static void pointer_handle_button(void *data, - struct wl_pointer *pointer, - uint32_t serial, - uint32_t time, - uint32_t button, - uint32_t state) +static void output_handle_scale(void* data, struct wl_output *wl_output, + int32_t factor) { - struct vo_wayland_state *wl = data; + struct vo_wayland_output *output = data; + output->scale = factor; +} - state = state == WL_POINTER_BUTTON_STATE_PRESSED ? MP_KEY_STATE_DOWN - : MP_KEY_STATE_UP; +static const struct wl_output_listener output_listener = { + output_handle_geometry, + output_handle_mode, + output_handle_done, + output_handle_scale, +}; - button = button == BTN_LEFT ? MP_MBTN_LEFT : - button == BTN_MIDDLE ? MP_MBTN_MID : MP_MBTN_RIGHT; +static void data_offer_handle_offer(void *data, struct wl_data_offer *offer, + const char *mime_type) +{ + struct vo_wayland_state *wl = data; + int score = mp_event_get_mime_type_score(wl->vo->input_ctx, mime_type); + if (score > wl->dnd_mime_score) { + wl->dnd_mime_score = score; + talloc_free(wl->dnd_mime_type); + wl->dnd_mime_type = talloc_strdup(wl, mime_type); + MP_VERBOSE(wl, "Given DND offer with mime type %s\n", wl->dnd_mime_type); + } +} - mp_input_put_key(wl->vo->input_ctx, button | state); +static void data_offer_source_actions(void *data, struct wl_data_offer *offer, uint32_t source_actions) +{ - if (!mp_input_test_dragging(wl->vo->input_ctx, wl->window.mouse_x, wl->window.mouse_y) && - (button == MP_MBTN_LEFT) && (state == MP_KEY_STATE_DOWN)) - window_move(wl, serial); } -static void pointer_handle_axis(void *data, - struct wl_pointer *pointer, - uint32_t time, - uint32_t axis, - wl_fixed_t value) +static void data_offer_action(void *data, struct wl_data_offer *wl_data_offer, uint32_t dnd_action) { struct vo_wayland_state *wl = data; - - // value is 10.00 on a normal mouse wheel - // scale it down to 1.00 for multipliying it with the commands - if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { - if (value > 0) - mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_DOWN, - wl_fixed_to_double(value)*0.1); - if (value < 0) - mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_UP, - wl_fixed_to_double(value)*-0.1); - } - else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { - if (value > 0) - mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_RIGHT, - wl_fixed_to_double(value)*0.1); - if (value < 0) - mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_LEFT, - wl_fixed_to_double(value)*-0.1); - } + wl->dnd_action = dnd_action & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY ? + DND_REPLACE : DND_APPEND; + MP_VERBOSE(wl, "DND action is %s\n", + wl->dnd_action == DND_REPLACE ? "DND_REPLACE" : "DND_APPEND"); } -static const struct wl_pointer_listener pointer_listener = { - pointer_handle_enter, - pointer_handle_leave, - pointer_handle_motion, - pointer_handle_button, - pointer_handle_axis, +static const struct wl_data_offer_listener data_offer_listener = { + data_offer_handle_offer, + data_offer_source_actions, + data_offer_action, }; -static void seat_handle_capabilities(void *data, - struct wl_seat *seat, - enum wl_seat_capability caps) +static void data_device_handle_data_offer(void *data, struct wl_data_device *wl_ddev, + struct wl_data_offer *id) { struct vo_wayland_state *wl = data; + if (wl->dnd_offer) + wl_data_offer_destroy(wl->dnd_offer); - if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !wl->input.keyboard) { - wl->input.keyboard = wl_seat_get_keyboard(seat); - wl_keyboard_add_listener(wl->input.keyboard, &keyboard_listener, wl); - } - else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && wl->input.keyboard) { - wl_keyboard_destroy(wl->input.keyboard); - wl->input.keyboard = NULL; - } - if ((caps & WL_SEAT_CAPABILITY_POINTER) && !wl->input.pointer) { - wl->input.pointer = wl_seat_get_pointer(seat); - wl_pointer_add_listener(wl->input.pointer, &pointer_listener, wl); - } - else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && wl->input.pointer) { - wl_pointer_destroy(wl->input.pointer); - wl->input.pointer = NULL; + wl->dnd_offer = id; + wl_data_offer_add_listener(id, &data_offer_listener, wl); +} + +static void data_device_handle_enter(void *data, struct wl_data_device *wl_ddev, + uint32_t serial, struct wl_surface *surface, + wl_fixed_t x, wl_fixed_t y, + struct wl_data_offer *id) +{ + struct vo_wayland_state *wl = data; + if (wl->dnd_offer != id) { + MP_FATAL(wl, "DND offer ID mismatch!\n"); + return; } + + wl_data_offer_set_actions(id, WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | + WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE, + WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY); + + wl_data_offer_accept(id, serial, wl->dnd_mime_type); + + MP_VERBOSE(wl, "Accepting DND offer with mime type %s\n", wl->dnd_mime_type); } -static void seat_handle_name(void *data, - struct wl_seat *seat, - const char *name) +static void data_device_handle_leave(void *data, struct wl_data_device *wl_ddev) { struct vo_wayland_state *wl = data; - MP_VERBOSE(wl, "Seat \"%s\" connected\n", name); + + if (wl->dnd_offer) { + if (wl->dnd_fd != -1) + return; + wl_data_offer_destroy(wl->dnd_offer); + wl->dnd_offer = NULL; + } + + MP_VERBOSE(wl, "Releasing DND offer with mime type %s\n", wl->dnd_mime_type); + + talloc_free(wl->dnd_mime_type); + wl->dnd_mime_type = NULL; + wl->dnd_mime_score = 0; } -static const struct wl_seat_listener seat_listener = { - seat_handle_capabilities, - seat_handle_name, -}; +static void data_device_handle_motion(void *data, struct wl_data_device *wl_ddev, + uint32_t time, wl_fixed_t x, wl_fixed_t y) +{ + struct vo_wayland_state *wl = data; -static void registry_handle_global(void *data, struct wl_registry *reg, - uint32_t id, const char *interface, - uint32_t version) + wl_data_offer_accept(wl->dnd_offer, time, wl->dnd_mime_type); +} + +static void data_device_handle_drop(void *data, struct wl_data_device *wl_ddev) { struct vo_wayland_state *wl = data; - if (strcmp(interface, "wl_compositor") == 0) { + int pipefd[2]; - wl->display.compositor = wl_registry_bind(reg, id, - &wl_compositor_interface, - MPMIN(3, version)); + if (pipe2(pipefd, O_CLOEXEC) == -1) { + MP_ERR(wl, "Failed to create dnd pipe!\n"); + return; } - else if (strcmp(interface, "wl_shell") == 0) { + MP_VERBOSE(wl, "Receiving DND offer with mime %s\n", wl->dnd_mime_type); - wl->display.shell = wl_registry_bind(reg, id, &wl_shell_interface, 1); - } + wl_data_offer_receive(wl->dnd_offer, wl->dnd_mime_type, pipefd[1]); + close(pipefd[1]); - else if (strcmp(interface, "wl_shm") == 0) { + wl->dnd_fd = pipefd[0]; +} - wl->display.shm = wl_registry_bind(reg, id, &wl_shm_interface, 1); - } +static void data_device_handle_selection(void *data, struct wl_data_device *wl_ddev, + struct wl_data_offer *id) +{ +} - else if (strcmp(interface, "wl_output") == 0) { +static const struct wl_data_device_listener data_device_listener = { + data_device_handle_data_offer, + data_device_handle_enter, + data_device_handle_leave, + data_device_handle_motion, + data_device_handle_drop, + data_device_handle_selection, +}; - struct vo_wayland_output *output = - talloc_zero(wl, struct vo_wayland_output); +static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id, + const char *interface, uint32_t ver) +{ + int found = 1; + struct vo_wayland_state *wl = data; + + if (!strcmp(interface, "wl_compositor") && found++) { + ver = MPMIN(ver, 4); /* Cap the version */ + wl->compositor = wl_registry_bind(reg, id, &wl_compositor_interface, ver); + } - output->id = id; - output->scale = 1; + if (!strcmp(interface, "wl_output") && found++) { + struct vo_wayland_output *output = talloc_zero(wl, struct vo_wayland_output); + + output->id = id; + output->scale = 1; output->output = wl_registry_bind(reg, id, &wl_output_interface, - MPMIN(2, version)); + MPMIN(2, ver)); wl_output_add_listener(output->output, &output_listener, output); - wl_list_insert(&wl->display.output_list, &output->link); + wl_list_insert(&wl->output_list, &output->link); } - else if (strcmp(interface, "wl_seat") == 0) { + if (!strcmp(interface, "zxdg_shell_v6") && found++) { + wl->shell = wl_registry_bind(reg, id, &zxdg_shell_v6_interface, 1); + zxdg_shell_v6_add_listener(wl->shell, &xdg_shell_listener, wl); + } - wl->input.seat = wl_registry_bind(reg, id, &wl_seat_interface, 4); - wl_seat_add_listener(wl->input.seat, &seat_listener, wl); + if (!strcmp(interface, "wl_seat") && found++) { + wl->seat = wl_registry_bind(reg, id, &wl_seat_interface, 1); + wl_seat_add_listener(wl->seat, &seat_listener, wl); + } + if (!strcmp(interface, "wl_shm") && found++) { + wl->shm = wl_registry_bind(reg, id, &wl_shm_interface, 1); } - else if (strcmp(interface, "wl_subcompositor") == 0) { + if (!strcmp(interface, "wl_data_device_manager") && (ver >= 3) && found++) { + wl->dnd_devman = wl_registry_bind(reg, id, &wl_data_device_manager_interface, 3); + } + + if (!strcmp(interface, "org_kde_kwin_server_decoration_manager") && found++) { + wl->server_decoration_manager = wl_registry_bind(reg, id, &org_kde_kwin_server_decoration_manager_interface, 1); + } - wl->display.subcomp = wl_registry_bind(reg, id, - &wl_subcompositor_interface, 1); + if (!strcmp(interface, "zwp_idle_inhibit_manager_v1") && found++) { + wl->idle_inhibit_manager = wl_registry_bind(reg, id, &zwp_idle_inhibit_manager_v1_interface, 1); } + + if (found > 1) + MP_VERBOSE(wl, "Registered for protocol %s\n", interface); } -static void registry_handle_global_remove(void *data, - struct wl_registry *registry, - uint32_t id) +static void registry_handle_remove(void *data, struct wl_registry *reg, uint32_t id) { } static const struct wl_registry_listener registry_listener = { - registry_handle_global, - registry_handle_global_remove + registry_handle_add, + registry_handle_remove, }; - -/*** internal functions ***/ - -static int lookupkey(int key) +static void surface_handle_enter(void *data, struct wl_surface *wl_surface, + struct wl_output *output) { - const char *passthrough_keys = " -+*/<>`~!@#$%^&()_{}:;\"\',.?\\|=[]"; + struct vo_wayland_state *wl = data; + wl->current_output = NULL; - int mpkey = 0; - if ((key >= 'a' && key <= 'z') || - (key >= 'A' && key <= 'Z') || - (key >= '0' && key <= '9') || - (key > 0 && key < 256 && strchr(passthrough_keys, key))) - mpkey = key; + struct vo_wayland_output *o; + wl_list_for_each(o, &wl->output_list, link) { + if (o->output == output) { + wl->current_output = o; + break; + } + } - if (!mpkey) - mpkey = lookup_keymap_table(keymap, key); + wl->current_output->has_surface = 1; + wl->scaling = wl->current_output->scale; - return mpkey; + MP_VERBOSE(wl, "Surface entered an output, scale = %i\n", wl->scaling); + + wl->pending_vo_events |= VO_EVENT_WIN_STATE; } -static void hide_cursor (struct vo_wayland_state *wl) +static void surface_handle_leave(void *data, struct wl_surface *wl_surface, + struct wl_output *output) { - if (!wl->cursor.pointer) - return; + struct vo_wayland_state *wl = data; - wl_pointer_set_cursor(wl->cursor.pointer, wl->cursor.serial, NULL, 0, 0); + struct vo_wayland_output *o; + wl_list_for_each(o, &wl->output_list, link) { + if (o->output == output) { + o->has_surface = 0; + return; + } + } } -static void show_cursor (struct vo_wayland_state *wl) +static const struct wl_surface_listener surface_listener = { + surface_handle_enter, + surface_handle_leave, +}; + +static int spawn_cursor(struct vo_wayland_state *wl) { - if (!wl->cursor.pointer) - return; + wl->cursor_theme = wl_cursor_theme_load(NULL, 24, wl->shm); + if (!wl->cursor_theme) { + MP_ERR(wl, "Unable to load cursor theme!\n"); + return 1; + } - struct wl_cursor_image *image = wl->cursor.default_cursor->images[0]; - struct wl_buffer *buffer = wl_cursor_image_get_buffer(image); + wl->default_cursor = wl_cursor_theme_get_cursor(wl->cursor_theme, "left_ptr"); + if (!wl->default_cursor) { + MP_ERR(wl, "Unable to load cursor theme!\n"); + return 1; + } - wl_pointer_set_cursor(wl->cursor.pointer, - wl->cursor.serial, - wl->cursor.surface, - image->hotspot_x, - image->hotspot_y); + wl->cursor_surface = wl_compositor_create_surface(wl->compositor); - wl_surface_attach(wl->cursor.surface, buffer, 0, 0); - wl_surface_damage(wl->cursor.surface, 0, 0, image->width, image->height); - wl_surface_commit(wl->cursor.surface); + return 0; } -static void window_move(struct vo_wayland_state *wl, uint32_t serial) +static void handle_surface_config(void *data, struct zxdg_surface_v6 *surface, + uint32_t serial) { - if (wl->display.shell) - wl_shell_surface_move(wl->window.shell_surface, wl->input.seat, serial); + zxdg_surface_v6_ack_configure(surface, serial); } -static void window_set_toplevel(struct vo_wayland_state *wl) +static const struct zxdg_surface_v6_listener xdg_surface_listener = { + handle_surface_config, +}; + +static void handle_toplevel_config(void *data, struct zxdg_toplevel_v6 *toplevel, + int32_t width, int32_t height, struct wl_array *states) { - if (wl->display.shell) - wl_shell_surface_set_toplevel(wl->window.shell_surface); + uint32_t *p; + struct vo_wayland_state *wl = data; + struct mp_rect old_geometry = wl->geometry; + + int prev_fs_state = wl->fullscreen; + int maximized = 0; + wl->fullscreen = 0; + wl_array_for_each(p, states) { + enum zxdg_toplevel_v6_state state = *p; + switch (state) { + case ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN: + wl->fullscreen = 1; + break; + case ZXDG_TOPLEVEL_V6_STATE_RESIZING: + wl->pending_vo_events |= VO_EVENT_LIVE_RESIZING; + break; + case ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED: + maximized = 1; + break; + case ZXDG_TOPLEVEL_V6_STATE_ACTIVATED: + break; + } + } + + if (width > 0 && height > 0) { + if (!wl->fullscreen) { + if (wl->vo->opts->keepaspect && wl->vo->opts->keepaspect_window && + !maximized) { + if (width > height) + width = height * wl->aspect_ratio; + else + height = width / wl->aspect_ratio; + } + wl->window_size.x0 = 0; + wl->window_size.y0 = 0; + wl->window_size.x1 = width; + wl->window_size.y1 = height; + } + wl->geometry.x0 = 0; + wl->geometry.y0 = 0; + wl->geometry.x1 = width; + wl->geometry.y1 = height; + } else { + wl->geometry = wl->window_size; + } + + MP_VERBOSE(wl, "Resizing due to xdg from %ix%i to %ix%i\n", + mp_rect_w(old_geometry)*wl->scaling, mp_rect_h(old_geometry)*wl->scaling, + mp_rect_w(wl->geometry)*wl->scaling, mp_rect_h(wl->geometry)*wl->scaling); + + if (prev_fs_state != wl->fullscreen) + wl->pending_vo_events |= VO_EVENT_FULLSCREEN_STATE; + wl->pending_vo_events |= VO_EVENT_RESIZE; } -static void window_set_title(struct vo_wayland_state *wl, const char *title) +static void handle_toplevel_close(void *data, struct zxdg_toplevel_v6 *xdg_toplevel) { - if (wl->display.shell) - wl_shell_surface_set_title(wl->window.shell_surface, title); } -static void schedule_resize(struct vo_wayland_state *wl, - uint32_t edges, - int32_t width, - int32_t height) +static const struct zxdg_toplevel_v6_listener xdg_toplevel_listener = { + handle_toplevel_config, + handle_toplevel_close, +}; + +static int create_surface(struct vo_wayland_state *wl) { - int32_t minimum_size = 150; - int32_t x, y; - float win_aspect = wl->window.aspect; - if (win_aspect <= 0) - win_aspect = 1; - - MP_DBG(wl, "schedule resize: %dx%d\n", width, height); - - width = MPMAX(minimum_size, width); - height = MPMAX(minimum_size, height); - if (wl->display.current_output) { - int scale = wl->display.current_output->scale; - width = MPMIN(width, wl->display.current_output->width /scale); - height = MPMIN(height, wl->display.current_output->height/scale); - } + wl->surface = wl_compositor_create_surface(wl->compositor); - // don't keep the aspect ratio in fullscreen mode because the compositor - // shows the desktop in the border regions if the video does not have the same - // aspect ratio as the screen - /* if only the height is changed we have to calculate the width - * in any other case we calculate the height */ - switch (edges) { - case WL_SHELL_SURFACE_RESIZE_TOP: - case WL_SHELL_SURFACE_RESIZE_BOTTOM: - width = win_aspect * height; - break; - case WL_SHELL_SURFACE_RESIZE_LEFT: - case WL_SHELL_SURFACE_RESIZE_RIGHT: - case WL_SHELL_SURFACE_RESIZE_TOP_LEFT: // just a preference - case WL_SHELL_SURFACE_RESIZE_TOP_RIGHT: - case WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT: - case WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT: - height = (1 / win_aspect) * width; - break; - } + wl->xdg_surface = zxdg_shell_v6_get_xdg_surface(wl->shell, wl->surface); + zxdg_surface_v6_add_listener(wl->xdg_surface, &xdg_surface_listener, wl); - if (edges & WL_SHELL_SURFACE_RESIZE_LEFT) - x = wl->window.width - width; - else - x = 0; + wl->xdg_toplevel = zxdg_surface_v6_get_toplevel(wl->xdg_surface); + zxdg_toplevel_v6_add_listener(wl->xdg_toplevel, &xdg_toplevel_listener, wl); - if (edges & WL_SHELL_SURFACE_RESIZE_TOP) - y = wl->window.height - height; - else - y = 0; + zxdg_toplevel_v6_set_title (wl->xdg_toplevel, "mpv"); + zxdg_toplevel_v6_set_app_id(wl->xdg_toplevel, "mpv"); - wl->window.sh_width = width; - wl->window.sh_height = height; - wl->window.sh_x = x; - wl->window.sh_y = y; - wl->window.events |= VO_EVENT_RESIZE; + return 0; } -static void frame_callback(void *data, - struct wl_callback *callback, - uint32_t time) +static const struct wl_callback_listener frame_listener; + +static void frame_callback(void *data, struct wl_callback *callback, uint32_t time) { struct vo_wayland_state *wl = data; - if (wl->frame.function) - wl->frame.function(wl->frame.data, time); - - if (callback) - wl_callback_destroy(callback); - - wl->frame.callback = wl_surface_frame(wl->window.video_surface); + wl_callback_destroy(callback); - if (!wl->frame.callback) { - MP_ERR(wl, "wl_surface_frame failed\n"); - return; - } + wl->frame_callback = wl_surface_frame(wl->surface); + wl_callback_add_listener(wl->frame_callback, &frame_listener, wl); - wl_callback_add_listener(wl->frame.callback, &frame_listener, wl); - wl_surface_commit(wl->window.video_surface); + if (wl->callback_exec) + wl->callback_exec(wl, wl->callback_exec_priv, time); } static const struct wl_callback_listener frame_listener = { - frame_callback + frame_callback, }; -static bool create_display(struct vo_wayland_state *wl) +static int set_border_decorations(struct vo_wayland_state *wl, int state) { - if (wl->vo->probing && !getenv("XDG_RUNTIME_DIR")) - return false; + if (!wl->server_decoration) + return VO_NOTIMPL; + enum org_kde_kwin_server_decoration_mode mode; + if (state) + mode = ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER; + else + mode = ORG_KDE_KWIN_SERVER_DECORATION_MODE_NONE; + org_kde_kwin_server_decoration_request_mode(wl->server_decoration, mode); + return VO_TRUE; +} - wl->display.display = wl_display_connect(NULL); +int vo_wayland_init(struct vo *vo) +{ + vo->wl = talloc_zero(NULL, struct vo_wayland_state); + struct vo_wayland_state *wl = vo->wl; - if (!wl->display.display) { - MP_MSG(wl, wl->vo->probing ? MSGL_V : MSGL_ERR, - "failed to connect to a wayland server: " - "check if a wayland compositor is running\n"); + *wl = (struct vo_wayland_state) { + .display = wl_display_connect(NULL), + .vo = vo, + .log = mp_log_new(wl, vo->log, "wayland"), + .scaling = 1, + .wakeup_pipe = {-1, -1}, + .dnd_fd = -1, + }; + if (!wl->display) return false; - } - wl->display.registry = wl_display_get_registry(wl->display.display); - wl_registry_add_listener(wl->display.registry, ®istry_listener, wl); - - wl_display_roundtrip(wl->display.display); + if (create_input(wl)) + return false; + wl_list_init(&wl->output_list); - wl->display.display_fd = wl_display_get_fd(wl->display.display); + wl->registry = wl_display_get_registry(wl->display); + wl_registry_add_listener(wl->registry, ®istry_listener, wl); - return true; -} + /* Do a roundtrip to run the registry */ + wl_display_roundtrip(wl->display); -static void destroy_display(struct vo_wayland_state *wl) -{ - struct vo_wayland_output *output = NULL; - struct vo_wayland_output *tmp = NULL; - - wl_list_for_each_safe(output, tmp, &wl->display.output_list, link) { - if (output && output->output) { - wl_output_destroy(output->output); - output->output = NULL; - wl_list_remove(&output->link); - } + if (!wl->shell) { + MP_FATAL(wl, "Compositor doesn't support the zxdg_shell_v6 protocol!\n"); + return false; } + if (!wl->idle_inhibit_manager) + MP_VERBOSE(wl, "Compositor doesn't support the " + "zwp_idle_inhibit_manager_v1 protocol!\n"); + if (!wl->dnd_devman) + MP_VERBOSE(wl, "Compositor doesn't support the " + "wl_data_device_manager (v3) protocol!\n"); + if (!wl->server_decoration_manager) + MP_VERBOSE(wl, "Compositor doesn't support the " + "org_kde_kwin_server_decoration_manager protocol!\n"); + + if (spawn_cursor(wl)) + return false; + if (create_surface(wl)) + return false; - if (wl->display.shm) - wl_shm_destroy(wl->display.shm); - - if (wl->display.shell) - wl_shell_destroy(wl->display.shell); + wl_surface_add_listener(wl->surface, &surface_listener, wl); - if (wl->display.subcomp) - wl_subcompositor_destroy(wl->display.subcomp); + if (wl->dnd_devman) { + wl->dnd_ddev = wl_data_device_manager_get_data_device(wl->dnd_devman, wl->seat); + wl_data_device_add_listener(wl->dnd_ddev, &data_device_listener, wl); + } - if (wl->display.compositor) - wl_compositor_destroy(wl->display.compositor); + if (wl->server_decoration_manager) { + wl->server_decoration = org_kde_kwin_server_decoration_manager_create(wl->server_decoration_manager, wl->surface); + set_border_decorations(wl, vo->opts->border); + } - if (wl->display.registry) - wl_registry_destroy(wl->display.registry); + wl->display_fd = wl_display_get_fd(wl->display); + mp_make_wakeup_pipe(wl->wakeup_pipe); - if (wl->display.display) { - wl_display_flush(wl->display.display); - wl_display_disconnect(wl->display.display); - } + return true; } -static bool create_window(struct vo_wayland_state *wl) +void vo_wayland_uninit(struct vo *vo) { - wl->window.video_surface = - wl_compositor_create_surface(wl->display.compositor); + struct vo_wayland_state *wl = vo->wl; - wl_surface_add_listener(wl->window.video_surface, - &surface_listener, wl); + if (wl->cursor_theme) + wl_cursor_theme_destroy(wl->cursor_theme); - if (wl->display.shell) { - wl->window.shell_surface = wl_shell_get_shell_surface(wl->display.shell, - wl->window.video_surface); + if (wl->cursor_surface) + wl_surface_destroy(wl->cursor_surface); - if (!wl->window.shell_surface) { - MP_ERR(wl, "creating shell surface failed\n"); - return false; - } + if (wl->xkb_context) + xkb_context_unref(wl->xkb_context); - wl_shell_surface_add_listener(wl->window.shell_surface, - &shell_surface_listener, wl); + if (wl->idle_inhibitor) + zwp_idle_inhibitor_v1_destroy(wl->idle_inhibitor); - wl_shell_surface_set_toplevel(wl->window.shell_surface); - wl_shell_surface_set_class(wl->window.shell_surface, "mpv"); - } + if (wl->idle_inhibit_manager) + zwp_idle_inhibit_manager_v1_destroy(wl->idle_inhibit_manager); - return true; -} + if (wl->shell) + zxdg_shell_v6_destroy(wl->shell); -static void destroy_window(struct vo_wayland_state *wl) -{ - if (wl->window.shell_surface) - wl_shell_surface_destroy(wl->window.shell_surface); + if (wl->shm) + wl_shm_destroy(wl->shm); - if (wl->window.video_surface) - wl_surface_destroy(wl->window.video_surface); + if (wl->dnd_devman) + wl_data_device_manager_destroy(wl->dnd_devman); - if (wl->frame.callback) - wl_callback_destroy(wl->frame.callback); -} + if (wl->server_decoration) + org_kde_kwin_server_decoration_destroy(wl->server_decoration); -static bool create_cursor(struct vo_wayland_state *wl) -{ - if (!wl->display.shm) { - MP_ERR(wl->vo, "no shm interface available\n"); - return false; - } + if (wl->server_decoration_manager) + org_kde_kwin_server_decoration_manager_destroy(wl->server_decoration_manager); - wl->cursor.surface = - wl_compositor_create_surface(wl->display.compositor); + if (wl->surface) + wl_surface_destroy(wl->surface); - if (!wl->cursor.surface) - return false; + if (wl->frame_callback) + wl_callback_destroy(wl->frame_callback); - wl->cursor.theme = wl_cursor_theme_load(NULL, 32, wl->display.shm); - wl->cursor.default_cursor = wl_cursor_theme_get_cursor(wl->cursor.theme, - "left_ptr"); + if (wl->display) { + close(wl_display_get_fd(wl->display)); + wl_display_disconnect(wl->display); + } - return true; + for (int n = 0; n < 2; n++) + close(wl->wakeup_pipe[n]); + talloc_free(wl); + vo->wl = NULL; } -static void destroy_cursor(struct vo_wayland_state *wl) +void vo_wayland_set_cb_exec(struct vo *vo, vo_wayland_frame_cb cb, void *cb_priv) { - if (wl->cursor.theme) - wl_cursor_theme_destroy(wl->cursor.theme); + struct vo_wayland_state *wl = vo->wl; - if (wl->cursor.surface) - wl_surface_destroy(wl->cursor.surface); + if (cb) { + wl->callback_exec = cb; + wl->callback_exec_priv = cb_priv; + wl->frame_callback = wl_surface_frame(wl->surface); + wl_callback_add_listener(wl->frame_callback, &frame_listener, wl); + } } -static bool create_input(struct vo_wayland_state *wl) +static struct vo_wayland_output *find_output(struct vo_wayland_state *wl, int index) { - wl->input.xkb.context = xkb_context_new(0); + int screen_id = 0; + struct vo_wayland_output *output; + wl_list_for_each_reverse(output, &wl->output_list, link) { + if (index == screen_id) + return output; - if (!wl->input.xkb.context) { - MP_ERR(wl, "failed to initialize input: check xkbcommon\n"); - return false; + screen_id++; } - - return true; + return NULL; } -static void destroy_input(struct vo_wayland_state *wl) +int vo_wayland_reconfig(struct vo *vo) { - if (wl->input.keyboard) { - wl_keyboard_destroy(wl->input.keyboard); - xkb_keymap_unref(wl->input.xkb.keymap); - xkb_state_unref(wl->input.xkb.state); + struct wl_output *wl_out = NULL; + struct mp_rect screenrc = { 0 }; + struct vo_wayland_state *wl = vo->wl; + + MP_VERBOSE(wl, "Reconfiguring!\n"); + + if ((wl_list_length(&wl->output_list) == 1) || (vo->opts->fsscreen_id >= 0)) { + int idx = wl_list_length(&wl->output_list) == 1 ? 0 : vo->opts->fsscreen_id; + struct vo_wayland_output *out = out = find_output(wl, idx); + if (!out) { + MP_ERR(wl, "Screen index %i not found/unavailable!\n", idx); + } else { + wl_out = out->output; + wl->current_output = out; + wl->scaling = out->scale; + screenrc = wl->current_output->geometry; + } } - if (wl->input.xkb.context) - xkb_context_unref(wl->input.xkb.context); - - if (wl->input.pointer) - wl_pointer_destroy(wl->input.pointer); - - if (wl->input.seat) - wl_seat_destroy(wl->input.seat); -} - -/*** mplayer2 interface ***/ - -int vo_wayland_init(struct vo *vo) -{ - vo->wayland = talloc_zero(NULL, struct vo_wayland_state); - struct vo_wayland_state *wl = vo->wayland; - *wl = (struct vo_wayland_state){ - .vo = vo, - .log = mp_log_new(wl, vo->log, "wayland"), - .wakeup_pipe = {-1, -1}, - }; - - wl_list_init(&wl->display.output_list); + struct vo_win_geometry geo; + vo_calc_window_geometry(vo, &screenrc, &geo); + vo_apply_window_geometry(vo, &geo); - if (!create_input(wl) - || !create_display(wl) - || !create_window(wl) - || !create_cursor(wl)) - { - vo_wayland_uninit(vo); - return false; - } + wl->geometry.x0 = 0; + wl->geometry.y0 = 0; + wl->geometry.x1 = vo->dwidth / wl->scaling; + wl->geometry.y1 = vo->dheight / wl->scaling; + wl->window_size = wl->geometry; + wl->aspect_ratio = vo->dwidth / (float)vo->dheight; - // create_display's roundtrip only adds the interfaces - // the second roundtrip receives output modes, geometry and more ... - wl_display_roundtrip(wl->display.display); - - struct vo_wayland_output *o = NULL; - wl_list_for_each(o, &wl->display.output_list, link) { - MP_VERBOSE(wl, "output received:\n" - "\tvendor: %s\n" - "\tmodel: %s\n" - "\tw: %d, h: %d\n" - "\tscale: %d\n" - "\tHz: %f\n", - o->make, o->model, - o->width, o->height, o->scale, - o->refresh_rate / 1000.0f); + if (vo->opts->fullscreen) { + /* If already fullscreen, fix resolution for the frame size change */ + if (wl->fullscreen && wl->current_output) { + wl->geometry.x0 = 0; + wl->geometry.y0 = 0; + wl->geometry.x1 = wl->current_output->width / wl->scaling; + wl->geometry.y1 = wl->current_output->height / wl->scaling; + } else { + zxdg_toplevel_v6_set_fullscreen(wl->xdg_toplevel, wl_out); + } } - mp_make_wakeup_pipe(wl->wakeup_pipe); + wl_surface_set_buffer_scale(wl->surface, wl->scaling); + wl_surface_commit(wl->surface); + if (wl->configured) + wl->pending_vo_events |= VO_EVENT_RESIZE; + wl->configured = 1; return true; } -void vo_wayland_uninit(struct vo *vo) +static int toggle_screensaver_inhibitor(struct vo_wayland_state *wl, int state) { - struct vo_wayland_state *wl = vo->wayland; - if (!wl) - return; + if (!wl->idle_inhibit_manager) + return 1; + if (state == (!!wl->idle_inhibitor)) + return 0; + if (state) { + MP_VERBOSE(wl, "Enabling idle inhibitor\n"); + struct zwp_idle_inhibit_manager_v1 *mgr = wl->idle_inhibit_manager; + wl->idle_inhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor(mgr, wl->surface); + } else { + MP_VERBOSE(wl, "Disabling the idle inhibitor\n"); + zwp_idle_inhibitor_v1_destroy(wl->idle_inhibitor); + } + return 0; +} - destroy_cursor(wl); - destroy_window(wl); - destroy_input(wl); - destroy_display(wl); - for (int n = 0; n < 2; n++) - close(wl->wakeup_pipe[n]); - talloc_free(wl); - vo->wayland = NULL; +static void toggle_fullscreen(struct vo_wayland_state *wl) +{ + if (wl->fullscreen) + zxdg_toplevel_v6_unset_fullscreen(wl->xdg_toplevel); + else + zxdg_toplevel_v6_set_fullscreen(wl->xdg_toplevel, NULL); } -static void vo_wayland_ontop(struct vo *vo) +static void update_window_title(struct vo_wayland_state *wl, char *title) { - struct vo_wayland_state *wl = vo->wayland; - if (!vo->opts->ontop) - return; - MP_DBG(wl, "going ontop\n"); - window_set_toplevel(wl); - schedule_resize(wl, 0, wl->window.width, wl->window.height); + talloc_free(wl->window_title); + wl->window_title = talloc_strdup(wl, title); + if (wl->xdg_toplevel) + zxdg_toplevel_v6_set_title(wl->xdg_toplevel, wl->window_title); } -static void vo_wayland_fullscreen(struct vo *vo) +static void check_dnd_fd(struct vo_wayland_state *wl) { - struct vo_wayland_state *wl = vo->wayland; - if (!wl->display.shell) + if (wl->dnd_fd == -1) return; - struct wl_output *fs_output = wl->display.fs_output; + struct pollfd fdp = { wl->dnd_fd, POLLIN | POLLERR | POLLHUP, 0 }; + if (poll(&fdp, 1, 0) <= 0) + return; - if (vo->opts->fullscreen) { - MP_DBG(wl, "going fullscreen\n"); - wl->window.is_fullscreen = true; - wl->window.p_width = wl->window.width; - wl->window.p_height = wl->window.height; - if (wl->display.current_output) - schedule_resize(wl, 0, wl->display.current_output->width, - wl->display.current_output->height); - wl_shell_surface_set_fullscreen(wl->window.shell_surface, - WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, - 0, fs_output); + if (fdp.revents & POLLIN) { + ptrdiff_t offset = 0; + size_t data_read = 0; + const size_t chunk_size = 1; + uint8_t *buffer = ta_zalloc_size(wl, chunk_size); + if (!buffer) + goto end; + + while ((data_read = read(wl->dnd_fd, buffer + offset, chunk_size)) > 0) { + offset += data_read; + buffer = ta_realloc_size(wl, buffer, offset + chunk_size); + memset(buffer + offset, 0, chunk_size); + if (!buffer) + goto end; + } + + MP_VERBOSE(wl, "Read %td bytes from the DND fd\n", offset); + + struct bstr file_list = bstr0(buffer); + mp_event_drop_mime_data(wl->vo->input_ctx, wl->dnd_mime_type, + file_list, wl->dnd_action); + talloc_free(buffer); +end: + wl_data_offer_finish(wl->dnd_offer); + talloc_free(wl->dnd_mime_type); + wl->dnd_mime_type = NULL; + wl->dnd_mime_score = 0; } - else { - MP_DBG(wl, "leaving fullscreen\n"); - wl->window.is_fullscreen = false; - window_set_toplevel(wl); - schedule_resize(wl, 0, wl->window.p_width, wl->window.p_height); + if (fdp.revents & (POLLIN | POLLERR | POLLHUP)) { + close(wl->dnd_fd); + wl->dnd_fd = -1; } } -static void vo_wayland_update_screeninfo(struct vo *vo, struct mp_rect *screenrc) +static char **get_displays_spanned(struct vo_wayland_state *wl) { - struct vo_wayland_state *wl = vo->wayland; - struct mp_vo_opts *opts = vo->opts; - - *screenrc = (struct mp_rect){0}; - - int screen_id = 0; - + char **names = NULL; + int displays_spanned = 0; struct vo_wayland_output *output; - struct vo_wayland_output *first_output = NULL; - struct vo_wayland_output *fsscreen_output = NULL; - - if (opts->fsscreen_id >= 0) { - wl_list_for_each_reverse(output, &wl->display.output_list, link) { - if (!output || !output->width) - continue; - - if (opts->fsscreen_id == screen_id) - fsscreen_output = output; - - screen_id++; - } - } - - if (fsscreen_output) { - wl->display.fs_output = fsscreen_output->output; - screenrc->x1 = fsscreen_output->width; - screenrc->y1 = fsscreen_output->height; - } - else { - wl->display.fs_output = NULL; /* current output is always 0 */ - - if (first_output) { - screenrc->x1 = wl->display.current_output->width; - screenrc->y1 = wl->display.current_output->height; - } + wl_list_for_each_reverse(output, &wl->output_list, link) { + if (output->has_surface) + MP_TARRAY_APPEND(NULL, names, displays_spanned, + talloc_strdup(NULL, output->model)); } - - wl->window.fs_width = screenrc->x1; - wl->window.fs_height = screenrc->y1; + MP_TARRAY_APPEND(NULL, names, displays_spanned, NULL); + return names; } int vo_wayland_control(struct vo *vo, int *events, int request, void *arg) { - struct vo_wayland_state *wl = vo->wayland; - wl_display_dispatch_pending(wl->display.display); + struct vo_wayland_state *wl = vo->wl; + wl_display_dispatch_pending(wl->display); switch (request) { - case VOCTRL_CHECK_EVENTS: - *events |= wl->window.events; - wl->window.events = 0; - return VO_TRUE; - case VOCTRL_FULLSCREEN: - vo_wayland_fullscreen(vo); - return VO_TRUE; - case VOCTRL_ONTOP: - vo_wayland_ontop(vo); - return VO_TRUE; - case VOCTRL_GET_UNFS_WINDOW_SIZE: { - int *s = arg, scale = 1; - if (wl->display.current_output) - scale = wl->display.current_output->scale; - s[0] = scale*wl->window.width; - s[1] = scale*wl->window.height; - return VO_TRUE; - } - case VOCTRL_SET_UNFS_WINDOW_SIZE: { - int *s = arg; - if (!wl->window.is_fullscreen) - schedule_resize(wl, 0, s[0], s[1]); - return VO_TRUE; - } - case VOCTRL_SET_CURSOR_VISIBILITY: - if (*(bool *)arg) { - if (!wl->cursor.visible) - show_cursor(wl); + case VOCTRL_CHECK_EVENTS: + check_dnd_fd(wl); + *events |= wl->pending_vo_events; + wl->pending_vo_events = 0; + return VO_TRUE; + case VOCTRL_UPDATE_WINDOW_TITLE: + update_window_title(wl, (char *)arg); + return VO_TRUE; + case VOCTRL_FULLSCREEN: + toggle_fullscreen(wl); + return VO_TRUE; + case VOCTRL_GET_FULLSCREEN: + *(int *)arg = wl->fullscreen; + return VO_TRUE; + case VOCTRL_SET_CURSOR_VISIBILITY: + set_cursor_visibility(wl, *(bool *)arg); + return VO_TRUE; + case VOCTRL_BORDER: + return set_border_decorations(wl, vo->opts->border); + case VOCTRL_GET_DISPLAY_NAMES: { + *(char ***)arg = get_displays_spanned(wl); + return VO_TRUE; } - else { - if (wl->cursor.visible) - hide_cursor(wl); + case VOCTRL_KILL_SCREENSAVER: + if (toggle_screensaver_inhibitor(wl, true)) + return VO_NOTAVAIL; + return VO_TRUE; + case VOCTRL_RESTORE_SCREENSAVER: + if (toggle_screensaver_inhibitor(wl, false)) + return VO_NOTAVAIL; + return VO_TRUE; + case VOCTRL_GET_UNFS_WINDOW_SIZE: { + int *s = arg; + s[0] = mp_rect_w(wl->geometry)*wl->scaling; + s[1] = mp_rect_h(wl->geometry)*wl->scaling; + return VO_TRUE; } - wl->cursor.visible = *(bool *)arg; - return VO_TRUE; - case VOCTRL_UPDATE_WINDOW_TITLE: - window_set_title(wl, (char*) arg); - return VO_TRUE; - case VOCTRL_GET_DISPLAY_FPS: { - if (!wl->display.current_output) - break; - - // refresh rate is stored in milli-Hertz (mHz) - double fps = wl->display.current_output->refresh_rate / 1000.0f; - *(double*) arg = fps; - return VO_TRUE; - } + case VOCTRL_SET_UNFS_WINDOW_SIZE: { + int *s = arg; + if (!wl->fullscreen) { + wl->geometry.x0 = 0; + wl->geometry.y0 = 0; + wl->geometry.x1 = s[0]/wl->scaling; + wl->geometry.y1 = s[1]/wl->scaling; + wl->pending_vo_events |= VO_EVENT_RESIZE; + } + return VO_TRUE; + } + case VOCTRL_GET_DISPLAY_FPS: + if (!wl->current_output) + return VO_NOTAVAIL; + *(double *)arg = wl->current_output->refresh_rate; + return VO_TRUE; } - return VO_NOTIMPL; -} -bool vo_wayland_config(struct vo *vo) -{ - struct vo_wayland_state *wl = vo->wayland; - - struct mp_rect screenrc; - vo_wayland_update_screeninfo(vo, &screenrc); - - struct vo_win_geometry geo; - vo_calc_window_geometry(vo, &screenrc, &geo); - vo_apply_window_geometry(vo, &geo); - - wl->window.p_width = vo->dwidth; - wl->window.p_height = vo->dheight; - wl->window.aspect = vo->dwidth / (float) MPMAX(vo->dheight, 1); - - wl->window.width = vo->dwidth; - wl->window.height = vo->dheight; - vo_wayland_fullscreen(vo); - - return true; -} - -void vo_wayland_request_frame(struct vo *vo, void *data, vo_wayland_frame_cb cb) -{ - struct vo_wayland_state *wl = vo->wayland; - wl->frame.data = data; - wl->frame.function = cb; - MP_DBG(wl, "restart frame callback\n"); - frame_callback(wl, NULL, 0); + return VO_NOTIMPL; } void vo_wayland_wakeup(struct vo *vo) { - struct vo_wayland_state *wl = vo->wayland; + struct vo_wayland_state *wl = vo->wl; (void)write(wl->wakeup_pipe[1], &(char){0}, 1); } void vo_wayland_wait_events(struct vo *vo, int64_t until_time_us) { - struct vo_wayland_state *wl = vo->wayland; - struct wl_display *dp = wl->display.display; + struct vo_wayland_state *wl = vo->wl; + struct wl_display *display = wl->display; + + if (wl->display_fd == -1) + return; struct pollfd fds[2] = { - {.fd = wl->display.display_fd, .events = POLLIN }, - {.fd = wl->wakeup_pipe[0], .events = POLLIN }, + {.fd = wl->display_fd, .events = POLLIN }, + {.fd = wl->wakeup_pipe[0], .events = POLLIN }, }; int64_t wait_us = until_time_us - mp_time_us(); int timeout_ms = MPCLAMP((wait_us + 999) / 1000, 0, 10000); - wl_display_dispatch_pending(dp); - wl_display_flush(dp); + wl_display_dispatch_pending(display); + wl_display_flush(display); poll(fds, 2, timeout_ms); if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { - MP_FATAL(wl, "error occurred on the display fd: " - "closing file descriptor\n"); - close(wl->display.display_fd); + MP_FATAL(wl, "Error occurred on the display fd, closing\n"); + close(wl->display_fd); + wl->display_fd = -1; mp_input_put_key(vo->input_ctx, MP_KEY_CLOSE_WIN); } if (fds[0].revents & POLLIN) - wl_display_dispatch(dp); + wl_display_dispatch(display); if (fds[1].revents & POLLIN) mp_flush_wakeup_pipe(wl->wakeup_pipe[0]); diff --git a/video/out/wayland_common.h b/video/out/wayland_common.h index 4bb90d6520..60d5924efa 100644 --- a/video/out/wayland_common.h +++ b/video/out/wayland_common.h @@ -1,6 +1,5 @@ /* * This file is part of mpv video player. - * Copyright © 2013 Alexander Preisinger * * mpv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,133 +18,100 @@ #ifndef MPLAYER_WAYLAND_COMMON_H #define MPLAYER_WAYLAND_COMMON_H -#include -#include #include #include #include -#include "config.h" +#include "vo.h" +#include "input/event.h" -#if HAVE_GL_WAYLAND -#include -#include -#include -#endif - -struct vo; +typedef void (*vo_wayland_frame_cb)(struct vo_wayland_state *wl, void *priv, uint32_t time); struct vo_wayland_output { - uint32_t id; /* unique name */ + uint32_t id; struct wl_output *output; + struct mp_rect geometry; + int width; + int height; + int scale; uint32_t flags; - int32_t width; - int32_t height; - int32_t scale; - int32_t refresh_rate; // fps (mHz) + double refresh_rate; const char *make; const char *model; + int has_surface; struct wl_list link; }; -typedef void (*vo_wayland_frame_cb)(void *data, uint32_t time); - struct vo_wayland_state { - struct vo *vo; - struct mp_log* log; + struct mp_log *log; + struct vo *vo; + struct wl_display *display; + struct wl_shm *shm; + struct wl_compositor *compositor; + struct wl_registry *registry; + + /* State */ + struct mp_rect geometry; + struct mp_rect window_size; + float aspect_ratio; + int fullscreen; + char *window_title; int wakeup_pipe[2]; - - struct { - void *data; - vo_wayland_frame_cb function; - struct wl_callback *callback; - } frame; - -#if HAVE_GL_WAYLAND - struct { - EGLSurface egl_surface; - - struct wl_egl_window *egl_window; - - struct { - EGLDisplay dpy; - EGLContext ctx; - EGLConfig conf; - } egl; - } egl_context; -#endif - - struct { - int fd; - struct wl_display *display; - struct wl_registry *registry; - struct wl_compositor *compositor; - struct wl_shell *shell; - - struct wl_list output_list; - struct wl_output *fs_output; /* fullscreen output */ - struct vo_wayland_output *current_output; - - int display_fd; - - struct wl_shm *shm; - - struct wl_subcompositor *subcomp; - } display; - - struct { - int32_t width; // current size of the window - int32_t height; - int32_t p_width; // previous sizes for leaving fullscreen - int32_t p_height; - int32_t sh_width; // sheduled width for resizing - int32_t sh_height; - int32_t sh_x; // x, y calculated with the drag edges for moving - int32_t sh_y; - float aspect; - - bool is_fullscreen; // don't keep aspect ratio in fullscreen mode - int32_t fs_width; // fullscreen sizes - int32_t fs_height; - - struct wl_surface *video_surface; - int32_t mouse_x; // mouse position inside the surface - int32_t mouse_y; - struct wl_shell_surface *shell_surface; - int events; /* mplayer events (VO_EVENT_RESIZE) */ - } window; - - struct { - struct wl_cursor *default_cursor; - struct wl_cursor_theme *theme; - struct wl_surface *surface; - - /* pointer for fading out */ - bool visible; - struct wl_pointer *pointer; - uint32_t serial; - } cursor; - - struct { - struct wl_seat *seat; - struct wl_keyboard *keyboard; - struct wl_pointer *pointer; - - struct { - struct xkb_context *context; - struct xkb_keymap *keymap; - struct xkb_state *state; - } xkb; - } input; + int pending_vo_events; + int mouse_x; + int mouse_y; + int scaling; + int touch_entries; + uint32_t pointer_id; + int display_fd; + int configured; + vo_wayland_frame_cb callback_exec; + void *callback_exec_priv; + struct wl_callback *frame_callback; + struct wl_list output_list; + struct vo_wayland_output *current_output; + + /* Shell */ + struct wl_surface *surface; + struct zxdg_shell_v6 *shell; + struct zxdg_toplevel_v6 *xdg_toplevel; + struct zxdg_surface_v6 *xdg_surface; + struct org_kde_kwin_server_decoration_manager *server_decoration_manager; + struct org_kde_kwin_server_decoration *server_decoration; + struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager; + struct zwp_idle_inhibitor_v1 *idle_inhibitor; + + /* Input */ + struct wl_seat *seat; + struct wl_pointer *pointer; + struct wl_touch *touch; + struct wl_keyboard *keyboard; + struct xkb_context *xkb_context; + struct xkb_keymap *xkb_keymap; + struct xkb_state *xkb_state; + + /* DND */ + struct wl_data_device_manager *dnd_devman; + struct wl_data_device *dnd_ddev; + struct wl_data_offer *dnd_offer; + enum mp_dnd_action dnd_action; + char *dnd_mime_type; + int dnd_mime_score; + int dnd_fd; + + /* Cursor */ + struct wl_cursor_theme *cursor_theme; + struct wl_cursor *default_cursor; + struct wl_surface *cursor_surface; }; int vo_wayland_init(struct vo *vo); -void vo_wayland_uninit(struct vo *vo); -bool vo_wayland_config(struct vo *vo); +int vo_wayland_reconfig(struct vo *vo); int vo_wayland_control(struct vo *vo, int *events, int request, void *arg); +void vo_wayland_check_events(struct vo *vo); +void vo_wayland_uninit(struct vo *vo); void vo_wayland_wakeup(struct vo *vo); void vo_wayland_wait_events(struct vo *vo, int64_t until_time_us); -void vo_wayland_request_frame(struct vo *vo, void *data, vo_wayland_frame_cb cb); +void vo_wayland_set_cb_exec(struct vo *vo, vo_wayland_frame_cb cb, void *cb_priv); #endif /* MPLAYER_WAYLAND_COMMON_H */ - diff --git a/waftools/checks/custom.py b/waftools/checks/custom.py index d8065a35f2..698742406f 100644 --- a/waftools/checks/custom.py +++ b/waftools/checks/custom.py @@ -4,7 +4,7 @@ from waflib import Utils import os __all__ = ["check_pthreads", "check_iconv", "check_lua", - "check_cocoa", "check_openal"] + "check_cocoa", "check_openal", "check_wl_protocols"] pthreads_program = load_fragment('pthreads.c') @@ -83,6 +83,15 @@ def check_lua(ctx, dependency_identifier): return True return False +def check_wl_protocols(ctx, dependency_identifier): + def fn(ctx, dependency_identifier): + ret = check_pkg_config_datadir("wayland-protocols") + ret = ret(ctx, dependency_identifier) + if ret != None: + ctx.env.WL_PROTO_DIR = ret.split()[0] + return ret + return fn(ctx, dependency_identifier) + def check_cocoa(ctx, dependency_identifier): fn = check_cc( fragment = load_fragment('cocoa.m'), diff --git a/waftools/checks/generic.py b/waftools/checks/generic.py index 153cf9463d..093c600f34 100644 --- a/waftools/checks/generic.py +++ b/waftools/checks/generic.py @@ -7,7 +7,8 @@ __all__ = [ "check_pkg_config", "check_pkg_config_mixed", "check_pkg_config_mixed_all", "check_pkg_config_cflags", "check_cc", "check_statement", "check_libs", "check_headers", "compose_checks", "check_true", "any_version", - "load_fragment", "check_stub", "check_ctx_vars", "check_program"] + "load_fragment", "check_stub", "check_ctx_vars", "check_program", + "check_pkg_config_datadir"] any_version = None @@ -82,6 +83,9 @@ def check_pkg_config_mixed_all(*all_args, **kw_ext): def check_pkg_config_cflags(*args, **kw_ext): return _check_pkg_config([], ["--cflags"], *args, **kw_ext) +def check_pkg_config_datadir(*args, **kw_ext): + return _check_pkg_config([], ["--variable=pkgdatadir"], *args, **kw_ext) + def _check_pkg_config(_dyn_libs, _pkgc_args, *args, **kw_ext): def fn(ctx, dependency_identifier, **kw): argsl = list(args) @@ -113,7 +117,7 @@ def _check_pkg_config(_dyn_libs, _pkgc_args, *args, **kw_ext): # added only at its first occurrence. original_append_unique = ConfigSet.append_unique ConfigSet.append_unique = ConfigSet.append_value - result = bool(ctx.check_cfg(**opts)) + result = ctx.check_cfg(**opts) ConfigSet.append_unique = original_append_unique defkey = inflector.define_key(dependency_identifier) diff --git a/waftools/generators/sources.py b/waftools/generators/sources.py index 96224cf839..b0b423edda 100644 --- a/waftools/generators/sources.py +++ b/waftools/generators/sources.py @@ -9,6 +9,9 @@ def __zshcomp_cmd__(ctx, argument): return '"${{BIN_PERL}}" "{0}/TOOLS/zsh.pl" "{1}" > "${{TGT}}"' \ .format(ctx.srcnode.abspath(), argument) +def __wayland_scanner_cmd__(ctx, mode, dir, src): + return "${{WAYSCAN}} {0} < {1}/{2} > ${{TGT}}".format(mode, dir, src) + def __file2string__(ctx, **kwargs): ctx( rule = __file2string_cmd__(ctx), @@ -51,5 +54,24 @@ def __zshcomp__(ctx, **kwargs): **kwargs ) -BuildContext.file2string = __file2string__ -BuildContext.zshcomp = __zshcomp__ +def __wayland_protocol_code__(ctx, **kwargs): + ctx( + rule = __wayland_scanner_cmd__(ctx, 'code', kwargs['proto_dir'], + kwargs['protocol'] + '.xml'), + name = os.path.basename(kwargs['target']), + **kwargs + ) + +def __wayland_protocol_header__(ctx, **kwargs): + ctx( + rule = __wayland_scanner_cmd__(ctx, 'client-header', kwargs['proto_dir'], + kwargs['protocol'] + '.xml'), + before = ('c',), + name = os.path.basename(kwargs['target']), + **kwargs + ) + +BuildContext.file2string = __file2string__ +BuildContext.wayland_protocol_code = __wayland_protocol_code__ +BuildContext.wayland_protocol_header = __wayland_protocol_header__ +BuildContext.zshcomp = __zshcomp__ diff --git a/wscript b/wscript index 6707f77198..941131cf4c 100644 --- a/wscript +++ b/wscript @@ -593,9 +593,18 @@ video_output_features = [ 'desc': 'GBM', 'deps': 'gbm.h', 'func': check_pkg_config('gbm'), + } , { + 'name': '--wayland-scanner', + 'desc': 'wayland-scanner', + 'func': check_program('wayland-scanner', 'WAYSCAN') + } , { + 'name': '--wayland-protocols', + 'desc': 'wayland-protocols', + 'func': check_wl_protocols } , { 'name': '--wayland', 'desc': 'Wayland', + 'deps': 'wayland-protocols && wayland-scanner', 'func': check_pkg_config('wayland-client', '>= 1.6.0', 'wayland-cursor', '>= 1.6.0', 'xkbcommon', '>= 0.3.0'), diff --git a/wscript_build.py b/wscript_build.py index 5d8ee616fe..25875dcfcd 100644 --- a/wscript_build.py +++ b/wscript_build.py @@ -116,6 +116,26 @@ def build(ctx): target = "player/javascript/defaults.js.inc", ) + if ctx.dependency_satisfied('wayland'): + ctx.wayland_protocol_code(proto_dir = ctx.env.WL_PROTO_DIR, + protocol = "unstable/xdg-shell/xdg-shell-unstable-v6", + target = "video/out/wayland/xdg-shell-v6.c") + ctx.wayland_protocol_header(proto_dir = ctx.env.WL_PROTO_DIR, + protocol = "unstable/xdg-shell/xdg-shell-unstable-v6", + target = "video/out/wayland/xdg-shell-v6.h") + ctx.wayland_protocol_code(proto_dir = ctx.env.WL_PROTO_DIR, + protocol = "unstable/idle-inhibit/idle-inhibit-unstable-v1", + target = "video/out/wayland/idle-inhibit-v1.c") + ctx.wayland_protocol_header(proto_dir = ctx.env.WL_PROTO_DIR, + protocol = "unstable/idle-inhibit/idle-inhibit-unstable-v1", + target = "video/out/wayland/idle-inhibit-v1.h") + ctx.wayland_protocol_code(proto_dir = "../video/out/wayland", + protocol = "server-decoration", + target = "video/out/wayland/srv-decor.c") + ctx.wayland_protocol_header(proto_dir = "../video/out/wayland", + protocol = "server-decoration", + target = "video/out/wayland/srv-decor.h") + ctx(features = "ebml_header", target = "ebml_types.h") ctx(features = "ebml_definitions", target = "ebml_defs.c") @@ -454,6 +474,9 @@ def build(ctx): ( "video/out/vulkan/spirv_nvidia.c", "vulkan" ), ( "video/out/win32/exclusive_hack.c", "gl-win32" ), ( "video/out/wayland_common.c", "wayland" ), + ( "video/out/wayland/xdg-shell-v6.c", "wayland" ), + ( "video/out/wayland/idle-inhibit-v1.c", "wayland" ), + ( "video/out/wayland/srv-decor.c", "wayland" ), ( "video/out/win_state.c"), ( "video/out/x11_common.c", "x11" ), ( "video/out/drm_common.c", "drm" ), -- cgit v1.2.3