summaryrefslogtreecommitdiffstats
path: root/video/out
diff options
context:
space:
mode:
authorRostislav Pehlivanov <atomnuker@gmail.com>2017-10-01 21:16:49 +0100
committerRostislav Pehlivanov <atomnuker@gmail.com>2017-10-03 19:36:02 +0100
commit68f9ee7e0b3fdddfa42fa11a15d9ae84460d5e19 (patch)
tree91b3c4dd976c54a241dc17d04ccdd15e1cf70ff8 /video/out
parent980116360b0f393e16064ec3b7a4ef9efb14372e (diff)
downloadmpv-68f9ee7e0b3fdddfa42fa11a15d9ae84460d5e19.tar.bz2
mpv-68f9ee7e0b3fdddfa42fa11a15d9ae84460d5e19.tar.xz
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.
Diffstat (limited to 'video/out')
-rw-r--r--video/out/opengl/context_wayland.c193
-rw-r--r--video/out/vo.h2
-rw-r--r--video/out/vulkan/context_wayland.c73
-rw-r--r--video/out/wayland/server-decoration.xml94
-rw-r--r--video/out/wayland_common.c1720
-rw-r--r--video/out/wayland_common.h180
6 files changed, 1209 insertions, 1053 deletions
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 <http://www.gnu.org/licenses/>.
*/
+#include <wayland-egl.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
#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);
-
- /**
- * <http://lists.freedesktop.org/archives/wayland-devel/2013-November/012019.html>
- *
- * 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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="server_decoration">
+ <copyright><![CDATA[
+ Copyright (C) 2015 Martin Gräßlin
+
+ 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, see <http://www.gnu.org/licenses/>.
+ ]]></copyright>
+ <interface name="org_kde_kwin_server_decoration_manager" version="1">
+ <description summary="Server side window decoration manager">
+ 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.
+ </description>
+ <request name="create">
+ <description summary="Create a server-side decoration object for a given surface">
+ 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.
+ </description>
+ <arg name="id" type="new_id" interface="org_kde_kwin_server_decoration"/>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </request>
+ <enum name="mode">
+ <description summary="Possible values to use in request_mode and the event mode."/>
+ <entry name="None" value="0" summary="Undecorated: The surface is not decorated at all, neither server nor client-side. An example is a popup surface which should not be decorated."/>
+ <entry name="Client" value="1" summary="Client-side decoration: The decoration is part of the surface and the client."/>
+ <entry name="Server" value="2" summary="Server-side decoration: The server embeds the surface into a decoration frame."/>
+ </enum>
+ <event name="default_mode">
+ <description summary="The default mode used on the server">
+ 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.
+ </description>
+ <arg name="mode" type="uint" summary="The default decoration mode applied to newly created server decorations."/>
+ </event>
+ </interface>
+ <interface name="org_kde_kwin_server_decoration" version="1">
+ <request name="release" type="destructor">
+ <description summary="release the server decoration object"/>
+ </request>
+ <enum name="mode">
+ <description summary="Possible values to use in request_mode and the event mode."/>
+ <entry name="None" value="0" summary="Undecorated: The surface is not decorated at all, neither server nor client-side. An example is a popup surface which should not be decorated."/>
+ <entry name="Client" value="1" summary="Client-side decoration: The decoration is part of the surface and the client."/>
+ <entry name="Server" value="2" summary="Server-side decoration: The server embeds the surface into a decoration frame."/>
+ </enum>
+ <request name="request_mode">
+ <description summary="The decoration mode the surface wants to use."/>
+ <arg name="mode" type="uint" summary="The mode this surface wants to use."/>
+ </request>
+ <event name="mode">
+ <description summary="The new decoration mode applied by the server">
+ 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.
+ </description>
+ <arg name="mode" type="uint" summary="The decoration mode applied to the surface by the server."/>
+ </event>
+ </interface>
+</protocol>
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 <alexander.preisinger@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <assert.h>
#include <poll.h>
#include <unistd.h>
-
-#include <sys/mman.h>
#include <linux/input.h>
-
-#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 +29