twitchapon-anim

Basic Twitchapon Receiver/Visuals
git clone git://bsandro.tech/twitchapon-anim
Log | Files | Refs | README | LICENSE

wl_window.c (58560B)


      1 //========================================================================
      2 // GLFW 3.3 Wayland - www.glfw.org
      3 //------------------------------------------------------------------------
      4 // Copyright (c) 2014 Jonas Ådahl <jadahl@gmail.com>
      5 //
      6 // This software is provided 'as-is', without any express or implied
      7 // warranty. In no event will the authors be held liable for any damages
      8 // arising from the use of this software.
      9 //
     10 // Permission is granted to anyone to use this software for any purpose,
     11 // including commercial applications, and to alter it and redistribute it
     12 // freely, subject to the following restrictions:
     13 //
     14 // 1. The origin of this software must not be misrepresented; you must not
     15 //    claim that you wrote the original software. If you use this software
     16 //    in a product, an acknowledgment in the product documentation would
     17 //    be appreciated but is not required.
     18 //
     19 // 2. Altered source versions must be plainly marked as such, and must not
     20 //    be misrepresented as being the original software.
     21 //
     22 // 3. This notice may not be removed or altered from any source
     23 //    distribution.
     24 //
     25 //========================================================================
     26 // It is fine to use C99 in this file because it will not be built with VS
     27 //========================================================================
     28 
     29 #define _GNU_SOURCE
     30 
     31 #include "internal.h"
     32 
     33 #include <stdio.h>
     34 #include <stdlib.h>
     35 #include <errno.h>
     36 #include <unistd.h>
     37 #include <string.h>
     38 #include <fcntl.h>
     39 #include <sys/mman.h>
     40 #include <sys/timerfd.h>
     41 #include <poll.h>
     42 
     43 
     44 static void shellSurfaceHandlePing(void* data,
     45                                    struct wl_shell_surface* shellSurface,
     46                                    uint32_t serial)
     47 {
     48     wl_shell_surface_pong(shellSurface, serial);
     49 }
     50 
     51 static void shellSurfaceHandleConfigure(void* data,
     52                                         struct wl_shell_surface* shellSurface,
     53                                         uint32_t edges,
     54                                         int32_t width,
     55                                         int32_t height)
     56 {
     57     _GLFWwindow* window = data;
     58     float aspectRatio;
     59     float targetRatio;
     60 
     61     if (!window->monitor)
     62     {
     63         if (_glfw.wl.viewporter && window->decorated)
     64         {
     65             width -= _GLFW_DECORATION_HORIZONTAL;
     66             height -= _GLFW_DECORATION_VERTICAL;
     67         }
     68         if (width < 1)
     69             width = 1;
     70         if (height < 1)
     71             height = 1;
     72 
     73         if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)
     74         {
     75             aspectRatio = (float)width / (float)height;
     76             targetRatio = (float)window->numer / (float)window->denom;
     77             if (aspectRatio < targetRatio)
     78                 height = width / targetRatio;
     79             else if (aspectRatio > targetRatio)
     80                 width = height * targetRatio;
     81         }
     82 
     83         if (window->minwidth != GLFW_DONT_CARE && width < window->minwidth)
     84             width = window->minwidth;
     85         else if (window->maxwidth != GLFW_DONT_CARE && width > window->maxwidth)
     86             width = window->maxwidth;
     87 
     88         if (window->minheight != GLFW_DONT_CARE && height < window->minheight)
     89             height = window->minheight;
     90         else if (window->maxheight != GLFW_DONT_CARE && height > window->maxheight)
     91             height = window->maxheight;
     92     }
     93 
     94     _glfwInputWindowSize(window, width, height);
     95     _glfwPlatformSetWindowSize(window, width, height);
     96     _glfwInputWindowDamage(window);
     97 }
     98 
     99 static void shellSurfaceHandlePopupDone(void* data,
    100                                         struct wl_shell_surface* shellSurface)
    101 {
    102 }
    103 
    104 static const struct wl_shell_surface_listener shellSurfaceListener = {
    105     shellSurfaceHandlePing,
    106     shellSurfaceHandleConfigure,
    107     shellSurfaceHandlePopupDone
    108 };
    109 
    110 static int createTmpfileCloexec(char* tmpname)
    111 {
    112     int fd;
    113 
    114     fd = mkostemp(tmpname, O_CLOEXEC);
    115     if (fd >= 0)
    116         unlink(tmpname);
    117 
    118     return fd;
    119 }
    120 
    121 /*
    122  * Create a new, unique, anonymous file of the given size, and
    123  * return the file descriptor for it. The file descriptor is set
    124  * CLOEXEC. The file is immediately suitable for mmap()'ing
    125  * the given size at offset zero.
    126  *
    127  * The file should not have a permanent backing store like a disk,
    128  * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
    129  *
    130  * The file name is deleted from the file system.
    131  *
    132  * The file is suitable for buffer sharing between processes by
    133  * transmitting the file descriptor over Unix sockets using the
    134  * SCM_RIGHTS methods.
    135  *
    136  * posix_fallocate() is used to guarantee that disk space is available
    137  * for the file at the given size. If disk space is insufficient, errno
    138  * is set to ENOSPC. If posix_fallocate() is not supported, program may
    139  * receive SIGBUS on accessing mmap()'ed file contents instead.
    140  */
    141 static int createAnonymousFile(off_t size)
    142 {
    143     static const char template[] = "/glfw-shared-XXXXXX";
    144     const char* path;
    145     char* name;
    146     int fd;
    147     int ret;
    148 
    149 #ifdef HAVE_MEMFD_CREATE
    150     fd = memfd_create("glfw-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING);
    151     if (fd >= 0)
    152     {
    153         // We can add this seal before calling posix_fallocate(), as the file
    154         // is currently zero-sized anyway.
    155         //
    156         // There is also no need to check for the return value, we couldn’t do
    157         // anything with it anyway.
    158         fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
    159     }
    160     else
    161 #elif defined(SHM_ANON)
    162     fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600);
    163     if (fd < 0)
    164 #endif
    165     {
    166         path = getenv("XDG_RUNTIME_DIR");
    167         if (!path)
    168         {
    169             errno = ENOENT;
    170             return -1;
    171         }
    172 
    173         name = calloc(strlen(path) + sizeof(template), 1);
    174         strcpy(name, path);
    175         strcat(name, template);
    176 
    177         fd = createTmpfileCloexec(name);
    178         free(name);
    179         if (fd < 0)
    180             return -1;
    181     }
    182 
    183 #if defined(SHM_ANON)
    184     // posix_fallocate does not work on SHM descriptors
    185     ret = ftruncate(fd, size);
    186 #else
    187     ret = posix_fallocate(fd, 0, size);
    188 #endif
    189     if (ret != 0)
    190     {
    191         close(fd);
    192         errno = ret;
    193         return -1;
    194     }
    195     return fd;
    196 }
    197 
    198 static struct wl_buffer* createShmBuffer(const GLFWimage* image)
    199 {
    200     struct wl_shm_pool* pool;
    201     struct wl_buffer* buffer;
    202     int stride = image->width * 4;
    203     int length = image->width * image->height * 4;
    204     void* data;
    205     int fd, i;
    206 
    207     fd = createAnonymousFile(length);
    208     if (fd < 0)
    209     {
    210         _glfwInputError(GLFW_PLATFORM_ERROR,
    211                         "Wayland: Creating a buffer file for %d B failed: %m",
    212                         length);
    213         return NULL;
    214     }
    215 
    216     data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    217     if (data == MAP_FAILED)
    218     {
    219         _glfwInputError(GLFW_PLATFORM_ERROR,
    220                         "Wayland: mmap failed: %m");
    221         close(fd);
    222         return NULL;
    223     }
    224 
    225     pool = wl_shm_create_pool(_glfw.wl.shm, fd, length);
    226 
    227     close(fd);
    228     unsigned char* source = (unsigned char*) image->pixels;
    229     unsigned char* target = data;
    230     for (i = 0;  i < image->width * image->height;  i++, source += 4)
    231     {
    232         unsigned int alpha = source[3];
    233 
    234         *target++ = (unsigned char) ((source[2] * alpha) / 255);
    235         *target++ = (unsigned char) ((source[1] * alpha) / 255);
    236         *target++ = (unsigned char) ((source[0] * alpha) / 255);
    237         *target++ = (unsigned char) alpha;
    238     }
    239 
    240     buffer =
    241         wl_shm_pool_create_buffer(pool, 0,
    242                                   image->width,
    243                                   image->height,
    244                                   stride, WL_SHM_FORMAT_ARGB8888);
    245     munmap(data, length);
    246     wl_shm_pool_destroy(pool);
    247 
    248     return buffer;
    249 }
    250 
    251 static void createDecoration(_GLFWdecorationWayland* decoration,
    252                              struct wl_surface* parent,
    253                              struct wl_buffer* buffer, GLFWbool opaque,
    254                              int x, int y,
    255                              int width, int height)
    256 {
    257     struct wl_region* region;
    258 
    259     decoration->surface = wl_compositor_create_surface(_glfw.wl.compositor);
    260     decoration->subsurface =
    261         wl_subcompositor_get_subsurface(_glfw.wl.subcompositor,
    262                                         decoration->surface, parent);
    263     wl_subsurface_set_position(decoration->subsurface, x, y);
    264     decoration->viewport = wp_viewporter_get_viewport(_glfw.wl.viewporter,
    265                                                       decoration->surface);
    266     wp_viewport_set_destination(decoration->viewport, width, height);
    267     wl_surface_attach(decoration->surface, buffer, 0, 0);
    268 
    269     if (opaque)
    270     {
    271         region = wl_compositor_create_region(_glfw.wl.compositor);
    272         wl_region_add(region, 0, 0, width, height);
    273         wl_surface_set_opaque_region(decoration->surface, region);
    274         wl_surface_commit(decoration->surface);
    275         wl_region_destroy(region);
    276     }
    277     else
    278         wl_surface_commit(decoration->surface);
    279 }
    280 
    281 static void createDecorations(_GLFWwindow* window)
    282 {
    283     unsigned char data[] = { 224, 224, 224, 255 };
    284     const GLFWimage image = { 1, 1, data };
    285     GLFWbool opaque = (data[3] == 255);
    286 
    287     if (!_glfw.wl.viewporter || !window->decorated || window->wl.decorations.serverSide)
    288         return;
    289 
    290     if (!window->wl.decorations.buffer)
    291         window->wl.decorations.buffer = createShmBuffer(&image);
    292     if (!window->wl.decorations.buffer)
    293         return;
    294 
    295     createDecoration(&window->wl.decorations.top, window->wl.surface,
    296                      window->wl.decorations.buffer, opaque,
    297                      0, -_GLFW_DECORATION_TOP,
    298                      window->wl.width, _GLFW_DECORATION_TOP);
    299     createDecoration(&window->wl.decorations.left, window->wl.surface,
    300                      window->wl.decorations.buffer, opaque,
    301                      -_GLFW_DECORATION_WIDTH, -_GLFW_DECORATION_TOP,
    302                      _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
    303     createDecoration(&window->wl.decorations.right, window->wl.surface,
    304                      window->wl.decorations.buffer, opaque,
    305                      window->wl.width, -_GLFW_DECORATION_TOP,
    306                      _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
    307     createDecoration(&window->wl.decorations.bottom, window->wl.surface,
    308                      window->wl.decorations.buffer, opaque,
    309                      -_GLFW_DECORATION_WIDTH, window->wl.height,
    310                      window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH);
    311 }
    312 
    313 static void destroyDecoration(_GLFWdecorationWayland* decoration)
    314 {
    315     if (decoration->surface)
    316         wl_surface_destroy(decoration->surface);
    317     if (decoration->subsurface)
    318         wl_subsurface_destroy(decoration->subsurface);
    319     if (decoration->viewport)
    320         wp_viewport_destroy(decoration->viewport);
    321     decoration->surface = NULL;
    322     decoration->subsurface = NULL;
    323     decoration->viewport = NULL;
    324 }
    325 
    326 static void destroyDecorations(_GLFWwindow* window)
    327 {
    328     destroyDecoration(&window->wl.decorations.top);
    329     destroyDecoration(&window->wl.decorations.left);
    330     destroyDecoration(&window->wl.decorations.right);
    331     destroyDecoration(&window->wl.decorations.bottom);
    332 }
    333 
    334 static void xdgDecorationHandleConfigure(void* data,
    335                                          struct zxdg_toplevel_decoration_v1* decoration,
    336                                          uint32_t mode)
    337 {
    338     _GLFWwindow* window = data;
    339 
    340     window->wl.decorations.serverSide = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
    341 
    342     if (!window->wl.decorations.serverSide)
    343         createDecorations(window);
    344 }
    345 
    346 static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener = {
    347     xdgDecorationHandleConfigure,
    348 };
    349 
    350 // Makes the surface considered as XRGB instead of ARGB.
    351 static void setOpaqueRegion(_GLFWwindow* window)
    352 {
    353     struct wl_region* region;
    354 
    355     region = wl_compositor_create_region(_glfw.wl.compositor);
    356     if (!region)
    357         return;
    358 
    359     wl_region_add(region, 0, 0, window->wl.width, window->wl.height);
    360     wl_surface_set_opaque_region(window->wl.surface, region);
    361     wl_surface_commit(window->wl.surface);
    362     wl_region_destroy(region);
    363 }
    364 
    365 
    366 static void resizeWindow(_GLFWwindow* window)
    367 {
    368     int scale = window->wl.scale;
    369     int scaledWidth = window->wl.width * scale;
    370     int scaledHeight = window->wl.height * scale;
    371     wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
    372     if (!window->wl.transparent)
    373         setOpaqueRegion(window);
    374     _glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
    375     _glfwInputWindowContentScale(window, scale, scale);
    376 
    377     if (!window->wl.decorations.top.surface)
    378         return;
    379 
    380     // Top decoration.
    381     wp_viewport_set_destination(window->wl.decorations.top.viewport,
    382                                 window->wl.width, _GLFW_DECORATION_TOP);
    383     wl_surface_commit(window->wl.decorations.top.surface);
    384 
    385     // Left decoration.
    386     wp_viewport_set_destination(window->wl.decorations.left.viewport,
    387                                 _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
    388     wl_surface_commit(window->wl.decorations.left.surface);
    389 
    390     // Right decoration.
    391     wl_subsurface_set_position(window->wl.decorations.right.subsurface,
    392                                window->wl.width, -_GLFW_DECORATION_TOP);
    393     wp_viewport_set_destination(window->wl.decorations.right.viewport,
    394                                 _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
    395     wl_surface_commit(window->wl.decorations.right.surface);
    396 
    397     // Bottom decoration.
    398     wl_subsurface_set_position(window->wl.decorations.bottom.subsurface,
    399                                -_GLFW_DECORATION_WIDTH, window->wl.height);
    400     wp_viewport_set_destination(window->wl.decorations.bottom.viewport,
    401                                 window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH);
    402     wl_surface_commit(window->wl.decorations.bottom.surface);
    403 }
    404 
    405 static void checkScaleChange(_GLFWwindow* window)
    406 {
    407     int scale = 1;
    408     int i;
    409     int monitorScale;
    410 
    411     // Check if we will be able to set the buffer scale or not.
    412     if (_glfw.wl.compositorVersion < 3)
    413         return;
    414 
    415     // Get the scale factor from the highest scale monitor.
    416     for (i = 0; i < window->wl.monitorsCount; ++i)
    417     {
    418         monitorScale = window->wl.monitors[i]->wl.scale;
    419         if (scale < monitorScale)
    420             scale = monitorScale;
    421     }
    422 
    423     // Only change the framebuffer size if the scale changed.
    424     if (scale != window->wl.scale)
    425     {
    426         window->wl.scale = scale;
    427         wl_surface_set_buffer_scale(window->wl.surface, scale);
    428         resizeWindow(window);
    429     }
    430 }
    431 
    432 static void surfaceHandleEnter(void *data,
    433                                struct wl_surface *surface,
    434                                struct wl_output *output)
    435 {
    436     _GLFWwindow* window = data;
    437     _GLFWmonitor* monitor = wl_output_get_user_data(output);
    438 
    439     if (window->wl.monitorsCount + 1 > window->wl.monitorsSize)
    440     {
    441         ++window->wl.monitorsSize;
    442         window->wl.monitors =
    443             realloc(window->wl.monitors,
    444                     window->wl.monitorsSize * sizeof(_GLFWmonitor*));
    445     }
    446 
    447     window->wl.monitors[window->wl.monitorsCount++] = monitor;
    448 
    449     checkScaleChange(window);
    450 }
    451 
    452 static void surfaceHandleLeave(void *data,
    453                                struct wl_surface *surface,
    454                                struct wl_output *output)
    455 {
    456     _GLFWwindow* window = data;
    457     _GLFWmonitor* monitor = wl_output_get_user_data(output);
    458     GLFWbool found;
    459     int i;
    460 
    461     for (i = 0, found = GLFW_FALSE; i < window->wl.monitorsCount - 1; ++i)
    462     {
    463         if (monitor == window->wl.monitors[i])
    464             found = GLFW_TRUE;
    465         if (found)
    466             window->wl.monitors[i] = window->wl.monitors[i + 1];
    467     }
    468     window->wl.monitors[--window->wl.monitorsCount] = NULL;
    469 
    470     checkScaleChange(window);
    471 }
    472 
    473 static const struct wl_surface_listener surfaceListener = {
    474     surfaceHandleEnter,
    475     surfaceHandleLeave
    476 };
    477 
    478 static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable)
    479 {
    480     if (enable && !window->wl.idleInhibitor && _glfw.wl.idleInhibitManager)
    481     {
    482         window->wl.idleInhibitor =
    483             zwp_idle_inhibit_manager_v1_create_inhibitor(
    484                 _glfw.wl.idleInhibitManager, window->wl.surface);
    485         if (!window->wl.idleInhibitor)
    486             _glfwInputError(GLFW_PLATFORM_ERROR,
    487                             "Wayland: Idle inhibitor creation failed");
    488     }
    489     else if (!enable && window->wl.idleInhibitor)
    490     {
    491         zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor);
    492         window->wl.idleInhibitor = NULL;
    493     }
    494 }
    495 
    496 static GLFWbool createSurface(_GLFWwindow* window,
    497                               const _GLFWwndconfig* wndconfig)
    498 {
    499     window->wl.surface = wl_compositor_create_surface(_glfw.wl.compositor);
    500     if (!window->wl.surface)
    501         return GLFW_FALSE;
    502 
    503     wl_surface_add_listener(window->wl.surface,
    504                             &surfaceListener,
    505                             window);
    506 
    507     wl_surface_set_user_data(window->wl.surface, window);
    508 
    509     window->wl.native = wl_egl_window_create(window->wl.surface,
    510                                              wndconfig->width,
    511                                              wndconfig->height);
    512     if (!window->wl.native)
    513         return GLFW_FALSE;
    514 
    515     window->wl.width = wndconfig->width;
    516     window->wl.height = wndconfig->height;
    517     window->wl.scale = 1;
    518 
    519     if (!window->wl.transparent)
    520         setOpaqueRegion(window);
    521 
    522     return GLFW_TRUE;
    523 }
    524 
    525 static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor,
    526                           int refreshRate)
    527 {
    528     if (window->wl.xdg.toplevel)
    529     {
    530         xdg_toplevel_set_fullscreen(
    531             window->wl.xdg.toplevel,
    532             monitor->wl.output);
    533     }
    534     else if (window->wl.shellSurface)
    535     {
    536         wl_shell_surface_set_fullscreen(
    537             window->wl.shellSurface,
    538             WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
    539             refreshRate * 1000, // Convert Hz to mHz.
    540             monitor->wl.output);
    541     }
    542     setIdleInhibitor(window, GLFW_TRUE);
    543     if (!window->wl.decorations.serverSide)
    544         destroyDecorations(window);
    545 }
    546 
    547 static GLFWbool createShellSurface(_GLFWwindow* window)
    548 {
    549     if (!_glfw.wl.shell)
    550     {
    551         _glfwInputError(GLFW_PLATFORM_ERROR,
    552                         "Wayland: wl_shell protocol not available");
    553         return GLFW_FALSE;
    554     }
    555 
    556     window->wl.shellSurface = wl_shell_get_shell_surface(_glfw.wl.shell,
    557                                                          window->wl.surface);
    558     if (!window->wl.shellSurface)
    559     {
    560         _glfwInputError(GLFW_PLATFORM_ERROR,
    561                         "Wayland: Shell surface creation failed");
    562         return GLFW_FALSE;
    563     }
    564 
    565     wl_shell_surface_add_listener(window->wl.shellSurface,
    566                                   &shellSurfaceListener,
    567                                   window);
    568 
    569     if (window->wl.title)
    570         wl_shell_surface_set_title(window->wl.shellSurface, window->wl.title);
    571 
    572     if (window->monitor)
    573     {
    574         setFullscreen(window, window->monitor, 0);
    575     }
    576     else if (window->wl.maximized)
    577     {
    578         wl_shell_surface_set_maximized(window->wl.shellSurface, NULL);
    579         setIdleInhibitor(window, GLFW_FALSE);
    580         createDecorations(window);
    581     }
    582     else
    583     {
    584         wl_shell_surface_set_toplevel(window->wl.shellSurface);
    585         setIdleInhibitor(window, GLFW_FALSE);
    586         createDecorations(window);
    587     }
    588 
    589     wl_surface_commit(window->wl.surface);
    590 
    591     return GLFW_TRUE;
    592 }
    593 
    594 static void xdgToplevelHandleConfigure(void* data,
    595                                        struct xdg_toplevel* toplevel,
    596                                        int32_t width,
    597                                        int32_t height,
    598                                        struct wl_array* states)
    599 {
    600     _GLFWwindow* window = data;
    601     float aspectRatio;
    602     float targetRatio;
    603     uint32_t* state;
    604     GLFWbool maximized = GLFW_FALSE;
    605     GLFWbool fullscreen = GLFW_FALSE;
    606     GLFWbool activated = GLFW_FALSE;
    607 
    608     wl_array_for_each(state, states)
    609     {
    610         switch (*state)
    611         {
    612             case XDG_TOPLEVEL_STATE_MAXIMIZED:
    613                 maximized = GLFW_TRUE;
    614                 break;
    615             case XDG_TOPLEVEL_STATE_FULLSCREEN:
    616                 fullscreen = GLFW_TRUE;
    617                 break;
    618             case XDG_TOPLEVEL_STATE_RESIZING:
    619                 break;
    620             case XDG_TOPLEVEL_STATE_ACTIVATED:
    621                 activated = GLFW_TRUE;
    622                 break;
    623         }
    624     }
    625 
    626     if (width != 0 && height != 0)
    627     {
    628         if (!maximized && !fullscreen)
    629         {
    630             if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)
    631             {
    632                 aspectRatio = (float)width / (float)height;
    633                 targetRatio = (float)window->numer / (float)window->denom;
    634                 if (aspectRatio < targetRatio)
    635                     height = width / targetRatio;
    636                 else if (aspectRatio > targetRatio)
    637                     width = height * targetRatio;
    638             }
    639         }
    640 
    641         _glfwInputWindowSize(window, width, height);
    642         _glfwPlatformSetWindowSize(window, width, height);
    643         _glfwInputWindowDamage(window);
    644     }
    645 
    646     if (window->wl.wasFullscreen && window->autoIconify)
    647     {
    648         if (!activated || !fullscreen)
    649         {
    650             _glfwPlatformIconifyWindow(window);
    651             window->wl.wasFullscreen = GLFW_FALSE;
    652         }
    653     }
    654     if (fullscreen && activated)
    655         window->wl.wasFullscreen = GLFW_TRUE;
    656     _glfwInputWindowFocus(window, activated);
    657 }
    658 
    659 static void xdgToplevelHandleClose(void* data,
    660                                    struct xdg_toplevel* toplevel)
    661 {
    662     _GLFWwindow* window = data;
    663     _glfwInputWindowCloseRequest(window);
    664 }
    665 
    666 static const struct xdg_toplevel_listener xdgToplevelListener = {
    667     xdgToplevelHandleConfigure,
    668     xdgToplevelHandleClose
    669 };
    670 
    671 static void xdgSurfaceHandleConfigure(void* data,
    672                                       struct xdg_surface* surface,
    673                                       uint32_t serial)
    674 {
    675     xdg_surface_ack_configure(surface, serial);
    676 }
    677 
    678 static const struct xdg_surface_listener xdgSurfaceListener = {
    679     xdgSurfaceHandleConfigure
    680 };
    681 
    682 static void setXdgDecorations(_GLFWwindow* window)
    683 {
    684     if (_glfw.wl.decorationManager)
    685     {
    686         window->wl.xdg.decoration =
    687             zxdg_decoration_manager_v1_get_toplevel_decoration(
    688                 _glfw.wl.decorationManager, window->wl.xdg.toplevel);
    689         zxdg_toplevel_decoration_v1_add_listener(window->wl.xdg.decoration,
    690                                                  &xdgDecorationListener,
    691                                                  window);
    692         zxdg_toplevel_decoration_v1_set_mode(
    693             window->wl.xdg.decoration,
    694             ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
    695     }
    696     else
    697     {
    698         window->wl.decorations.serverSide = GLFW_FALSE;
    699         createDecorations(window);
    700     }
    701 }
    702 
    703 static GLFWbool createXdgSurface(_GLFWwindow* window)
    704 {
    705     window->wl.xdg.surface = xdg_wm_base_get_xdg_surface(_glfw.wl.wmBase,
    706                                                          window->wl.surface);
    707     if (!window->wl.xdg.surface)
    708     {
    709         _glfwInputError(GLFW_PLATFORM_ERROR,
    710                         "Wayland: xdg-surface creation failed");
    711         return GLFW_FALSE;
    712     }
    713 
    714     xdg_surface_add_listener(window->wl.xdg.surface,
    715                              &xdgSurfaceListener,
    716                              window);
    717 
    718     window->wl.xdg.toplevel = xdg_surface_get_toplevel(window->wl.xdg.surface);
    719     if (!window->wl.xdg.toplevel)
    720     {
    721         _glfwInputError(GLFW_PLATFORM_ERROR,
    722                         "Wayland: xdg-toplevel creation failed");
    723         return GLFW_FALSE;
    724     }
    725 
    726     xdg_toplevel_add_listener(window->wl.xdg.toplevel,
    727                               &xdgToplevelListener,
    728                               window);
    729 
    730     if (window->wl.title)
    731         xdg_toplevel_set_title(window->wl.xdg.toplevel, window->wl.title);
    732 
    733     if (window->minwidth != GLFW_DONT_CARE && window->minheight != GLFW_DONT_CARE)
    734         xdg_toplevel_set_min_size(window->wl.xdg.toplevel,
    735                                   window->minwidth, window->minheight);
    736     if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE)
    737         xdg_toplevel_set_max_size(window->wl.xdg.toplevel,
    738                                   window->maxwidth, window->maxheight);
    739 
    740     if (window->monitor)
    741     {
    742         xdg_toplevel_set_fullscreen(window->wl.xdg.toplevel,
    743                                     window->monitor->wl.output);
    744         setIdleInhibitor(window, GLFW_TRUE);
    745     }
    746     else if (window->wl.maximized)
    747     {
    748         xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
    749         setIdleInhibitor(window, GLFW_FALSE);
    750         setXdgDecorations(window);
    751     }
    752     else
    753     {
    754         setIdleInhibitor(window, GLFW_FALSE);
    755         setXdgDecorations(window);
    756     }
    757 
    758     wl_surface_commit(window->wl.surface);
    759     wl_display_roundtrip(_glfw.wl.display);
    760 
    761     return GLFW_TRUE;
    762 }
    763 
    764 static void setCursorImage(_GLFWwindow* window,
    765                            _GLFWcursorWayland* cursorWayland)
    766 {
    767     struct itimerspec timer = {};
    768     struct wl_cursor* wlCursor = cursorWayland->cursor;
    769     struct wl_cursor_image* image;
    770     struct wl_buffer* buffer;
    771     struct wl_surface* surface = _glfw.wl.cursorSurface;
    772     int scale = 1;
    773 
    774     if (!wlCursor)
    775         buffer = cursorWayland->buffer;
    776     else
    777     {
    778         if (window->wl.scale > 1 && cursorWayland->cursorHiDPI)
    779         {
    780             wlCursor = cursorWayland->cursorHiDPI;
    781             scale = 2;
    782         }
    783 
    784         image = wlCursor->images[cursorWayland->currentImage];
    785         buffer = wl_cursor_image_get_buffer(image);
    786         if (!buffer)
    787             return;
    788 
    789         timer.it_value.tv_sec = image->delay / 1000;
    790         timer.it_value.tv_nsec = (image->delay % 1000) * 1000000;
    791         timerfd_settime(_glfw.wl.cursorTimerfd, 0, &timer, NULL);
    792 
    793         cursorWayland->width = image->width;
    794         cursorWayland->height = image->height;
    795         cursorWayland->xhot = image->hotspot_x;
    796         cursorWayland->yhot = image->hotspot_y;
    797     }
    798 
    799     wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
    800                           surface,
    801                           cursorWayland->xhot / scale,
    802                           cursorWayland->yhot / scale);
    803     wl_surface_set_buffer_scale(surface, scale);
    804     wl_surface_attach(surface, buffer, 0, 0);
    805     wl_surface_damage(surface, 0, 0,
    806                       cursorWayland->width, cursorWayland->height);
    807     wl_surface_commit(surface);
    808 }
    809 
    810 static void incrementCursorImage(_GLFWwindow* window)
    811 {
    812     _GLFWcursor* cursor;
    813 
    814     if (!window || window->wl.decorations.focus != mainWindow)
    815         return;
    816 
    817     cursor = window->wl.currentCursor;
    818     if (cursor && cursor->wl.cursor)
    819     {
    820         cursor->wl.currentImage += 1;
    821         cursor->wl.currentImage %= cursor->wl.cursor->image_count;
    822         setCursorImage(window, &cursor->wl);
    823     }
    824 }
    825 
    826 static void handleEvents(int timeout)
    827 {
    828     struct wl_display* display = _glfw.wl.display;
    829     struct pollfd fds[] = {
    830         { wl_display_get_fd(display), POLLIN },
    831         { _glfw.wl.timerfd, POLLIN },
    832         { _glfw.wl.cursorTimerfd, POLLIN },
    833     };
    834     ssize_t read_ret;
    835     uint64_t repeats, i;
    836 
    837     while (wl_display_prepare_read(display) != 0)
    838         wl_display_dispatch_pending(display);
    839 
    840     // If an error different from EAGAIN happens, we have likely been
    841     // disconnected from the Wayland session, try to handle that the best we
    842     // can.
    843     if (wl_display_flush(display) < 0 && errno != EAGAIN)
    844     {
    845         _GLFWwindow* window = _glfw.windowListHead;
    846         while (window)
    847         {
    848             _glfwInputWindowCloseRequest(window);
    849             window = window->next;
    850         }
    851         wl_display_cancel_read(display);
    852         return;
    853     }
    854 
    855     if (poll(fds, 3, timeout) > 0)
    856     {
    857         if (fds[0].revents & POLLIN)
    858         {
    859             wl_display_read_events(display);
    860             wl_display_dispatch_pending(display);
    861         }
    862         else
    863         {
    864             wl_display_cancel_read(display);
    865         }
    866 
    867         if (fds[1].revents & POLLIN)
    868         {
    869             read_ret = read(_glfw.wl.timerfd, &repeats, sizeof(repeats));
    870             if (read_ret != 8)
    871                 return;
    872 
    873             for (i = 0; i < repeats; ++i)
    874                 _glfwInputKey(_glfw.wl.keyboardFocus, _glfw.wl.keyboardLastKey,
    875                               _glfw.wl.keyboardLastScancode, GLFW_REPEAT,
    876                               _glfw.wl.xkb.modifiers);
    877         }
    878 
    879         if (fds[2].revents & POLLIN)
    880         {
    881             read_ret = read(_glfw.wl.cursorTimerfd, &repeats, sizeof(repeats));
    882             if (read_ret != 8)
    883                 return;
    884 
    885             incrementCursorImage(_glfw.wl.pointerFocus);
    886         }
    887     }
    888     else
    889     {
    890         wl_display_cancel_read(display);
    891     }
    892 }
    893 
    894 // Translates a GLFW standard cursor to a theme cursor name
    895 //
    896 static char *translateCursorShape(int shape)
    897 {
    898     switch (shape)
    899     {
    900         case GLFW_ARROW_CURSOR:
    901             return "left_ptr";
    902         case GLFW_IBEAM_CURSOR:
    903             return "xterm";
    904         case GLFW_CROSSHAIR_CURSOR:
    905             return "crosshair";
    906         case GLFW_HAND_CURSOR:
    907             return "hand2";
    908         case GLFW_HRESIZE_CURSOR:
    909             return "sb_h_double_arrow";
    910         case GLFW_VRESIZE_CURSOR:
    911             return "sb_v_double_arrow";
    912     }
    913     return NULL;
    914 }
    915 
    916 //////////////////////////////////////////////////////////////////////////
    917 //////                       GLFW platform API                      //////
    918 //////////////////////////////////////////////////////////////////////////
    919 
    920 int _glfwPlatformCreateWindow(_GLFWwindow* window,
    921                               const _GLFWwndconfig* wndconfig,
    922                               const _GLFWctxconfig* ctxconfig,
    923                               const _GLFWfbconfig* fbconfig)
    924 {
    925     window->wl.transparent = fbconfig->transparent;
    926 
    927     if (!createSurface(window, wndconfig))
    928         return GLFW_FALSE;
    929 
    930     if (ctxconfig->client != GLFW_NO_API)
    931     {
    932         if (ctxconfig->source == GLFW_EGL_CONTEXT_API ||
    933             ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
    934         {
    935             if (!_glfwInitEGL())
    936                 return GLFW_FALSE;
    937             if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
    938                 return GLFW_FALSE;
    939         }
    940         else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
    941         {
    942             if (!_glfwInitOSMesa())
    943                 return GLFW_FALSE;
    944             if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
    945                 return GLFW_FALSE;
    946         }
    947     }
    948 
    949     if (wndconfig->title)
    950         window->wl.title = _glfw_strdup(wndconfig->title);
    951 
    952     if (wndconfig->visible)
    953     {
    954         if (_glfw.wl.wmBase)
    955         {
    956             if (!createXdgSurface(window))
    957                 return GLFW_FALSE;
    958         }
    959         else
    960         {
    961             if (!createShellSurface(window))
    962                 return GLFW_FALSE;
    963         }
    964 
    965         window->wl.visible = GLFW_TRUE;
    966     }
    967     else
    968     {
    969         window->wl.xdg.surface = NULL;
    970         window->wl.xdg.toplevel = NULL;
    971         window->wl.shellSurface = NULL;
    972         window->wl.visible = GLFW_FALSE;
    973     }
    974 
    975     window->wl.currentCursor = NULL;
    976 
    977     window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*));
    978     window->wl.monitorsCount = 0;
    979     window->wl.monitorsSize = 1;
    980 
    981     return GLFW_TRUE;
    982 }
    983 
    984 void _glfwPlatformDestroyWindow(_GLFWwindow* window)
    985 {
    986     if (window == _glfw.wl.pointerFocus)
    987     {
    988         _glfw.wl.pointerFocus = NULL;
    989         _glfwInputCursorEnter(window, GLFW_FALSE);
    990     }
    991     if (window == _glfw.wl.keyboardFocus)
    992     {
    993         _glfw.wl.keyboardFocus = NULL;
    994         _glfwInputWindowFocus(window, GLFW_FALSE);
    995     }
    996 
    997     if (window->wl.idleInhibitor)
    998         zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor);
    999 
   1000     if (window->context.destroy)
   1001         window->context.destroy(window);
   1002 
   1003     destroyDecorations(window);
   1004     if (window->wl.xdg.decoration)
   1005         zxdg_toplevel_decoration_v1_destroy(window->wl.xdg.decoration);
   1006 
   1007     if (window->wl.decorations.buffer)
   1008         wl_buffer_destroy(window->wl.decorations.buffer);
   1009 
   1010     if (window->wl.native)
   1011         wl_egl_window_destroy(window->wl.native);
   1012 
   1013     if (window->wl.shellSurface)
   1014         wl_shell_surface_destroy(window->wl.shellSurface);
   1015 
   1016     if (window->wl.xdg.toplevel)
   1017         xdg_toplevel_destroy(window->wl.xdg.toplevel);
   1018 
   1019     if (window->wl.xdg.surface)
   1020         xdg_surface_destroy(window->wl.xdg.surface);
   1021 
   1022     if (window->wl.surface)
   1023         wl_surface_destroy(window->wl.surface);
   1024 
   1025     free(window->wl.title);
   1026     free(window->wl.monitors);
   1027 }
   1028 
   1029 void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
   1030 {
   1031     if (window->wl.title)
   1032         free(window->wl.title);
   1033     window->wl.title = _glfw_strdup(title);
   1034     if (window->wl.xdg.toplevel)
   1035         xdg_toplevel_set_title(window->wl.xdg.toplevel, title);
   1036     else if (window->wl.shellSurface)
   1037         wl_shell_surface_set_title(window->wl.shellSurface, title);
   1038 }
   1039 
   1040 void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
   1041                                 int count, const GLFWimage* images)
   1042 {
   1043     _glfwInputError(GLFW_PLATFORM_ERROR,
   1044                     "Wayland: Setting window icon not supported");
   1045 }
   1046 
   1047 void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
   1048 {
   1049     // A Wayland client is not aware of its position, so just warn and leave it
   1050     // as (0, 0)
   1051 
   1052     _glfwInputError(GLFW_PLATFORM_ERROR,
   1053                     "Wayland: Window position retrieval not supported");
   1054 }
   1055 
   1056 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
   1057 {
   1058     // A Wayland client can not set its position, so just warn
   1059 
   1060     _glfwInputError(GLFW_PLATFORM_ERROR,
   1061                     "Wayland: Window position setting not supported");
   1062 }
   1063 
   1064 void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
   1065 {
   1066     if (width)
   1067         *width = window->wl.width;
   1068     if (height)
   1069         *height = window->wl.height;
   1070 }
   1071 
   1072 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
   1073 {
   1074     window->wl.width = width;
   1075     window->wl.height = height;
   1076     resizeWindow(window);
   1077 }
   1078 
   1079 void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
   1080                                       int minwidth, int minheight,
   1081                                       int maxwidth, int maxheight)
   1082 {
   1083     if (_glfw.wl.wmBase)
   1084     {
   1085         if (window->wl.xdg.toplevel)
   1086         {
   1087             if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE)
   1088                 minwidth = minheight = 0;
   1089             if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)
   1090                 maxwidth = maxheight = 0;
   1091             xdg_toplevel_set_min_size(window->wl.xdg.toplevel, minwidth, minheight);
   1092             xdg_toplevel_set_max_size(window->wl.xdg.toplevel, maxwidth, maxheight);
   1093             wl_surface_commit(window->wl.surface);
   1094         }
   1095     }
   1096     else
   1097     {
   1098         // TODO: find out how to trigger a resize.
   1099         // The actual limits are checked in the wl_shell_surface::configure handler.
   1100     }
   1101 }
   1102 
   1103 void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window,
   1104                                        int numer, int denom)
   1105 {
   1106     // TODO: find out how to trigger a resize.
   1107     // The actual limits are checked in the wl_shell_surface::configure handler.
   1108 }
   1109 
   1110 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window,
   1111                                      int* width, int* height)
   1112 {
   1113     _glfwPlatformGetWindowSize(window, width, height);
   1114     *width *= window->wl.scale;
   1115     *height *= window->wl.scale;
   1116 }
   1117 
   1118 void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
   1119                                      int* left, int* top,
   1120                                      int* right, int* bottom)
   1121 {
   1122     if (window->decorated && !window->monitor && !window->wl.decorations.serverSide)
   1123     {
   1124         if (top)
   1125             *top = _GLFW_DECORATION_TOP;
   1126         if (left)
   1127             *left = _GLFW_DECORATION_WIDTH;
   1128         if (right)
   1129             *right = _GLFW_DECORATION_WIDTH;
   1130         if (bottom)
   1131             *bottom = _GLFW_DECORATION_WIDTH;
   1132     }
   1133 }
   1134 
   1135 void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
   1136                                         float* xscale, float* yscale)
   1137 {
   1138     if (xscale)
   1139         *xscale = (float) window->wl.scale;
   1140     if (yscale)
   1141         *yscale = (float) window->wl.scale;
   1142 }
   1143 
   1144 void _glfwPlatformIconifyWindow(_GLFWwindow* window)
   1145 {
   1146     if (_glfw.wl.wmBase)
   1147     {
   1148         if (window->wl.xdg.toplevel)
   1149             xdg_toplevel_set_minimized(window->wl.xdg.toplevel);
   1150     }
   1151     else
   1152     {
   1153         _glfwInputError(GLFW_PLATFORM_ERROR,
   1154                         "Wayland: Iconify window not supported on wl_shell");
   1155     }
   1156 }
   1157 
   1158 void _glfwPlatformRestoreWindow(_GLFWwindow* window)
   1159 {
   1160     if (window->wl.xdg.toplevel)
   1161     {
   1162         if (window->monitor)
   1163             xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
   1164         if (window->wl.maximized)
   1165             xdg_toplevel_unset_maximized(window->wl.xdg.toplevel);
   1166         // There is no way to unset minimized, or even to know if we are
   1167         // minimized, so there is nothing to do here.
   1168     }
   1169     else if (window->wl.shellSurface)
   1170     {
   1171         if (window->monitor || window->wl.maximized)
   1172             wl_shell_surface_set_toplevel(window->wl.shellSurface);
   1173     }
   1174     _glfwInputWindowMonitor(window, NULL);
   1175     window->wl.maximized = GLFW_FALSE;
   1176 }
   1177 
   1178 void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
   1179 {
   1180     if (window->wl.xdg.toplevel)
   1181     {
   1182         xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
   1183     }
   1184     else if (window->wl.shellSurface)
   1185     {
   1186         // Let the compositor select the best output.
   1187         wl_shell_surface_set_maximized(window->wl.shellSurface, NULL);
   1188     }
   1189     window->wl.maximized = GLFW_TRUE;
   1190 }
   1191 
   1192 void _glfwPlatformShowWindow(_GLFWwindow* window)
   1193 {
   1194     if (!window->wl.visible)
   1195     {
   1196         if (_glfw.wl.wmBase)
   1197             createXdgSurface(window);
   1198         else if (!window->wl.shellSurface)
   1199             createShellSurface(window);
   1200         window->wl.visible = GLFW_TRUE;
   1201     }
   1202 }
   1203 
   1204 void _glfwPlatformHideWindow(_GLFWwindow* window)
   1205 {
   1206     if (window->wl.xdg.toplevel)
   1207     {
   1208         xdg_toplevel_destroy(window->wl.xdg.toplevel);
   1209         xdg_surface_destroy(window->wl.xdg.surface);
   1210         window->wl.xdg.toplevel = NULL;
   1211         window->wl.xdg.surface = NULL;
   1212     }
   1213     else if (window->wl.shellSurface)
   1214     {
   1215         wl_shell_surface_destroy(window->wl.shellSurface);
   1216         window->wl.shellSurface = NULL;
   1217     }
   1218     window->wl.visible = GLFW_FALSE;
   1219 }
   1220 
   1221 void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
   1222 {
   1223     // TODO
   1224     _glfwInputError(GLFW_PLATFORM_ERROR,
   1225                     "Wayland: Window attention request not implemented yet");
   1226 }
   1227 
   1228 void _glfwPlatformFocusWindow(_GLFWwindow* window)
   1229 {
   1230     _glfwInputError(GLFW_PLATFORM_ERROR,
   1231                     "Wayland: Focusing a window requires user interaction");
   1232 }
   1233 
   1234 void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
   1235                                    _GLFWmonitor* monitor,
   1236                                    int xpos, int ypos,
   1237                                    int width, int height,
   1238                                    int refreshRate)
   1239 {
   1240     if (monitor)
   1241     {
   1242         setFullscreen(window, monitor, refreshRate);
   1243     }
   1244     else
   1245     {
   1246         if (window->wl.xdg.toplevel)
   1247             xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
   1248         else if (window->wl.shellSurface)
   1249             wl_shell_surface_set_toplevel(window->wl.shellSurface);
   1250         setIdleInhibitor(window, GLFW_FALSE);
   1251         if (!_glfw.wl.decorationManager)
   1252             createDecorations(window);
   1253     }
   1254     _glfwInputWindowMonitor(window, monitor);
   1255 }
   1256 
   1257 int _glfwPlatformWindowFocused(_GLFWwindow* window)
   1258 {
   1259     return _glfw.wl.keyboardFocus == window;
   1260 }
   1261 
   1262 int _glfwPlatformWindowIconified(_GLFWwindow* window)
   1263 {
   1264     // wl_shell doesn't have any iconified concept, and xdg-shell doesn’t give
   1265     // any way to request whether a surface is iconified.
   1266     return GLFW_FALSE;
   1267 }
   1268 
   1269 int _glfwPlatformWindowVisible(_GLFWwindow* window)
   1270 {
   1271     return window->wl.visible;
   1272 }
   1273 
   1274 int _glfwPlatformWindowMaximized(_GLFWwindow* window)
   1275 {
   1276     return window->wl.maximized;
   1277 }
   1278 
   1279 int _glfwPlatformWindowHovered(_GLFWwindow* window)
   1280 {
   1281     return window->wl.hovered;
   1282 }
   1283 
   1284 int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
   1285 {
   1286     return window->wl.transparent;
   1287 }
   1288 
   1289 void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
   1290 {
   1291     // TODO
   1292     _glfwInputError(GLFW_PLATFORM_ERROR,
   1293                     "Wayland: Window attribute setting not implemented yet");
   1294 }
   1295 
   1296 void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
   1297 {
   1298     if (!window->monitor)
   1299     {
   1300         if (enabled)
   1301             createDecorations(window);
   1302         else
   1303             destroyDecorations(window);
   1304     }
   1305 }
   1306 
   1307 void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
   1308 {
   1309     // TODO
   1310     _glfwInputError(GLFW_PLATFORM_ERROR,
   1311                     "Wayland: Window attribute setting not implemented yet");
   1312 }
   1313 
   1314 float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
   1315 {
   1316     return 1.f;
   1317 }
   1318 
   1319 void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
   1320 {
   1321 }
   1322 
   1323 void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled)
   1324 {
   1325     // This is handled in relativePointerHandleRelativeMotion
   1326 }
   1327 
   1328 GLFWbool _glfwPlatformRawMouseMotionSupported(void)
   1329 {
   1330     return GLFW_TRUE;
   1331 }
   1332 
   1333 void _glfwPlatformPollEvents(void)
   1334 {
   1335     handleEvents(0);
   1336 }
   1337 
   1338 void _glfwPlatformWaitEvents(void)
   1339 {
   1340     handleEvents(-1);
   1341 }
   1342 
   1343 void _glfwPlatformWaitEventsTimeout(double timeout)
   1344 {
   1345     handleEvents((int) (timeout * 1e3));
   1346 }
   1347 
   1348 void _glfwPlatformPostEmptyEvent(void)
   1349 {
   1350     wl_display_sync(_glfw.wl.display);
   1351 }
   1352 
   1353 void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
   1354 {
   1355     if (xpos)
   1356         *xpos = window->wl.cursorPosX;
   1357     if (ypos)
   1358         *ypos = window->wl.cursorPosY;
   1359 }
   1360 
   1361 static GLFWbool isPointerLocked(_GLFWwindow* window);
   1362 
   1363 void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
   1364 {
   1365     if (isPointerLocked(window))
   1366     {
   1367         zwp_locked_pointer_v1_set_cursor_position_hint(
   1368             window->wl.pointerLock.lockedPointer,
   1369             wl_fixed_from_double(x), wl_fixed_from_double(y));
   1370         wl_surface_commit(window->wl.surface);
   1371     }
   1372 }
   1373 
   1374 void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
   1375 {
   1376     _glfwPlatformSetCursor(window, window->wl.currentCursor);
   1377 }
   1378 
   1379 const char* _glfwPlatformGetScancodeName(int scancode)
   1380 {
   1381     // TODO
   1382     return NULL;
   1383 }
   1384 
   1385 int _glfwPlatformGetKeyScancode(int key)
   1386 {
   1387     return _glfw.wl.scancodes[key];
   1388 }
   1389 
   1390 int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
   1391                               const GLFWimage* image,
   1392                               int xhot, int yhot)
   1393 {
   1394     cursor->wl.buffer = createShmBuffer(image);
   1395     if (!cursor->wl.buffer)
   1396         return GLFW_FALSE;
   1397 
   1398     cursor->wl.width = image->width;
   1399     cursor->wl.height = image->height;
   1400     cursor->wl.xhot = xhot;
   1401     cursor->wl.yhot = yhot;
   1402     return GLFW_TRUE;
   1403 }
   1404 
   1405 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
   1406 {
   1407     struct wl_cursor* standardCursor;
   1408 
   1409     standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme,
   1410                                                 translateCursorShape(shape));
   1411     if (!standardCursor)
   1412     {
   1413         _glfwInputError(GLFW_PLATFORM_ERROR,
   1414                         "Wayland: Standard cursor \"%s\" not found",
   1415                         translateCursorShape(shape));
   1416         return GLFW_FALSE;
   1417     }
   1418 
   1419     cursor->wl.cursor = standardCursor;
   1420     cursor->wl.currentImage = 0;
   1421 
   1422     if (_glfw.wl.cursorThemeHiDPI)
   1423     {
   1424         standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI,
   1425                                                     translateCursorShape(shape));
   1426         cursor->wl.cursorHiDPI = standardCursor;
   1427     }
   1428 
   1429     return GLFW_TRUE;
   1430 }
   1431 
   1432 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
   1433 {
   1434     // If it's a standard cursor we don't need to do anything here
   1435     if (cursor->wl.cursor)
   1436         return;
   1437 
   1438     if (cursor->wl.buffer)
   1439         wl_buffer_destroy(cursor->wl.buffer);
   1440 }
   1441 
   1442 static void relativePointerHandleRelativeMotion(void* data,
   1443                                                 struct zwp_relative_pointer_v1* pointer,
   1444                                                 uint32_t timeHi,
   1445                                                 uint32_t timeLo,
   1446                                                 wl_fixed_t dx,
   1447                                                 wl_fixed_t dy,
   1448                                                 wl_fixed_t dxUnaccel,
   1449                                                 wl_fixed_t dyUnaccel)
   1450 {
   1451     _GLFWwindow* window = data;
   1452     double xpos = window->virtualCursorPosX;
   1453     double ypos = window->virtualCursorPosY;
   1454 
   1455     if (window->cursorMode != GLFW_CURSOR_DISABLED)
   1456         return;
   1457 
   1458     if (window->rawMouseMotion)
   1459     {
   1460         xpos += wl_fixed_to_double(dxUnaccel);
   1461         ypos += wl_fixed_to_double(dyUnaccel);
   1462     }
   1463     else
   1464     {
   1465         xpos += wl_fixed_to_double(dx);
   1466         ypos += wl_fixed_to_double(dy);
   1467     }
   1468 
   1469     _glfwInputCursorPos(window, xpos, ypos);
   1470 }
   1471 
   1472 static const struct zwp_relative_pointer_v1_listener relativePointerListener = {
   1473     relativePointerHandleRelativeMotion
   1474 };
   1475 
   1476 static void lockedPointerHandleLocked(void* data,
   1477                                       struct zwp_locked_pointer_v1* lockedPointer)
   1478 {
   1479 }
   1480 
   1481 static void unlockPointer(_GLFWwindow* window)
   1482 {
   1483     struct zwp_relative_pointer_v1* relativePointer =
   1484         window->wl.pointerLock.relativePointer;
   1485     struct zwp_locked_pointer_v1* lockedPointer =
   1486         window->wl.pointerLock.lockedPointer;
   1487 
   1488     zwp_relative_pointer_v1_destroy(relativePointer);
   1489     zwp_locked_pointer_v1_destroy(lockedPointer);
   1490 
   1491     window->wl.pointerLock.relativePointer = NULL;
   1492     window->wl.pointerLock.lockedPointer = NULL;
   1493 }
   1494 
   1495 static void lockPointer(_GLFWwindow* window);
   1496 
   1497 static void lockedPointerHandleUnlocked(void* data,
   1498                                         struct zwp_locked_pointer_v1* lockedPointer)
   1499 {
   1500 }
   1501 
   1502 static const struct zwp_locked_pointer_v1_listener lockedPointerListener = {
   1503     lockedPointerHandleLocked,
   1504     lockedPointerHandleUnlocked
   1505 };
   1506 
   1507 static void lockPointer(_GLFWwindow* window)
   1508 {
   1509     struct zwp_relative_pointer_v1* relativePointer;
   1510     struct zwp_locked_pointer_v1* lockedPointer;
   1511 
   1512     if (!_glfw.wl.relativePointerManager)
   1513     {
   1514         _glfwInputError(GLFW_PLATFORM_ERROR,
   1515                         "Wayland: no relative pointer manager");
   1516         return;
   1517     }
   1518 
   1519     relativePointer =
   1520         zwp_relative_pointer_manager_v1_get_relative_pointer(
   1521             _glfw.wl.relativePointerManager,
   1522             _glfw.wl.pointer);
   1523     zwp_relative_pointer_v1_add_listener(relativePointer,
   1524                                          &relativePointerListener,
   1525                                          window);
   1526 
   1527     lockedPointer =
   1528         zwp_pointer_constraints_v1_lock_pointer(
   1529             _glfw.wl.pointerConstraints,
   1530             window->wl.surface,
   1531             _glfw.wl.pointer,
   1532             NULL,
   1533             ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
   1534     zwp_locked_pointer_v1_add_listener(lockedPointer,
   1535                                        &lockedPointerListener,
   1536                                        window);
   1537 
   1538     window->wl.pointerLock.relativePointer = relativePointer;
   1539     window->wl.pointerLock.lockedPointer = lockedPointer;
   1540 
   1541     wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
   1542                           NULL, 0, 0);
   1543 }
   1544 
   1545 static GLFWbool isPointerLocked(_GLFWwindow* window)
   1546 {
   1547     return window->wl.pointerLock.lockedPointer != NULL;
   1548 }
   1549 
   1550 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
   1551 {
   1552     struct wl_cursor* defaultCursor;
   1553     struct wl_cursor* defaultCursorHiDPI = NULL;
   1554 
   1555     if (!_glfw.wl.pointer)
   1556         return;
   1557 
   1558     window->wl.currentCursor = cursor;
   1559 
   1560     // If we're not in the correct window just save the cursor
   1561     // the next time the pointer enters the window the cursor will change
   1562     if (window != _glfw.wl.pointerFocus || window->wl.decorations.focus != mainWindow)
   1563         return;
   1564 
   1565     // Unlock possible pointer lock if no longer disabled.
   1566     if (window->cursorMode != GLFW_CURSOR_DISABLED && isPointerLocked(window))
   1567         unlockPointer(window);
   1568 
   1569     if (window->cursorMode == GLFW_CURSOR_NORMAL)
   1570     {
   1571         if (cursor)
   1572             setCursorImage(window, &cursor->wl);
   1573         else
   1574         {
   1575             defaultCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme,
   1576                                                        "left_ptr");
   1577             if (!defaultCursor)
   1578             {
   1579                 _glfwInputError(GLFW_PLATFORM_ERROR,
   1580                                 "Wayland: Standard cursor not found");
   1581                 return;
   1582             }
   1583             if (_glfw.wl.cursorThemeHiDPI)
   1584                 defaultCursorHiDPI =
   1585                     wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI,
   1586                                                "left_ptr");
   1587             _GLFWcursorWayland cursorWayland = {
   1588                 defaultCursor,
   1589                 defaultCursorHiDPI,
   1590                 NULL,
   1591                 0, 0,
   1592                 0, 0,
   1593                 0
   1594             };
   1595             setCursorImage(window, &cursorWayland);
   1596         }
   1597     }
   1598     else if (window->cursorMode == GLFW_CURSOR_DISABLED)
   1599     {
   1600         if (!isPointerLocked(window))
   1601             lockPointer(window);
   1602     }
   1603     else if (window->cursorMode == GLFW_CURSOR_HIDDEN)
   1604     {
   1605         wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, NULL, 0, 0);
   1606     }
   1607 }
   1608 
   1609 static void dataSourceHandleTarget(void* data,
   1610                                    struct wl_data_source* dataSource,
   1611                                    const char* mimeType)
   1612 {
   1613     if (_glfw.wl.dataSource != dataSource)
   1614     {
   1615         _glfwInputError(GLFW_PLATFORM_ERROR,
   1616                         "Wayland: Unknown clipboard data source");
   1617         return;
   1618     }
   1619 }
   1620 
   1621 static void dataSourceHandleSend(void* data,
   1622                                  struct wl_data_source* dataSource,
   1623                                  const char* mimeType,
   1624                                  int fd)
   1625 {
   1626     const char* string = _glfw.wl.clipboardSendString;
   1627     size_t len = _glfw.wl.clipboardSendSize;
   1628     int ret;
   1629 
   1630     if (_glfw.wl.dataSource != dataSource)
   1631     {
   1632         _glfwInputError(GLFW_PLATFORM_ERROR,
   1633                         "Wayland: Unknown clipboard data source");
   1634         return;
   1635     }
   1636 
   1637     if (!string)
   1638     {
   1639         _glfwInputError(GLFW_PLATFORM_ERROR,
   1640                         "Wayland: Copy requested from an invalid string");
   1641         return;
   1642     }
   1643 
   1644     if (strcmp(mimeType, "text/plain;charset=utf-8") != 0)
   1645     {
   1646         _glfwInputError(GLFW_PLATFORM_ERROR,
   1647                         "Wayland: Wrong MIME type asked from clipboard");
   1648         close(fd);
   1649         return;
   1650     }
   1651 
   1652     while (len > 0)
   1653     {
   1654         ret = write(fd, string, len);
   1655         if (ret == -1 && errno == EINTR)
   1656             continue;
   1657         if (ret == -1)
   1658         {
   1659             // TODO: also report errno maybe.
   1660             _glfwInputError(GLFW_PLATFORM_ERROR,
   1661                             "Wayland: Error while writing the clipboard");
   1662             close(fd);
   1663             return;
   1664         }
   1665         len -= ret;
   1666     }
   1667     close(fd);
   1668 }
   1669 
   1670 static void dataSourceHandleCancelled(void* data,
   1671                                       struct wl_data_source* dataSource)
   1672 {
   1673     wl_data_source_destroy(dataSource);
   1674 
   1675     if (_glfw.wl.dataSource != dataSource)
   1676     {
   1677         _glfwInputError(GLFW_PLATFORM_ERROR,
   1678                         "Wayland: Unknown clipboard data source");
   1679         return;
   1680     }
   1681 
   1682     _glfw.wl.dataSource = NULL;
   1683 }
   1684 
   1685 static const struct wl_data_source_listener dataSourceListener = {
   1686     dataSourceHandleTarget,
   1687     dataSourceHandleSend,
   1688     dataSourceHandleCancelled,
   1689 };
   1690 
   1691 void _glfwPlatformSetClipboardString(const char* string)
   1692 {
   1693     if (_glfw.wl.dataSource)
   1694     {
   1695         wl_data_source_destroy(_glfw.wl.dataSource);
   1696         _glfw.wl.dataSource = NULL;
   1697     }
   1698 
   1699     if (_glfw.wl.clipboardSendString)
   1700     {
   1701         free(_glfw.wl.clipboardSendString);
   1702         _glfw.wl.clipboardSendString = NULL;
   1703     }
   1704 
   1705     _glfw.wl.clipboardSendString = strdup(string);
   1706     if (!_glfw.wl.clipboardSendString)
   1707     {
   1708         _glfwInputError(GLFW_PLATFORM_ERROR,
   1709                         "Wayland: Impossible to allocate clipboard string");
   1710         return;
   1711     }
   1712     _glfw.wl.clipboardSendSize = strlen(string);
   1713     _glfw.wl.dataSource =
   1714         wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager);
   1715     if (!_glfw.wl.dataSource)
   1716     {
   1717         _glfwInputError(GLFW_PLATFORM_ERROR,
   1718                         "Wayland: Impossible to create clipboard source");
   1719         free(_glfw.wl.clipboardSendString);
   1720         return;
   1721     }
   1722     wl_data_source_add_listener(_glfw.wl.dataSource,
   1723                                 &dataSourceListener,
   1724                                 NULL);
   1725     wl_data_source_offer(_glfw.wl.dataSource, "text/plain;charset=utf-8");
   1726     wl_data_device_set_selection(_glfw.wl.dataDevice,
   1727                                  _glfw.wl.dataSource,
   1728                                  _glfw.wl.serial);
   1729 }
   1730 
   1731 static GLFWbool growClipboardString(void)
   1732 {
   1733     char* clipboard = _glfw.wl.clipboardString;
   1734 
   1735     clipboard = realloc(clipboard, _glfw.wl.clipboardSize * 2);
   1736     if (!clipboard)
   1737     {
   1738         _glfwInputError(GLFW_PLATFORM_ERROR,
   1739                         "Wayland: Impossible to grow clipboard string");
   1740         return GLFW_FALSE;
   1741     }
   1742     _glfw.wl.clipboardString = clipboard;
   1743     _glfw.wl.clipboardSize = _glfw.wl.clipboardSize * 2;
   1744     return GLFW_TRUE;
   1745 }
   1746 
   1747 const char* _glfwPlatformGetClipboardString(void)
   1748 {
   1749     int fds[2];
   1750     int ret;
   1751     size_t len = 0;
   1752 
   1753     if (!_glfw.wl.dataOffer)
   1754     {
   1755         _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
   1756                         "No clipboard data has been sent yet");
   1757         return NULL;
   1758     }
   1759 
   1760     ret = pipe2(fds, O_CLOEXEC);
   1761     if (ret < 0)
   1762     {
   1763         // TODO: also report errno maybe?
   1764         _glfwInputError(GLFW_PLATFORM_ERROR,
   1765                         "Wayland: Impossible to create clipboard pipe fds");
   1766         return NULL;
   1767     }
   1768 
   1769     wl_data_offer_receive(_glfw.wl.dataOffer, "text/plain;charset=utf-8", fds[1]);
   1770     close(fds[1]);
   1771 
   1772     // XXX: this is a huge hack, this function shouldn’t be synchronous!
   1773     handleEvents(-1);
   1774 
   1775     while (1)
   1776     {
   1777         // Grow the clipboard if we need to paste something bigger, there is no
   1778         // shrink operation yet.
   1779         if (len + 4096 > _glfw.wl.clipboardSize)
   1780         {
   1781             if (!growClipboardString())
   1782             {
   1783                 close(fds[0]);
   1784                 return NULL;
   1785             }
   1786         }
   1787 
   1788         // Then read from the fd to the clipboard, handling all known errors.
   1789         ret = read(fds[0], _glfw.wl.clipboardString + len, 4096);
   1790         if (ret == 0)
   1791             break;
   1792         if (ret == -1 && errno == EINTR)
   1793             continue;
   1794         if (ret == -1)
   1795         {
   1796             // TODO: also report errno maybe.
   1797             _glfwInputError(GLFW_PLATFORM_ERROR,
   1798                             "Wayland: Impossible to read from clipboard fd");
   1799             close(fds[0]);
   1800             return NULL;
   1801         }
   1802         len += ret;
   1803     }
   1804     close(fds[0]);
   1805     if (len + 1 > _glfw.wl.clipboardSize)
   1806     {
   1807         if (!growClipboardString())
   1808             return NULL;
   1809     }
   1810     _glfw.wl.clipboardString[len] = '\0';
   1811     return _glfw.wl.clipboardString;
   1812 }
   1813 
   1814 void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
   1815 {
   1816     if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_wayland_surface)
   1817         return;
   1818 
   1819     extensions[0] = "VK_KHR_surface";
   1820     extensions[1] = "VK_KHR_wayland_surface";
   1821 }
   1822 
   1823 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
   1824                                                       VkPhysicalDevice device,
   1825                                                       uint32_t queuefamily)
   1826 {
   1827     PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
   1828         vkGetPhysicalDeviceWaylandPresentationSupportKHR =
   1829         (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)
   1830         vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR");
   1831     if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR)
   1832     {
   1833         _glfwInputError(GLFW_API_UNAVAILABLE,
   1834                         "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension");
   1835         return VK_NULL_HANDLE;
   1836     }
   1837 
   1838     return vkGetPhysicalDeviceWaylandPresentationSupportKHR(device,
   1839                                                             queuefamily,
   1840                                                             _glfw.wl.display);
   1841 }
   1842 
   1843 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
   1844                                           _GLFWwindow* window,
   1845                                           const VkAllocationCallbacks* allocator,
   1846                                           VkSurfaceKHR* surface)
   1847 {
   1848     VkResult err;
   1849     VkWaylandSurfaceCreateInfoKHR sci;
   1850     PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR;
   1851 
   1852     vkCreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR)
   1853         vkGetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR");
   1854     if (!vkCreateWaylandSurfaceKHR)
   1855     {
   1856         _glfwInputError(GLFW_API_UNAVAILABLE,
   1857                         "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension");
   1858         return VK_ERROR_EXTENSION_NOT_PRESENT;
   1859     }
   1860 
   1861     memset(&sci, 0, sizeof(sci));
   1862     sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
   1863     sci.display = _glfw.wl.display;
   1864     sci.surface = window->wl.surface;
   1865 
   1866     err = vkCreateWaylandSurfaceKHR(instance, &sci, allocator, surface);
   1867     if (err)
   1868     {
   1869         _glfwInputError(GLFW_PLATFORM_ERROR,
   1870                         "Wayland: Failed to create Vulkan surface: %s",
   1871                         _glfwGetVulkanResultString(err));
   1872     }
   1873 
   1874     return err;
   1875 }
   1876 
   1877 
   1878 //////////////////////////////////////////////////////////////////////////
   1879 //////                        GLFW native API                       //////
   1880 //////////////////////////////////////////////////////////////////////////
   1881 
   1882 GLFWAPI struct wl_display* glfwGetWaylandDisplay(void)
   1883 {
   1884     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
   1885     return _glfw.wl.display;
   1886 }
   1887 
   1888 GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle)
   1889 {
   1890     _GLFWwindow* window = (_GLFWwindow*) handle;
   1891     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
   1892     return window->wl.surface;
   1893 }
   1894