aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Rostislav Pehlivanov <atomnuker@gmail.com>2017-10-01 21:16:49 +0100
committerGravatar Rostislav Pehlivanov <atomnuker@gmail.com>2017-10-03 19:36:02 +0100
commit68f9ee7e0b3fdddfa42fa11a15d9ae84460d5e19 (patch)
tree91b3c4dd976c54a241dc17d04ccdd15e1cf70ff8
parent980116360b0f393e16064ec3b7a4ef9efb14372e (diff)
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.
-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
-rw-r--r--waftools/checks/custom.py11
-rw-r--r--waftools/checks/generic.py8
-rw-r--r--waftools/generators/sources.py26
-rw-r--r--wscript9
-rw-r--r--wscript_build.py23
11 files changed, 1281 insertions, 1058 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 +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, &registry_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, &registry_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 <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
@@ -19,133 +18,100 @@
#ifndef MPLAYER_WAYLAND_COMMON_H
#define MPLAYER_WAYLAND_COMMON_H
-#include <stdint.h>
-#include <stdbool.h>
#include <wayland-client.h>
#include <wayland-cursor.h>
#include <xkbcommon/xkbcommon.h>
-#include "config.h"
+#include "vo.h"
+#include "input/event.h"
-#if HAVE_GL_WAYLAND
-#include <wayland-egl.h>
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#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
@@ -594,8 +594,17 @@ video_output_features = [
'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" ),