Skip to content
This repository
Browse code

wayland: add wayland support

All wayland only specific routines are placed in wayland_common.
This makes it easier to write other video outputs.

The EGL specific parts, as well as opengl context creation, are in gl_common.

This backend works for:
    * opengl-old
    * opengl
    * opengl-hq

To use it just specify the opengl backend
    --vo=opengl:backend=wayland
or disable the x11 build.

Don't forget to set EGL_PLATFORM to wayland.

Co-Author: Scott Moreau
(Sorry I lost the old commit history due to the file structure changes)
  • Loading branch information...
commit bf9b9c3bd0d0fe9d9116cbe287559bad5efe00fc 1 parent f143eec
Alexander Preisinger giselher authored
4 DOCS/man/en/vo.rst
Source Rendered
@@ -386,6 +386,8 @@ opengl
386 386 Win32/WGL
387 387 x11
388 388 X11/GLX
  389 + wayland
  390 + Wayland/EGL
389 391
390 392 indirect
391 393 Do YUV conversion and scaling as separate passes. This will
@@ -643,6 +645,8 @@ opengl-old
643 645 Win32/WGL
644 646 x11
645 647 X11/GLX
  648 + waylnad
  649 + Wayland/EGL
646 650
647 651 sdl
648 652 SDL 2.0+ Render video output driver, depending on system with or without
1  Makefile
@@ -99,6 +99,7 @@ SOURCES-$(ENCODING) += video/out/vo_lavc.c audio/out/ao_lavc.c \
99 99 core/encode_lavc.c
100 100 SOURCES-$(GL_WIN32) += video/out/w32_common.c
101 101 SOURCES-$(GL_X11) += video/out/x11_common.c
  102 +SOURCES-$(GL_WAYLAND) += video/out/wayland_common.c
102 103
103 104 SOURCES-$(JACK) += audio/out/ao_jack.c
104 105 SOURCES-$(JOYSTICK) += core/input/joystick.c
38 configure
@@ -353,6 +353,7 @@ Video output:
353 353 --enable-vm enable XF86VidMode support [autodetect]
354 354 --enable-xinerama enable Xinerama support [autodetect]
355 355 --enable-x11 enable X11 video output [autodetect]
  356 + --enable-wayland enable Wayland video output [autodetect]
356 357 --disable-xss disable screensaver support via xss [autodetect]
357 358 --disable-corevideo disable CoreVideo video output [autodetect]
358 359 --disable-cocoa disable Cocoa OpenGL backend [autodetect]
@@ -416,6 +417,7 @@ _prefix="/usr/local"
416 417 ffmpeg=auto
417 418 _encoding=yes
418 419 _x11=auto
  420 +_wayland=auto
419 421 _xss=auto
420 422 _xv=auto
421 423 _vdpau=auto
@@ -576,6 +578,8 @@ for ac_option do
576 578 --disable-cross-compile) _cross_compile=no ;;
577 579 --enable-encoding) _encoding=yes ;;
578 580 --disable-encoding) _encoding=no ;;
  581 + --enable-wayland) _wayland=yes ;;
  582 + --disable-wayland) _wayland=no ;;
