mirror of
https://github.com/mpv-player/mpv
synced 2025-03-19 01:47:38 +00:00
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.
This commit is contained in:
parent
980116360b
commit
68f9ee7e0b
@ -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);
|
||||
|
||||
if (x != 0)
|
||||
x = wl->window.width - width;
|
||||
|
||||
if (y != 0)
|
||||
y = wl->window.height - height;
|
||||
|
||||
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);
|
||||
|
||||
wl->window.width = width;
|
||||
wl->window.height = height;
|
||||
|
||||
/* set size for mplayer */
|
||||
wl->vo->dwidth = scale*wl->window.width;
|
||||
wl->vo->dheight = scale*wl->window.height;
|
||||
wl->vo->want_redraw = true;
|
||||
}
|
||||
|
||||
static void waylandgl_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);
|
||||
}
|
||||
|
||||
static bool egl_create_context(struct ra_ctx *ctx, struct vo_wayland_state *wl)
|
||||
{
|
||||
GL *gl = ctx->priv = talloc_zero(ctx, GL);
|
||||
|
||||
if (!(wl->egl_context.egl.dpy = eglGetDisplay(wl->display.display)))
|
||||
return false;
|
||||
|
||||
if (eglInitialize(wl->egl_context.egl.dpy, 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))
|
||||
return false;
|
||||
|
||||
eglMakeCurrent(wl->egl_context.egl.dpy, NULL, NULL, wl->egl_context.egl.ctx);
|
||||
|
||||
mpegl_load_functions(gl, wl->log);
|
||||
|
||||
struct ra_gl_ctx_params params = {
|
||||
.swap_buffers = waylandgl_swap_buffers,
|
||||
.native_display_type = "wl",
|
||||
.native_display = wl->display.display,
|
||||
struct priv {
|
||||
GL gl;
|
||||
EGLDisplay egl_display;
|
||||
EGLContext egl_context;
|
||||
EGLSurface egl_surface;
|
||||
EGLConfig egl_config;
|
||||
struct wl_egl_window *egl_window;
|
||||
};
|
||||
|
||||
if (!ra_gl_ctx_init(ctx, gl, params))
|
||||
static void resize(struct ra_ctx *ctx)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
struct vo_wayland_state *wl = ctx->vo->wl;
|
||||
|
||||
MP_VERBOSE(wl, "Handling resizing on the egl side\n");
|
||||
|
||||
const int32_t width = wl->scaling*mp_rect_w(wl->geometry);
|
||||
const int32_t height = wl->scaling*mp_rect_h(wl->geometry);
|
||||
|
||||
wl_surface_set_buffer_scale(wl->surface, wl->scaling);
|
||||
wl_egl_window_resize(p->egl_window, width, height, 0, 0);
|
||||
|
||||
wl->vo->dwidth = width;
|
||||
wl->vo->dheight = height;
|
||||
}
|
||||
|
||||
static void wayland_egl_swap_buffers(struct ra_ctx *ctx)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
eglSwapBuffers(p->egl_display, p->egl_surface);
|
||||
}
|
||||
|
||||
static bool egl_create_context(struct ra_ctx *ctx)
|
||||
{
|
||||
struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
|
||||
struct vo_wayland_state *wl = ctx->vo->wl;
|
||||
|
||||
if (!(p->egl_display = eglGetDisplay(wl->display)))
|
||||
return false;
|
||||
|
||||
if (eglInitialize(p->egl_display, NULL, NULL) != EGL_TRUE)
|
||||
return false;
|
||||
|
||||
if (!mpegl_create_context(ctx, p->egl_display, &p->egl_context,
|
||||
&p->egl_config))
|
||||
return false;
|
||||
|
||||
eglMakeCurrent(p->egl_display, NULL, NULL, p->egl_context);
|
||||
|
||||
mpegl_load_functions(&p->gl, wl->log);
|
||||
|
||||
struct ra_gl_ctx_params params = {
|
||||
.swap_buffers = wayland_egl_swap_buffers,
|
||||
.native_display_type = "wl",
|
||||
.native_display = wl->display,
|
||||
};
|
||||
|
||||
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);
|
||||
struct priv *p = ctx->priv;
|
||||
struct vo_wayland_state *wl = ctx->vo->wl;
|
||||
|
||||
wl->egl_context.egl_surface = eglCreateWindowSurface(wl->egl_context.egl.dpy,
|
||||
wl->egl_context.egl.conf,
|
||||
wl->egl_context.egl_window,
|
||||
NULL);
|
||||
p->egl_window = wl_egl_window_create(wl->surface, mp_rect_w(wl->geometry),
|
||||
mp_rect_h(wl->geometry));
|
||||
|
||||
eglMakeCurrent(wl->egl_context.egl.dpy,
|
||||
wl->egl_context.egl_surface,
|
||||
wl->egl_context.egl_surface,
|
||||
wl->egl_context.egl.ctx);
|
||||
p->egl_surface = eglCreateWindowSurface(p->egl_display, p->egl_config,
|
||||
p->egl_window, NULL);
|
||||
|
||||
wl_display_dispatch_pending(wl->display.display);
|
||||
eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface, p->egl_context);
|
||||
|
||||
/**
|
||||
* <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(p->egl_display, 0);
|
||||
|
||||
eglSwapInterval(wl->egl_context.egl.dpy, 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,
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
struct vo_wayland_state *wl = ctx->vo->wl;
|
||||
|
||||
if (wl->display.current_output)
|
||||
scale = wl->display.current_output->scale;
|
||||
MP_VERBOSE(wl, "Handling resizing on the vk side\n");
|
||||
|
||||
MP_VERBOSE(wl, "resizing %dx%d -> %dx%d\n", wl->window.width,
|
||||
wl->window.height,
|
||||
width,
|
||||
height);
|
||||
const int32_t width = wl->scaling*mp_rect_w(wl->geometry);
|
||||
const int32_t height = wl->scaling*mp_rect_h(wl->geometry);
|
||||
|
||||
wl_surface_set_buffer_scale(wl->window.video_surface, scale);
|
||||
int err = ra_vk_ctx_resize(ctx->swapchain, scale*width, scale*height);
|
||||
wl_surface_set_buffer_scale(wl->surface, wl->scaling);
|
||||
|
||||
wl->window.width = width;
|
||||
wl->window.height = height;
|
||||
|
||||
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,
|
||||
};
|
||||
|
94
video/out/wayland/server-decoration.xml
Normal file
94
video/out/wayland/server-decoration.xml
Normal file
@ -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>
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
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 vo *vo;
|
||||
struct wl_display *display;
|
||||
struct wl_registry *registry;
|
||||
struct wl_shm *shm;
|
||||
struct wl_compositor *compositor;
|
||||
struct wl_shell *shell;
|
||||
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];
|
||||
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 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;
|
||||
/* 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;
|
||||
|
||||
/* pointer for fading out */
|
||||
bool visible;
|
||||
struct wl_pointer *pointer;
|
||||
uint32_t serial;
|
||||
} cursor;
|
||||
|
||||
struct {
|
||||
/* Input */
|
||||
struct wl_seat *seat;
|
||||
struct wl_keyboard *keyboard;
|
||||
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;
|
||||
|
||||
struct {
|
||||
struct xkb_context *context;
|
||||
struct xkb_keymap *keymap;
|
||||
struct xkb_state *state;
|
||||
} xkb;
|
||||
} input;
|
||||
/* 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 */
|
||||
|
||||
|
@ -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'),
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
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__
|
||||
|
9
wscript
9
wscript
@ -593,9 +593,18 @@ video_output_features = [
|
||||
'desc': 'GBM',
|
||||
'deps': 'gbm.h',
|
||||
'func': check_pkg_config('gbm'),
|
||||
} , {
|
||||
'name': '--wayland-scanner',
|
||||
'desc': 'wayland-scanner',
|
||||
'func': check_program('wayland-scanner', 'WAYSCAN')
|
||||
} , {
|
||||
'name': '--wayland-protocols',
|
||||
'desc': 'wayland-protocols',
|
||||
'func': check_wl_protocols
|
||||
} , {
|
||||
'name': '--wayland',
|
||||
'desc': 'Wayland',
|
||||
'deps': 'wayland-protocols && wayland-scanner',
|
||||
'func': check_pkg_config('wayland-client', '>= 1.6.0',
|
||||
'wayland-cursor', '>= 1.6.0',
|
||||
'xkbcommon', '>= 0.3.0'),
|
||||
|
@ -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" ),
|
||||
|
Loading…
Reference in New Issue
Block a user