579 583 --enable-x11) _x11=yes ;;
580 584 --disable-x11) _x11=no ;;
581 585 --enable-xss) _xss=yes ;;
@@ -1829,6 +1833,17 @@ depends_on_application_services(){
1829 1833
1830 1834 fi #if darwin
1831 1835
  1836 +echocheck "Wayland"
  1837 +if test "$_wayland" != no; then
  1838 + _wayland="no"
  1839 + pkg_config_add "wayland-client >= 1.0.0 wayland-egl >= 1.0.0 wayland-cursor >= 1.0.0 xkbcommon >= 0.2.0" \
  1840 + && _wayland="yes"
  1841 + res_comment=""
  1842 +else
  1843 + _wayland="no"
  1844 + res_comment=""
  1845 +fi
  1846 +echores "$_wayland"
1832 1847
1833 1848 echocheck "X11 headers presence"
1834 1849 _x11_headers="no"
@@ -2105,20 +2120,23 @@ fi
2105 2120 # conflicts between -lGL and -framework OpenGL
2106 2121 echocheck "OpenGL"
2107 2122 #Note: this test is run even with --enable-gl since we autodetect linker flags
2108   -if (test "$_x11" = yes || test "$_cocoa" = yes || win32) && test "$_gl" != no ; then
  2123 +if (test "$_x11" = yes || test "$_wayland" = yes || test "$_cocoa" = yes || win32) && test "$_gl" != no ; then
2109 2124 cat > $TMPC << EOF
2110 2125 #ifdef GL_WIN32
2111 2126 #include <windows.h>
2112   -#include <GL/gl.h>
  2127 +#elif defined(GL_WAYLAND)
  2128 +#include <EGL/egl.h>
2113 2129 #else
2114   -#include <GL/gl.h>
2115 2130 #include <X11/Xlib.h>
2116 2131 #include <GL/glx.h>
2117 2132 #endif
  2133 +#include <GL/gl.h>
2118 2134 int main(int argc, char *argv[]) {
2119 2135 #ifdef GL_WIN32
2120 2136 HDC dc;
2121 2137 wglCreateContext(dc);
  2138 +#elif defined(GL_WAYLAND)
  2139 + eglCreateContext(NULL, NULL, EGL_NO_CONTEXT, NULL);
2122 2140 #else
2123 2141 glXCreateContext(NULL, NULL, NULL, True);
2124 2142 #endif
@@ -2137,6 +2155,11 @@ EOF
2137 2155 fi
2138 2156 done
2139 2157 fi
  2158 + if test "$_wayland" = yes && cc_check -DGL_WAYLAND -lGL -lEGL ; then
  2159 + _gl=yes
  2160 + _gl_wayland=yes
  2161 + libs_mplayer="$libs_mplayer -lGL -lEGL"
  2162 + fi
2140 2163 if win32 && cc_check -DGL_WIN32 -lopengl32 ; then
2141 2164 _gl=yes
2142 2165 _gl_win32=yes
@@ -2164,12 +2187,17 @@ if test "$_gl" = yes ; then
2164 2187 def_gl_x11='#define CONFIG_GL_X11 1'
2165 2188 res_comment="$res_comment x11"
2166 2189 fi
  2190 + if test "$_gl_wayland" = yes ; then
  2191 + def_gl_wayland='#define CONFIG_GL_WAYLAND'
  2192 + res_comment="$res_comment wayland"
  2193 + fi
2167 2194 vomodules="opengl $vomodules"
2168 2195 else
2169 2196 def_gl='#undef CONFIG_GL'
2170 2197 def_gl_cocoa='#undef CONFIG_GL_COCOA'
2171 2198 def_gl_win32='#undef CONFIG_GL_WIN32'
2172 2199 def_gl_x11='#undef CONFIG_GL_X11'
  2200 + def_gl_wayland='#undef CONFIG_GL_WAYLAND'
2173 2201 novomodules="opengl $novomodules"
2174 2202 fi
2175 2203 echores "$_gl"
@@ -3015,6 +3043,7 @@ GL = $_gl
3015 3043 GL_COCOA = $_gl_cocoa
3016 3044 GL_WIN32 = $_gl_win32
3017 3045 GL_X11 = $_gl_x11
  3046 +GL_WAYLAND = $_gl_wayland
3018 3047 HAVE_POSIX_SELECT = $_posix_select
3019 3048 HAVE_SYS_MMAN_H = $_mman
3020 3049 JACK = $_jack
@@ -3056,6 +3085,7 @@ VCD = $_vcd
3056 3085 VDPAU = $_vdpau
3057 3086 VSTREAM = $_vstream
3058 3087 X11 = $_x11
  3088 +WAYLAND = $_wayland
3059 3089 XV = $_xv
3060 3090
3061 3091 # FFmpeg
@@ -3251,12 +3281,14 @@ $def_gl
3251 3281 $def_gl_cocoa
3252 3282 $def_gl_win32
3253 3283 $def_gl_x11
  3284 +$def_gl_wayland
3254 3285 $def_jpeg
3255 3286 $def_mng
3256 3287 $def_v4l2
3257 3288 $def_vdpau
3258 3289 $def_vm
3259 3290 $def_x11
  3291 +$def_wayland
3260 3292 $def_xdpms
3261 3293 $def_xf86keysym
3262 3294 $def_xinerama
274 video/out/gl_common.c
@@ -1302,6 +1302,259 @@ static void swapGlBuffers_x11(MPGLContext *ctx)
1302 1302 }
1303 1303 #endif
1304 1304
  1305 +#ifdef CONFIG_GL_WAYLAND
  1306 +
  1307 +#include "wayland_common.h"
  1308 +#include <wayland-egl.h>
  1309 +#include <EGL/egl.h>
  1310 +#include <EGL/eglext.h>
  1311 +#include <assert.h>
  1312 +
  1313 +struct egl_context {
  1314 + EGLSurface egl_surface;
  1315 +
  1316 + struct wl_egl_window *egl_window;
  1317 +
  1318 + struct {
  1319 + EGLDisplay dpy;
  1320 + EGLContext ctx;
  1321 + EGLConfig conf;
  1322 + } egl;
  1323 +};
  1324 +
  1325 +static void egl_resize_func(struct vo_wayland_state *wl,
  1326 + struct egl_context *ctx)
  1327 +{
  1328 + int32_t x, y, scaled_height;
  1329 + double ratio;
  1330 + int minimum_size = 50;
  1331 +
  1332 + if (wl->window->pending_width < minimum_size)
  1333 + wl->window->pending_width = minimum_size;
  1334 + if (wl->window->pending_height < minimum_size)
  1335 + wl->window->pending_height = minimum_size;
  1336 +
  1337 + ratio = (double) wl->vo->aspdat.orgw / wl->vo->aspdat.orgh;
  1338 + scaled_height = wl->window->pending_height * ratio;
  1339 + if (wl->window->pending_width > scaled_height) {
  1340 + wl->window->pending_height = wl->window->pending_width / ratio;
  1341 + } else {
  1342 + wl->window->pending_width = scaled_height;
  1343 + }
  1344 +
  1345 + if (wl->window->edges & WL_SHELL_SURFACE_RESIZE_LEFT)
  1346 + x = wl->window->width - wl->window->pending_width;
  1347 + else
  1348 + x = 0;
  1349 +
  1350 + if (wl->window->edges & WL_SHELL_SURFACE_RESIZE_TOP)
  1351 + y = wl->window->height - wl->window->pending_height;
  1352 + else
  1353 + y = 0;
  1354 +
  1355 + wl_egl_window_resize(ctx->egl_window,
  1356 + wl->window->pending_width,
  1357 + wl->window->pending_height,
  1358 + x, y);
  1359 +
  1360 + wl->window->width = wl->window->pending_width;
  1361 + wl->window->height = wl->window->pending_height;
  1362 +
  1363 + /* set size for mplayer */
  1364 + wl->vo->dwidth = wl->window->pending_width;
  1365 + wl->vo->dheight = wl->window->pending_height;
  1366 + wl->window->events |= VO_EVENT_RESIZE;
  1367 + wl->window->edges = 0;
  1368 + wl->window->resize_needed = 0;
  1369 +}
  1370 +
  1371 +static bool egl_create_context(struct vo_wayland_state *wl,
  1372 + struct egl_context *egl_ctx,
  1373 + MPGLContext *ctx)
  1374 +{
  1375 + EGLint major, minor, n;
  1376 + EGLBoolean ret;
  1377 +
  1378 + GL *gl = ctx->gl;
  1379 +
  1380 + void *(*getProcAddress)(const GLubyte *);
  1381 + const char *(*eglExtStr)(EGLDisplay *, int);
  1382 + const char *eglstr = "";
  1383 +
  1384 + egl_ctx->egl.dpy = eglGetDisplay(wl->display->display);
  1385 + if (!egl_ctx->egl.dpy)
  1386 + return false;
  1387 +
  1388 + EGLint config_attribs[] = {
  1389 + EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
  1390 + EGL_RED_SIZE, 1,
  1391 + EGL_GREEN_SIZE, 1,
  1392 + EGL_BLUE_SIZE, 1,
  1393 + EGL_ALPHA_SIZE, 0,
  1394 + EGL_DEPTH_SIZE, 1,
  1395 + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
  1396 + EGL_NONE
  1397 + };
  1398 +
  1399 + /* major and minor here returns the supported EGL version (e.g.: 1.4) */
  1400 + ret = eglInitialize(egl_ctx->egl.dpy, &major, &minor);
  1401 + if (ret != EGL_TRUE)
  1402 + return false;
  1403 +
  1404 + EGLint context_attribs[] = {
  1405 + EGL_CONTEXT_MAJOR_VERSION_KHR,
  1406 + MPGL_VER_GET_MAJOR(ctx->requested_gl_version),
  1407 + /* EGL_CONTEXT_MINOR_VERSION_KHR, */
  1408 + /* MPGL_VER_GET_MINOR(ctx->requested_gl_version), */
  1409 + /* EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR, 0, */
  1410 + /* Segfaults on anything else than the major version */
  1411 + EGL_NONE
  1412 + };
  1413 +
  1414 + ret = eglBindAPI(EGL_OPENGL_API);
  1415 + assert(ret == EGL_TRUE);
  1416 +
  1417 + ret = eglChooseConfig(egl_ctx->egl.dpy, config_attribs,
  1418 + &egl_ctx->egl.conf, 1, &n);
  1419 + assert(ret && n == 1);
  1420 +
  1421 + egl_ctx->egl.ctx = eglCreateContext(egl_ctx->egl.dpy,
  1422 + egl_ctx->egl.conf,
  1423 + EGL_NO_CONTEXT,
  1424 + context_attribs);
  1425 + if (!egl_ctx->egl.ctx)
  1426 + return false;
  1427 +
  1428 + ret = eglMakeCurrent(egl_ctx->egl.dpy,
  1429 + NULL,
  1430 + NULL,
  1431 + egl_ctx->egl.ctx);
  1432 +
  1433 + assert(ret == EGL_TRUE);
  1434 +
  1435 + getProcAddress = getdladdr("eglGetProcAddress");
  1436 + if (!getProcAddress)
  1437 + mp_msg(MSGT_VO, MSGL_WARN, "[egl] No eglGetProcAdress");
  1438 +
  1439 + eglExtStr = getdladdr("eglQueryString");
  1440 + if (eglExtStr)
  1441 + eglstr = eglExtStr(egl_ctx->egl.dpy, EGL_EXTENSIONS);
  1442 +
  1443 + getFunctions(gl, (void*(*)(const GLubyte*))eglGetProcAddress, eglstr);
  1444 + if (!gl->BindProgram)
  1445 + getFunctions(gl, NULL, eglstr);
  1446 +
  1447 + return true;
  1448 +}
  1449 +
  1450 +static void egl_create_window(struct vo_wayland_state *wl,
  1451 + struct egl_context *egl_ctx,
  1452 + uint32_t width,
  1453 + uint32_t height)
  1454 +{
  1455 + EGLBoolean ret;
  1456 +
  1457 + wl->window->pending_width = width;
  1458 + wl->window->pending_height = height;
  1459 + wl->window->width = width;
  1460 + wl->window->height = height;
  1461 +
  1462 + egl_ctx->egl_window = wl_egl_window_create(wl->window->surface,
  1463 + wl->window->width,
  1464 + wl->window->height);
  1465 +
  1466 + egl_ctx->egl_surface = eglCreateWindowSurface(egl_ctx->egl.dpy,
  1467 + egl_ctx->egl.conf,
  1468 + egl_ctx->egl_window,
  1469 + NULL);
  1470 +
  1471 + ret = eglMakeCurrent(egl_ctx->egl.dpy,
  1472 + egl_ctx->egl_surface,
  1473 + egl_ctx->egl_surface,
  1474 + egl_ctx->egl.ctx);
  1475 +
  1476 + assert(ret == EGL_TRUE);
  1477 +
  1478 + wl_display_dispatch_pending(wl->display->display);
  1479 +}
  1480 +
  1481 +static bool config_window_wayland(struct MPGLContext *ctx,
  1482 + uint32_t d_width,
  1483 + uint32_t d_height,
  1484 + uint32_t flags)
  1485 +{
  1486 + struct egl_context * egl_ctx = ctx->priv;
  1487 + struct vo_wayland_state * wl = ctx->vo->wayland;
  1488 + bool ret = false;
  1489 +
  1490 + wl->window->pending_width = d_width;
  1491 + wl->window->pending_height = d_height;
  1492 + wl->window->width = d_width;
  1493 + wl->window->height = d_height;
  1494 +
  1495 + vo_wayland_update_window_title(ctx->vo);
  1496 +
  1497 + if ((VOFLAG_FULLSCREEN & flags) && wl->window->type != TYPE_FULLSCREEN)
  1498 + vo_wayland_fullscreen(ctx->vo);
  1499 +
  1500 + if (!egl_ctx->egl.ctx) {
  1501 + /* Create OpenGL context */
  1502 + ret = egl_create_context(wl, egl_ctx, ctx);
  1503 +
  1504 + /* If successfully created the context and we don't want to hide the
  1505 + * window than also create the window immediately */
  1506 + if (ret && !(VOFLAG_HIDDEN & flags))
  1507 + egl_create_window(wl, egl_ctx, d_width, d_height);
  1508 +
  1509 + return ret;
  1510 + }
  1511 + else {
  1512 + /* If the window exists just resize it */
  1513 + if (egl_ctx->egl_window)
  1514 + egl_resize_func(wl, egl_ctx);
  1515 +
  1516 + else {
  1517 + /* If the context exists and the hidden flag is unset then
  1518 + * create the window */
  1519 + if (!(VOFLAG_HIDDEN & flags))
  1520 + egl_create_window(wl, egl_ctx, d_width, d_height);
  1521 + }
  1522 + return true;
  1523 + }
  1524 +}
  1525 +
  1526 +static void releaseGlContext_wayland(MPGLContext *ctx)
  1527 +{
  1528 + GL *gl = ctx->gl;
  1529 + struct egl_context * egl_ctx = ctx->priv;
  1530 +
  1531 + gl->Finish();
  1532 + eglMakeCurrent(egl_ctx->egl.dpy, NULL, NULL, EGL_NO_CONTEXT);
  1533 + eglDestroyContext(egl_ctx->egl.dpy, egl_ctx->egl.ctx);
  1534 + eglTerminate(egl_ctx->egl.dpy);
  1535 + eglReleaseThread();
  1536 + wl_egl_window_destroy(egl_ctx->egl_window);
  1537 + egl_ctx->egl.ctx = NULL;
  1538 +}
  1539 +
  1540 +static void swapGlBuffers_wayland(MPGLContext *ctx)
  1541 +{
  1542 + struct egl_context * egl_ctx = ctx->priv;
  1543 + struct vo_wayland_state *wl = ctx->vo->wayland;
  1544 +
  1545 + eglSwapBuffers(egl_ctx->egl.dpy, egl_ctx->egl_surface);
  1546 +
  1547 + /* resize window after the buffers have swapped
  1548 + * makes resizing more fluid */
  1549 + if (wl->window->resize_needed) {
  1550 + wl_egl_window_get_attached_size(egl_ctx->egl_window,
  1551 + &wl->window->width,
  1552 + &wl->window->height);
  1553 + egl_resize_func(wl, egl_ctx);
  1554 + }
  1555 +}
  1556 +
  1557 +#endif
1305 1558
1306 1559 struct backend {
1307 1560 const char *name;
@@ -1313,6 +1566,7 @@ static struct backend backends[] = {
1313 1566 {"cocoa", GLTYPE_COCOA},
1314 1567 {"win", GLTYPE_W32},
1315 1568 {"x11", GLTYPE_X11},
  1569 + {"wayland", GLTYPE_WAYLAND},
1316 1570 {0}
1317 1571 };
1318 1572
@@ -1335,7 +1589,10 @@ MPGLContext *mpgl_init(enum MPGLType type, struct vo *vo)
1335 1589 ctx = mpgl_init(GLTYPE_W32, vo);
1336 1590 if (ctx)
1337 1591 return ctx;
1338   - return mpgl_init(GLTYPE_X11, vo);
  1592 + ctx = mpgl_init(GLTYPE_X11, vo);
  1593 + if (ctx)
  1594 + return ctx;
  1595 + return mpgl_init(GLTYPE_WAYLAND, vo);
1339 1596 }
1340 1597 ctx = talloc_zero(NULL, MPGLContext);
1341 1598 *ctx = (MPGLContext) {
@@ -1390,6 +1647,21 @@ MPGLContext *mpgl_init(enum MPGLType type, struct vo *vo)
1390 1647 ctx->vo_uninit = vo_x11_uninit;
1391 1648 break;
1392 1649 #endif
  1650 +#ifdef CONFIG_GL_WAYLAND
  1651 + case GLTYPE_WAYLAND:
  1652 + ctx->priv = talloc_zero(ctx, struct egl_context);
  1653 + ctx->config_window = config_window_wayland;
  1654 + ctx->releaseGlContext = releaseGlContext_wayland;
  1655 + ctx->swapGlBuffers = swapGlBuffers_wayland;
  1656 + ctx->update_xinerama_info = vo_wayland_update_screeninfo;
  1657 + ctx->border = vo_wayland_border;
  1658 + ctx->check_events = vo_wayland_check_events;
  1659 + ctx->fullscreen = vo_wayland_fullscreen;
  1660 + ctx->ontop = vo_wayland_ontop;
  1661 + ctx->vo_init = vo_wayland_init;
  1662 + ctx->vo_uninit = vo_wayland_uninit;
  1663 + break;
  1664 +#endif
1393 1665 }
1394 1666 if (ctx->vo_init && ctx->vo_init(vo))
1395 1667 return ctx;
1  video/out/gl_common.h
@@ -77,6 +77,7 @@ enum MPGLType {
77 77 GLTYPE_COCOA,
78 78 GLTYPE_W32,
79 79 GLTYPE_X11,
  80 + GLTYPE_WAYLAND,
80 81 };
81 82
82 83 enum {
1  video/out/vo.h
@@ -248,6 +248,7 @@ struct vo {
248 248 struct vo_x11_state *x11;
249 249 struct vo_w32_state *w32;
250 250 struct vo_cocoa_state *cocoa;
  251 + struct vo_wayland_state *wayland;
251 252 struct mp_fifo *key_fifo;
252 253 struct encode_lavc_context *encode_lavc_ctx;
253 254 struct input_ctx *input_ctx;
1  video/out/vo_opengl.c
@@ -2313,6 +2313,7 @@ static const char help_text[] =
2313 2313 " cocoa: Cocoa/OSX\n"
2314 2314 " win: Win32/WGL\n"
2315 2315 " x11: X11/GLX\n"
  2316 +" wayland: Wayland/EGL\n"
2316 2317 " indirect\n"
2317 2318 " Do YUV conversion and scaling as separate passes. This will\n"
2318 2319 " first render the video into a video-sized RGB texture, and\n"
1  video/out/vo_opengl_old.c
@@ -2243,6 +2243,7 @@ static int preinit(struct vo *vo, const char *arg)
2243 2243 " cocoa: Cocoa/OSX\n"
2244 2244 " win: Win32/WGL\n"
2245 2245 " x11: X11/GLX\n"
  2246 + " wayland: Wayland/EGL\n"
2246 2247 "\n");
2247 2248 return -1;
2248 2249 }
1,036 video/out/wayland_common.c
... ... @@ -0,0 +1,1036 @@
  1 +/*
  2 + * This file is part of MPlayer.
  3 + * Copyright © 2008 Kristian Høgsberg
  4 + * Copyright © 2012-2013 Collabora, Ltd.
  5 + * Copyright © 2012-2013 Scott Moreau <oreaus@gmail.com>
  6 + * Copyright © 2012-2013 Alexander Preisinger <alexander.preisinger@gmail.com>
  7 + *
  8 + * MPlayer is free software; you can redistribute it and/or modify
  9 + * it under the terms of the GNU General Public License as published by
  10 + * the Free Software Foundation; either version 2 of the License, or
  11 + * (at your option) any later version.
  12 + *
  13 + * MPlayer is distributed in the hope that it will be useful,
  14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16 + * GNU General Public License for more details.
  17 + *
  18 + * You should have received a copy of the GNU General Public License along
  19 + * with MPlayer; if not, write to the Free Software Foundation, Inc.,
  20 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  21 + */
  22 +
  23 +#include <stdio.h>
  24 +#include <stdlib.h>
  25 +#include <math.h>
  26 +#include <inttypes.h>
  27 +#include <limits.h>
  28 +#include <assert.h>
  29 +#include <fcntl.h>
  30 +#include <unistd.h>
  31 +
  32 +#include <sys/mman.h>
  33 +#include <sys/epoll.h>
  34 +#include <sys/timerfd.h>
  35 +#include <linux/input.h>
  36 +
  37 +#include "config.h"
  38 +#include "core/bstr.h"
  39 +#include "core/options.h"
  40 +#include "core/mp_msg.h"
  41 +#include "core/mp_fifo.h"
  42 +#include "libavutil/common.h"
  43 +#include "talloc.h"
  44 +
  45 +#include "wayland_common.h"
  46 +
  47 +#include "vo.h"
  48 +#include "aspect.h"
  49 +#include "osdep/timer.h"
  50 +
  51 +#include "core/subopt-helper.h"
  52 +
  53 +#include "core/input/input.h"
  54 +#include "core/input/keycodes.h"
  55 +
  56 +#define MOD_SHIFT_MASK 0x01
  57 +#define MOD_ALT_MASK 0x02
  58 +#define MOD_CONTROL_MASK 0x04
  59 +
  60 +#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
  61 +
  62 +static int lookupkey(int key);
  63 +
  64 +static void hide_cursor(struct vo_wayland_display * display);
  65 +static void show_cursor(struct vo_wayland_display * display);
  66 +
  67 +
  68 +/*** wayland interface ***/
  69 +
  70 +/* SHELL SURFACE LISTENER */
  71 +static void ssurface_handle_ping(void *data,
  72 + struct wl_shell_surface *shell_surface,
  73 + uint32_t serial)
  74 +{
  75 + wl_shell_surface_pong(shell_surface, serial);
  76 +}
  77 +
  78 +static void ssurface_schedule_resize(struct vo_wayland_window *window,
  79 + int32_t width,
  80 + int32_t height)
  81 +{
  82 + window->pending_width = width;
  83 + window->pending_height = height;
  84 + window->resize_needed = 1;
  85 + window->events |= VO_EVENT_RESIZE;
  86 +}
  87 +
  88 +static void ssurface_handle_configure(void *data,
  89 + struct wl_shell_surface *shell_surface,
  90 + uint32_t edges,
  91 + int32_t width,
  92 + int32_t height)
  93 +{
  94 + struct vo_wayland_state *wl = data;
  95 +
  96 + wl->window->edges = edges;
  97 + ssurface_schedule_resize(wl->window, width, height);
  98 +}
  99 +
  100 +static void ssurface_handle_popup_done(void *data,
  101 + struct wl_shell_surface *shell_surface)
  102 +{
  103 +}
  104 +
  105 +const struct wl_shell_surface_listener shell_surface_listener = {
  106 + ssurface_handle_ping,
  107 + ssurface_handle_configure,
  108 + ssurface_handle_popup_done
  109 +};
  110 +
  111 +/* OUTPUT LISTENER */
  112 +static void output_handle_geometry(void *data,
  113 + struct wl_output *wl_output,
  114 + int32_t x,
  115 + int32_t y,
  116 + int32_t physical_width,
  117 + int32_t physical_height,
  118 + int32_t subpixel,
  119 + const char *make,
  120 + const char *model,
  121 + int32_t transform)
  122 +{
  123 + /* Ignore transforms for now */
  124 + switch (transform) {
  125 + case WL_OUTPUT_TRANSFORM_NORMAL:
  126 + case WL_OUTPUT_TRANSFORM_90:
  127 + case WL_OUTPUT_TRANSFORM_180:
  128 + case WL_OUTPUT_TRANSFORM_270:
  129 + case WL_OUTPUT_TRANSFORM_FLIPPED:
  130 + case WL_OUTPUT_TRANSFORM_FLIPPED_90:
  131 + case WL_OUTPUT_TRANSFORM_FLIPPED_180:
  132 + case WL_OUTPUT_TRANSFORM_FLIPPED_270:
  133 + default:
  134 + break;
  135 + }
  136 +}
  137 +
  138 +static void output_handle_mode(void *data,
  139 + struct wl_output *wl_output,
  140 + uint32_t flags,
  141 + int32_t width,
  142 + int32_t height,
  143 + int32_t refresh)
  144 +{
  145 + struct vo_wayland_display *d = data;
  146 + struct vo_wayland_output *output;
  147 +
  148 + wl_list_for_each(output, &d->output_list, link) {
  149 + if (wl_output == output->output) {
  150 + output->width = width;
  151 + output->height = height;
  152 + if (flags)
  153 + output->flags = flags;
  154 + }
  155 + }
  156 +
  157 + /* one output is enough */
  158 + d->output_mode_received = 1;
  159 +}
  160 +
  161 +const struct wl_output_listener output_listener = {
  162 + output_handle_geometry,
  163 + output_handle_mode
  164 +};
  165 +
  166 +/* KEY LOOKUP */
  167 +static const struct mp_keymap keymap[] = {
  168 + // special keys
  169 + {XKB_KEY_Pause, MP_KEY_PAUSE}, {XKB_KEY_Escape, MP_KEY_ESC},
  170 + {XKB_KEY_BackSpace, MP_KEY_BS}, {XKB_KEY_Tab, MP_KEY_TAB},
  171 + {XKB_KEY_Return, MP_KEY_ENTER}, {XKB_KEY_Menu, MP_KEY_MENU},
  172 + {XKB_KEY_Print, MP_KEY_PRINT},
  173 +
  174 + // cursor keys
  175 + {XKB_KEY_Left, MP_KEY_LEFT}, {XKB_KEY_Right, MP_KEY_RIGHT},
  176 + {XKB_KEY_Up, MP_KEY_UP}, {XKB_KEY_Down, MP_KEY_DOWN},
  177 +
  178 + // navigation block
  179 + {XKB_KEY_Insert, MP_KEY_INSERT}, {XKB_KEY_Delete, MP_KEY_DELETE},
  180 + {XKB_KEY_Home, MP_KEY_HOME}, {XKB_KEY_End, MP_KEY_END},
  181 + {XKB_KEY_Page_Up, MP_KEY_PAGE_UP}, {XKB_KEY_Page_Down, MP_KEY_PAGE_DOWN},
  182 +
  183 + // F-keys
  184 + {XKB_KEY_F1, MP_KEY_F+1}, {XKB_KEY_F2, MP_KEY_F+2},
  185 + {XKB_KEY_F3, MP_KEY_F+3}, {XKB_KEY_F4, MP_KEY_F+4},
  186 + {XKB_KEY_F5, MP_KEY_F+5}, {XKB_KEY_F6, MP_KEY_F+6},
  187 + {XKB_KEY_F7, MP_KEY_F+7}, {XKB_KEY_F8, MP_KEY_F+8},
  188 + {XKB_KEY_F9, MP_KEY_F+9}, {XKB_KEY_F10, MP_KEY_F+10},
  189 + {XKB_KEY_F11, MP_KEY_F+11}, {XKB_KEY_F12, MP_KEY_F+12},
  190 +
  191 + // numpad independent of numlock
  192 + {XKB_KEY_KP_Subtract, '-'}, {XKB_KEY_KP_Add, '+'},
  193 + {XKB_KEY_KP_Multiply, '*'}, {XKB_KEY_KP_Divide, '/'},
  194 + {XKB_KEY_KP_Enter, MP_KEY_KPENTER},
  195 +
  196 + // numpad with numlock
  197 + {XKB_KEY_KP_0, MP_KEY_KP0}, {XKB_KEY_KP_1, MP_KEY_KP1},
  198 + {XKB_KEY_KP_2, MP_KEY_KP2}, {XKB_KEY_KP_3, MP_KEY_KP3},
  199 + {XKB_KEY_KP_4, MP_KEY_KP4}, {XKB_KEY_KP_5, MP_KEY_KP5},
  200 + {XKB_KEY_KP_6, MP_KEY_KP6}, {XKB_KEY_KP_7, MP_KEY_KP7},
  201 + {XKB_KEY_KP_8, MP_KEY_KP8}, {XKB_KEY_KP_9, MP_KEY_KP9},
  202 + {XKB_KEY_KP_Decimal, MP_KEY_KPDEC}, {XKB_KEY_KP_Separator, MP_KEY_KPDEC},
  203 +
  204 + // numpad without numlock
  205 + {XKB_KEY_KP_Insert, MP_KEY_KPINS}, {XKB_KEY_KP_End, MP_KEY_KP1},
  206 + {XKB_KEY_KP_Down, MP_KEY_KP2}, {XKB_KEY_KP_Page_Down, MP_KEY_KP3},
  207 + {XKB_KEY_KP_Left, MP_KEY_KP4}, {XKB_KEY_KP_Begin, MP_KEY_KP5},
  208 + {XKB_KEY_KP_Right, MP_KEY_KP6}, {XKB_KEY_KP_Home, MP_KEY_KP7},
  209 + {XKB_KEY_KP_Up, MP_KEY_KP8}, {XKB_KEY_KP_Page_Up, MP_KEY_KP9},
  210 + {XKB_KEY_KP_Delete, MP_KEY_KPDEL},
  211 +
  212 + {0, 0}
  213 +};
  214 +
  215 +/* KEYBOARD LISTENER */
  216 +static void keyboard_handle_keymap(void *data,
  217 + struct wl_keyboard *wl_keyboard,
  218 + uint32_t format,
  219 + int32_t fd,
  220 + uint32_t size)
  221 +{
  222 + struct vo_wayland_input *input = ((struct vo_wayland_state *) data)->input;
  223 + char *map_str;
  224 +
  225 + if(!data) {
  226 + close(fd);
  227 + return;
  228 + }
  229 +
  230 + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
  231 + close(fd);
  232 + return;
  233 + }
  234 +
  235 + map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
  236 + if (map_str == MAP_FAILED) {
  237 + close(fd);
  238 + return;
  239 + }
  240 +
  241 + input->xkb.keymap = xkb_map_new_from_string(input->xkb.context,
  242 + map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0);
  243 +
  244 + munmap(map_str, size);
  245 + close(fd);
  246 +
  247 + if (!input->xkb.keymap) {
  248 + mp_msg(MSGT_VO, MSGL_ERR, "[wayland] failed to compile keymap.\n");
  249 + return;
  250 + }
  251 +
  252 + input->xkb.state = xkb_state_new(input->xkb.keymap);
  253 + if (!input->xkb.state) {
  254 + mp_msg(MSGT_VO, MSGL_ERR, "[wayland] failed to create XKB state.\n");
  255 + xkb_map_unref(input->xkb.keymap);
  256 + input->xkb.keymap = NULL;
  257 + return;
  258 + }
  259 +
  260 + input->xkb.control_mask =
  261 + 1 << xkb_map_mod_get_index(input->xkb.keymap, "Control");
  262 + input->xkb.alt_mask =
  263 + 1 << xkb_map_mod_get_index(input->xkb.keymap, "Mod1");
  264 + input->xkb.shift_mask =
  265 + 1 << xkb_map_mod_get_index(input->xkb.keymap, "Shift");
  266 +}
  267 +
  268 +static void keyboard_handle_enter(void *data,
  269 + struct wl_keyboard *wl_keyboard,
  270 + uint32_t serial,
  271 + struct wl_surface *surface,
  272 + struct wl_array *keys)
  273 +{
  274 +}
  275 +
  276 +static void keyboard_handle_leave(void *data,
  277 + struct wl_keyboard *wl_keyboard,
  278 + uint32_t serial,
  279 + struct wl_surface *surface)
  280 +{
  281 +}
  282 +
  283 +static void keyboard_handle_key(void *data,
  284 + struct wl_keyboard *wl_keyboard,
  285 + uint32_t serial,
  286 + uint32_t time,
  287 + uint32_t key,
  288 + uint32_t state)
  289 +{
  290 + struct vo_wayland_state *wl = data;
  291 + struct vo_wayland_input *input = wl->input;
  292 + uint32_t code, num_syms;
  293 +
  294 + struct itimerspec its = {{0, 0}, {0, 0}};
  295 +
  296 + const xkb_keysym_t *syms;
  297 + xkb_keysym_t sym;
  298 + xkb_mod_mask_t mask;
  299 +
  300 + code = key + 8;
  301 + num_syms = xkb_key_get_syms(input->xkb.state, code, &syms);
  302 +
  303 + mask = xkb_state_serialize_mods(input->xkb.state,
  304 + XKB_STATE_DEPRESSED | XKB_STATE_LATCHED);
  305 +
  306 + input->modifiers = 0;
  307 + if (mask & input->xkb.control_mask)
  308 + input->modifiers |= MOD_CONTROL_MASK;
  309 + if (mask & input->xkb.alt_mask)
  310 + input->modifiers |= MOD_ALT_MASK;
  311 + if (mask & input->xkb.shift_mask)
  312 + input->modifiers |= MOD_SHIFT_MASK;
  313 +
  314 + sym = XKB_KEY_NoSymbol;
  315 + if (num_syms == 1)
  316 + sym = syms[0];
  317 +
  318 + if (sym != XKB_KEY_NoSymbol && state == WL_KEYBOARD_KEY_STATE_PRESSED) {
  319 + int mpkey = lookupkey(sym);
  320 + if (mpkey)
  321 + mplayer_put_key(wl->vo->key_fifo, mpkey);
  322 + input->events |= VO_EVENT_KEYPRESS;
  323 + }
  324 +
  325 + if (state == WL_KEYBOARD_KEY_STATE_RELEASED && key == input->repeat.key) {
  326 + input->repeat.sym = 0;
  327 + input->repeat.key = 0;
  328 + input->repeat.time = 0;
  329 + }
  330 + else if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
  331 + if (input->repeat.key == key) {
  332 + its.it_interval.tv_sec = 0;
  333 + its.it_interval.tv_nsec = 20 * 1000 * 1000;
  334 + its.it_value.tv_sec = 0;
  335 + its.it_value.tv_nsec = 100 * 1000 * 1000;
  336 + }
  337 + else {
  338 + input->repeat.sym = sym;
  339 + input->repeat.key = key;
  340 + input->repeat.time = time;
  341 + its.it_interval.tv_sec = 0;
  342 + its.it_interval.tv_nsec = 25 * 1000 * 1000;
  343 + its.it_value.tv_sec = 0;
  344 + its.it_value.tv_nsec = 400 * 1000 * 1000;
  345 + }
  346 + }
  347 + timerfd_settime(input->repeat.timer_fd, 0, &its, NULL);
  348 +}
  349 +
  350 +static void keyboard_handle_modifiers(void *data,
  351 + struct wl_keyboard *wl_keyboard,
  352 + uint32_t serial,
  353 + uint32_t mods_depressed,
  354 + uint32_t mods_latched,
  355 + uint32_t mods_locked,
  356 + uint32_t group)
  357 +{
  358 + struct vo_wayland_input *input = ((struct vo_wayland_state *) data)->input;
  359 +
  360 + xkb_state_update_mask(input->xkb.state, mods_depressed, mods_latched,
  361 + mods_locked, 0, 0, group);
  362 +}
  363 +
  364 +const struct wl_keyboard_listener keyboard_listener = {
  365 + keyboard_handle_keymap,
  366 + keyboard_handle_enter,
  367 + keyboard_handle_leave,
  368 + keyboard_handle_key,
  369 + keyboard_handle_modifiers
  370 +};
  371 +
  372 +/* POINTER LISTENER */
  373 +static void pointer_handle_enter(void *data,
  374 + struct wl_pointer *pointer,
  375 + uint32_t serial,
  376 + struct wl_surface *surface,
  377 + wl_fixed_t sx_w,
  378 + wl_fixed_t sy_w)
  379 +{
  380 + struct vo_wayland_state *wl = data;
  381 + struct vo_wayland_display * display = wl->display;
  382 +
  383 + display->cursor.serial = serial;
  384 + display->cursor.pointer = pointer;
  385 +
  386 + if (wl->window->type == TYPE_FULLSCREEN)
  387 + hide_cursor(display);
  388 + else if (display->cursor.default_cursor) {
  389 + show_cursor(display);
  390 + }
  391 +}
  392 +
  393 +static void pointer_handle_leave(void *data,
  394 + struct wl_pointer *pointer,
  395 + uint32_t serial,
  396 + struct wl_surface *surface)
  397 +{
  398 +}
  399 +
  400 +static void pointer_handle_motion(void *data,
  401 + struct wl_pointer *pointer,
  402 + uint32_t time,
  403 + wl_fixed_t sx_w,
  404 + wl_fixed_t sy_w)
  405 +{
  406 + struct vo_wayland_state *wl = data;
  407 + struct vo_wayland_display * display = wl->display;
  408 +
  409 + display->cursor.pointer = pointer;
  410 +
  411 + struct itimerspec its;
  412 +
  413 + if (wl->window->type == TYPE_FULLSCREEN) {
  414 + show_cursor(display);
  415 +
  416 + its.it_interval.tv_sec = 1;
  417 + its.it_interval.tv_nsec = 0;
  418 + its.it_value.tv_sec = 1;
  419 + its.it_value.tv_nsec = 0;
  420 + timerfd_settime(display->cursor.timer_fd, 0, &its, NULL);
  421 + }
  422 +}
  423 +
  424 +static void pointer_handle_button(void *data,
  425 + struct wl_pointer *pointer,
  426 + uint32_t serial,
  427 + uint32_t time,
  428 + uint32_t button,
  429 + uint32_t state)
  430 +{
  431 + struct vo_wayland_state *wl = data;
  432 +
  433 + mplayer_put_key(wl->vo->key_fifo, MP_MOUSE_BTN0 + (button - BTN_LEFT) |
  434 + ((state == WL_POINTER_BUTTON_STATE_PRESSED) ? MP_KEY_STATE_DOWN : 0));
  435 +}
  436 +
  437 +static void pointer_handle_axis(void *data,
  438 + struct wl_pointer *pointer,
  439 + uint32_t time,
  440 + uint32_t axis,
  441 + wl_fixed_t value)
  442 +{
  443 + struct vo_wayland_state *wl = data;
  444 +
  445 + if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
  446 + if (value > 0)
  447 + mplayer_put_key(wl->vo->key_fifo, MP_MOUSE_BTN4);
  448 + if (value < 0)
  449 + mplayer_put_key(wl->vo->key_fifo, MP_MOUSE_BTN3);
  450 + }
  451 +}
  452 +
  453 +static const struct wl_pointer_listener pointer_listener = {
  454 + pointer_handle_enter,
  455 + pointer_handle_leave,
  456 + pointer_handle_motion,
  457 + pointer_handle_button,
  458 + pointer_handle_axis,
  459 +};
  460 +
  461 +static void seat_handle_capabilities(void *data,
  462 + struct wl_seat *seat,
  463 + enum wl_seat_capability caps)
  464 +{
  465 + struct vo_wayland_state *wl = data;
  466 +
  467 + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !wl->input->keyboard) {
  468 + wl->input->keyboard = wl_seat_get_keyboard(seat);
  469 + wl_keyboard_set_user_data(wl->input->keyboard, wl);
  470 + wl_keyboard_add_listener(wl->input->keyboard, &keyboard_listener, wl);
  471 + }
  472 + else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && wl->input->keyboard) {
  473 + wl_keyboard_destroy(wl->input->keyboard);
  474 + wl->input->keyboard = NULL;
  475 + }
  476 + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !wl->input->pointer) {
  477 + wl->input->pointer = wl_seat_get_pointer(seat);
  478 + wl_pointer_set_user_data(wl->input->pointer, wl);
  479 + wl_pointer_add_listener(wl->input->pointer, &pointer_listener, wl);
  480 + }
  481 + else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && wl->input->pointer) {
  482 + wl_pointer_destroy(wl->input->pointer);
  483 + wl->input->pointer = NULL;
  484 + }
  485 +}
  486 +
  487 +static const struct wl_seat_listener seat_listener = {
  488 + seat_handle_capabilities,
  489 +};
  490 +
  491 +/* SHM LISTENER */
  492 +static void shm_handle_format(void *data,
  493 + struct wl_shm *wl_shm,
  494 + uint32_t format)
  495 +{
  496 + struct vo_wayland_display *d = data;
  497 + d->formats |= (1 << format);
  498 +}
  499 +
  500 +const struct wl_shm_listener shm_listener = {
  501 + shm_handle_format
  502 +};
  503 +
  504 +static void registry_handle_global (void *data,
  505 + struct wl_registry *registry,
  506 + uint32_t id,
  507 + const char *interface,
  508 + uint32_t version)
  509 +{
  510 + struct vo_wayland_state *wl = data;
  511 + struct vo_wayland_display *d = wl->display;
  512 +
  513 + if (strcmp(interface, "wl_compositor") == 0) {
  514 +
  515 + d->compositor = wl_registry_bind(d->registry, id,
  516 + &wl_compositor_interface, 1);
  517 + }
  518 +
  519 + else if (strcmp(interface, "wl_shell") == 0) {
  520 +
  521 + d->shell = wl_registry_bind(d->registry, id, &wl_shell_interface, 1);
  522 + }
  523 +
  524 + else if (strcmp(interface, "wl_shm") == 0) {
  525 +
  526 + d->cursor.shm = wl_registry_bind(d->registry, id, &wl_shm_interface, 1);
  527 + d->cursor.theme = wl_cursor_theme_load(NULL, 32, d->cursor.shm);
  528 + d->cursor.default_cursor = wl_cursor_theme_get_cursor(d->cursor.theme,
  529 + "left_ptr");
  530 + wl_shm_add_listener(d->cursor.shm, &shm_listener, d);
  531 + }
  532 +
  533 + else if (strcmp(interface, "wl_output") == 0) {
  534 +
  535 + struct vo_wayland_output *output = talloc_zero(d,
  536 + struct vo_wayland_output);
  537 + output->id = id;
  538 + output->output = wl_registry_bind(d->registry,
  539 + id,
  540 + &wl_output_interface,
  541 + 1);
  542 +
  543 + wl_output_add_listener(output->output, &output_listener, d);
  544 + wl_list_insert(&d->output_list, &output->link);
  545 + }
  546 +
  547 + else if (strcmp(interface, "wl_seat") == 0) {
  548 +
  549 + wl->input->seat = wl_registry_bind(d->registry,
  550 + id,
  551 + &wl_seat_interface,
  552 + 1);
  553 +
  554 + wl_seat_add_listener(wl->input->seat, &seat_listener, wl);
  555 + }
  556 +}
  557 +
  558 +static void registry_handle_global_remove (void *data,
  559 + struct wl_registry *registry,
  560 + uint32_t id)
  561 +{
  562 +}
  563 +
  564 +static const struct wl_registry_listener registry_listener = {
  565 + registry_handle_global,
  566 + registry_handle_global_remove
  567 +};
  568 +
  569 +
  570 +/*** internal functions ***/
  571 +
  572 +static int set_cloexec_or_close(int fd)
  573 +{
  574 + long flags;
  575 +
  576 + if (fd == -1)
  577 + return -1;
  578 +
  579 + if ((flags = fcntl(fd, F_GETFD)) == -1)
  580 + goto err;
  581 +
  582 + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
  583 + goto err;
  584 +
  585 + return fd;
  586 +
  587 +err:
  588 + close(fd);
  589 + return -1;
  590 +}
  591 +
  592 +static int os_epoll_create_cloexec(void)
  593 +{
  594 + int fd;
  595 +
  596 +#ifdef EPOLL_CLOEXEC
  597 + if ((fd = epoll_create1(EPOLL_CLOEXEC)) >= 0)
  598 + return fd;
  599 + if (errno != EINVAL)
  600 + return -1;
  601 +#endif
  602 +
  603 + return set_cloexec_or_close(epoll_create(1));
  604 +}
  605 +
  606 +static int lookupkey(int key)
  607 +{
  608 + static const char *passthrough_keys
  609 + = " -+*/<>`~!@#$%^&()_{}:;\"\',.?\\|=[]";
  610 +
  611 + int mpkey = 0;
  612 + if ((key >= 'a' && key <= 'z') ||
  613 + (key >= 'A' && key <= 'Z') ||
  614 + (key >= '0' && key <= '9') ||
  615 + (key > 0 && key < 256 && strchr(passthrough_keys, key)))
  616 + mpkey = key;
  617 +
  618 + if (!mpkey)
  619 + mpkey = lookup_keymap_table(keymap, key);
  620 +
  621 + return mpkey;
  622 +}
  623 +
  624 +static void hide_cursor (struct vo_wayland_display *display)
  625 +{
  626 + if (!display->cursor.pointer)
  627 + return;
  628 +
  629 + wl_pointer_set_cursor(display->cursor.pointer, display->cursor.serial,
  630 + NULL, 0, 0);
  631 +}
  632 +
  633 +static void show_cursor (struct vo_wayland_display *display)
  634 +{
  635 + if (!display->cursor.pointer)
  636 + return;
  637 +
  638 + struct wl_buffer *buffer;
  639 + struct wl_cursor_image *image;
  640 +
  641 + image = display->cursor.default_cursor->images[0];
  642 + buffer = wl_cursor_image_get_buffer(image);
  643 + wl_pointer_set_cursor(display->cursor.pointer, display->cursor.serial,
  644 + display->cursor.surface, image->hotspot_x, image->hotspot_y);
  645 + wl_surface_attach(display->cursor.surface, buffer, 0, 0);
  646 + wl_surface_damage(display->cursor.surface, 0, 0,
  647 + image->width, image->height);
  648 + wl_surface_commit(display->cursor.surface);
  649 +}
  650 +
  651 +static void
  652 +display_watch_fd(struct vo_wayland_display *display,
  653 + int fd, uint32_t events, struct vo_wayland_task *task)
  654 +{
  655 + struct epoll_event ep;
  656 +
  657 + if (display->epoll_fd < 0) {
  658 + mp_msg(MSGT_VO, MSGL_WARN, "[wayland] Could not watch fd\n");
  659 + return;
  660 + }
  661 +
  662 + ep.events = events;
  663 + ep.data.ptr = task;
  664 + epoll_ctl(display->epoll_fd, EPOLL_CTL_ADD, fd, &ep);
  665 +}
  666 +
  667 +static void display_handle_data(struct vo_wayland_task *task,
  668 + uint32_t events,
  669 + struct vo_wayland_state *wl)
  670 +{
  671 + struct vo_wayland_display *display = wl->display;
  672 + struct epoll_event ep;
  673 + int ret;
  674 +
  675 + if (events & EPOLLERR || events & EPOLLHUP)
  676 + exit(-1);
  677 +
  678 + if (events & EPOLLIN) {
  679 + ret = wl_display_dispatch(display->display);
  680 + if (ret == -1)
  681 + exit(-1);
  682 + }
  683 +
  684 + if (events & EPOLLOUT) {
  685 + ret = wl_display_flush(display->display);
  686 + if (ret == 0) {
  687 + ep.events = EPOLLIN | EPOLLERR | EPOLLHUP;
  688 + ep.data.ptr = &display->display_task;
  689 + epoll_ctl(display->epoll_fd, EPOLL_CTL_MOD,
  690 + display->display_fd, &ep);
  691 + } else if (ret == -1 && errno != EAGAIN)
  692 + exit(-1);
  693 + }
  694 +}
  695 +
  696 +static void cursor_timer_func(struct vo_wayland_task *task,
  697 + uint32_t events,