zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

wl_window.c (58821B)


      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: %s",
    212                         length, strerror(errno));
    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: %s", strerror(errno));
    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->subsurface)
    316         wl_subsurface_destroy(decoration->subsurface);
    317     if (decoration->surface)
    318         wl_surface_destroy(decoration->surface);
    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             if (_glfw.wl.keyboardFocus)
    874             {
    875                 for (i = 0; i < repeats; ++i)
    876                 {
    877                     _glfwInputKey(_glfw.wl.keyboardFocus,
    878                                   _glfw.wl.keyboardLastKey,
    879                                   _glfw.wl.keyboardLastScancode,
    880                                   GLFW_REPEAT,
    881                                   _glfw.wl.xkb.modifiers);
    882                 }
    883             }
    884         }
    885 
    886         if (fds[2].revents & POLLIN)
    887         {
    888             read_ret = read(_glfw.wl.cursorTimerfd, &repeats, sizeof(repeats));
    889             if (read_ret != 8)
    890                 return;
    891 
    892             incrementCursorImage(_glfw.wl.pointerFocus);
    893         }
    894     }
    895     else
    896     {
    897         wl_display_cancel_read(display);
    898     }
    899 }
    900 
    901 // Translates a GLFW standard cursor to a theme cursor name
    902 //
    903 static char *translateCursorShape(int shape)
    904 {
    905     switch (shape)
    906     {
    907         case GLFW_ARROW_CURSOR:
    908             return "left_ptr";
    909         case GLFW_IBEAM_CURSOR:
    910             return "xterm";
    911         case GLFW_CROSSHAIR_CURSOR:
    912             return "crosshair";
    913         case GLFW_HAND_CURSOR:
    914             return "hand2";
    915         case GLFW_HRESIZE_CURSOR:
    916             return "sb_h_double_arrow";
    917         case GLFW_VRESIZE_CURSOR:
    918             return "sb_v_double_arrow";
    919     }
    920     return NULL;
    921 }
    922 
    923 //////////////////////////////////////////////////////////////////////////
    924 //////                       GLFW platform API                      //////
    925 //////////////////////////////////////////////////////////////////////////
    926 
    927 int _glfwPlatformCreateWindow(_GLFWwindow* window,
    928                               const _GLFWwndconfig* wndconfig,
    929                               const _GLFWctxconfig* ctxconfig,
    930                               const _GLFWfbconfig* fbconfig)
    931 {
    932     window->wl.transparent = fbconfig->transparent;
    933 
    934     if (!createSurface(window, wndconfig))
    935         return GLFW_FALSE;
    936 
    937     if (ctxconfig->client != GLFW_NO_API)
    938     {
    939         if (ctxconfig->source == GLFW_EGL_CONTEXT_API ||
    940             ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
    941         {
    942             if (!_glfwInitEGL())
    943                 return GLFW_FALSE;
    944             if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
    945                 return GLFW_FALSE;
    946         }
    947         else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
    948         {
    949             if (!_glfwInitOSMesa())
    950                 return GLFW_FALSE;
    951             if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
    952                 return GLFW_FALSE;
    953         }
    954     }
    955 
    956     if (wndconfig->title)
    957         window->wl.title = _glfw_strdup(wndconfig->title);
    958 
    959     if (wndconfig->visible)
    960     {
    961         if (_glfw.wl.wmBase)
    962         {
    963             if (!createXdgSurface(window))
    964                 return GLFW_FALSE;
    965         }
    966         else
    967         {
    968             if (!createShellSurface(window))
    969                 return GLFW_FALSE;
    970         }
    971 
    972         window->wl.visible = GLFW_TRUE;
    973     }
    974     else
    975     {
    976         window->wl.xdg.surface = NULL;
    977         window->wl.xdg.toplevel = NULL;
    978         window->wl.shellSurface = NULL;
    979         window->wl.visible = GLFW_FALSE;
    980     }
    981 
    982     window->wl.currentCursor = NULL;
    983 
    984     window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*));
    985     window->wl.monitorsCount = 0;
    986     window->wl.monitorsSize = 1;
    987 
    988     return GLFW_TRUE;
    989 }
    990 
    991 void _glfwPlatformDestroyWindow(_GLFWwindow* window)
    992 {
    993     if (window == _glfw.wl.pointerFocus)
    994     {
    995         _glfw.wl.pointerFocus = NULL;
    996         _glfwInputCursorEnter(window, GLFW_FALSE);
    997     }
    998     if (window == _glfw.wl.keyboardFocus)
    999     {
   1000         _glfw.wl.keyboardFocus = NULL;
   1001         _glfwInputWindowFocus(window, GLFW_FALSE);
   1002     }
   1003 
   1004     if (window->wl.idleInhibitor)
   1005         zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor);
   1006 
   1007     if (window->context.destroy)
   1008         window->context.destroy(window);
   1009 
   1010     destroyDecorations(window);
   1011     if (window->wl.xdg.decoration)
   1012         zxdg_toplevel_decoration_v1_destroy(window->wl.xdg.decoration);
   1013 
   1014     if (window->wl.decorations.buffer)
   1015         wl_buffer_destroy(window->wl.decorations.buffer);
   1016 
   1017     if (window->wl.native)
   1018         wl_egl_window_destroy(window->wl.native);
   1019 
   1020     if (window->wl.shellSurface)
   1021         wl_shell_surface_destroy(window->wl.shellSurface);
   1022 
   1023     if (window->wl.xdg.toplevel)
   1024         xdg_toplevel_destroy(window->wl.xdg.toplevel);
   1025 
   1026     if (window->wl.xdg.surface)
   1027         xdg_surface_destroy(window->wl.xdg.surface);
   1028 
   1029     if (window->wl.surface)
   1030         wl_surface_destroy(window->wl.surface);
   1031 
   1032     free(window->wl.title);
   1033     free(window->wl.monitors);
   1034 }
   1035 
   1036 void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
   1037 {
   1038     if (window->wl.title)
   1039         free(window->wl.title);
   1040     window->wl.title = _glfw_strdup(title);
   1041     if (window->wl.xdg.toplevel)
   1042         xdg_toplevel_set_title(window->wl.xdg.toplevel, title);
   1043     else if (window->wl.shellSurface)
   1044         wl_shell_surface_set_title(window->wl.shellSurface, title);
   1045 }
   1046 
   1047 void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
   1048                                 int count, const GLFWimage* images)
   1049 {
   1050     _glfwInputError(GLFW_PLATFORM_ERROR,
   1051                     "Wayland: Setting window icon not supported");
   1052 }
   1053 
   1054 void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
   1055 {
   1056     // A Wayland client is not aware of its position, so just warn and leave it
   1057     // as (0, 0)
   1058 
   1059     _glfwInputError(GLFW_PLATFORM_ERROR,
   1060                     "Wayland: Window position retrieval not supported");
   1061 }
   1062 
   1063 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
   1064 {
   1065     // A Wayland client can not set its position, so just warn
   1066 
   1067     _glfwInputError(GLFW_PLATFORM_ERROR,
   1068                     "Wayland: Window position setting not supported");
   1069 }
   1070 
   1071 void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
   1072 {
   1073     if (width)
   1074         *width = window->wl.width;
   1075     if (height)
   1076         *height = window->wl.height;
   1077 }
   1078 
   1079 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
   1080 {
   1081     window->wl.width = width;
   1082     window->wl.height = height;
   1083     resizeWindow(window);
   1084 }
   1085 
   1086 void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
   1087                                       int minwidth, int minheight,
   1088                                       int maxwidth, int maxheight)
   1089 {
   1090     if (_glfw.wl.wmBase)
   1091     {
   1092         if (window->wl.xdg.toplevel)
   1093         {
   1094             if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE)
   1095                 minwidth = minheight = 0;
   1096             if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)
   1097                 maxwidth = maxheight = 0;
   1098             xdg_toplevel_set_min_size(window->wl.xdg.toplevel, minwidth, minheight);
   1099             xdg_toplevel_set_max_size(window->wl.xdg.toplevel, maxwidth, maxheight);
   1100             wl_surface_commit(window->wl.surface);
   1101         }
   1102     }
   1103     else
   1104     {
   1105         // TODO: find out how to trigger a resize.
   1106         // The actual limits are checked in the wl_shell_surface::configure handler.
   1107     }
   1108 }
   1109 
   1110 void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window,
   1111                                        int numer, int denom)
   1112 {
   1113     // TODO: find out how to trigger a resize.
   1114     // The actual limits are checked in the wl_shell_surface::configure handler.
   1115 }
   1116 
   1117 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window,
   1118                                      int* width, int* height)
   1119 {
   1120     _glfwPlatformGetWindowSize(window, width, height);
   1121     if (width)
   1122         *width *= window->wl.scale;
   1123     if (height)
   1124         *height *= window->wl.scale;
   1125 }
   1126 
   1127 void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
   1128                                      int* left, int* top,
   1129                                      int* right, int* bottom)
   1130 {
   1131     if (window->decorated && !window->monitor && !window->wl.decorations.serverSide)
   1132     {
   1133         if (top)
   1134             *top = _GLFW_DECORATION_TOP;
   1135         if (left)
   1136             *left = _GLFW_DECORATION_WIDTH;
   1137         if (right)
   1138             *right = _GLFW_DECORATION_WIDTH;
   1139         if (bottom)
   1140             *bottom = _GLFW_DECORATION_WIDTH;
   1141     }
   1142 }
   1143 
   1144 void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
   1145                                         float* xscale, float* yscale)
   1146 {
   1147     if (xscale)
   1148         *xscale = (float) window->wl.scale;
   1149     if (yscale)
   1150         *yscale = (float) window->wl.scale;
   1151 }
   1152 
   1153 void _glfwPlatformIconifyWindow(_GLFWwindow* window)
   1154 {
   1155     if (_glfw.wl.wmBase)
   1156     {
   1157         if (window->wl.xdg.toplevel)
   1158             xdg_toplevel_set_minimized(window->wl.xdg.toplevel);
   1159     }
   1160     else
   1161     {
   1162         _glfwInputError(GLFW_PLATFORM_ERROR,
   1163                         "Wayland: Iconify window not supported on wl_shell");
   1164     }
   1165 }
   1166 
   1167 void _glfwPlatformRestoreWindow(_GLFWwindow* window)
   1168 {
   1169     if (window->wl.xdg.toplevel)
   1170     {
   1171         if (window->monitor)
   1172             xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
   1173         if (window->wl.maximized)
   1174             xdg_toplevel_unset_maximized(window->wl.xdg.toplevel);
   1175         // There is no way to unset minimized, or even to know if we are
   1176         // minimized, so there is nothing to do here.
   1177     }
   1178     else if (window->wl.shellSurface)
   1179     {
   1180         if (window->monitor || window->wl.maximized)
   1181             wl_shell_surface_set_toplevel(window->wl.shellSurface);
   1182     }
   1183     _glfwInputWindowMonitor(window, NULL);
   1184     window->wl.maximized = GLFW_FALSE;
   1185 }
   1186 
   1187 void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
   1188 {
   1189     if (window->wl.xdg.toplevel)
   1190     {
   1191         xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
   1192     }
   1193     else if (window->wl.shellSurface)
   1194     {
   1195         // Let the compositor select the best output.
   1196         wl_shell_surface_set_maximized(window->wl.shellSurface, NULL);
   1197     }
   1198     window->wl.maximized = GLFW_TRUE;
   1199 }
   1200 
   1201 void _glfwPlatformShowWindow(_GLFWwindow* window)
   1202 {
   1203     if (!window->wl.visible)
   1204     {
   1205         if (_glfw.wl.wmBase)
   1206             createXdgSurface(window);
   1207         else if (!window->wl.shellSurface)
   1208             createShellSurface(window);
   1209         window->wl.visible = GLFW_TRUE;
   1210     }
   1211 }
   1212 
   1213 void _glfwPlatformHideWindow(_GLFWwindow* window)
   1214 {
   1215     if (window->wl.xdg.toplevel)
   1216     {
   1217         xdg_toplevel_destroy(window->wl.xdg.toplevel);
   1218         xdg_surface_destroy(window->wl.xdg.surface);
   1219         window->wl.xdg.toplevel = NULL;
   1220         window->wl.xdg.surface = NULL;
   1221     }
   1222     else if (window->wl.shellSurface)
   1223     {
   1224         wl_shell_surface_destroy(window->wl.shellSurface);
   1225         window->wl.shellSurface = NULL;
   1226     }
   1227     window->wl.visible = GLFW_FALSE;
   1228 }
   1229 
   1230 void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
   1231 {
   1232     // TODO
   1233     _glfwInputError(GLFW_PLATFORM_ERROR,
   1234                     "Wayland: Window attention request not implemented yet");
   1235 }
   1236 
   1237 void _glfwPlatformFocusWindow(_GLFWwindow* window)
   1238 {
   1239     _glfwInputError(GLFW_PLATFORM_ERROR,
   1240                     "Wayland: Focusing a window requires user interaction");
   1241 }
   1242 
   1243 void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
   1244                                    _GLFWmonitor* monitor,
   1245                                    int xpos, int ypos,
   1246                                    int width, int height,
   1247                                    int refreshRate)
   1248 {
   1249     if (monitor)
   1250     {
   1251         setFullscreen(window, monitor, refreshRate);
   1252     }
   1253     else
   1254     {
   1255         if (window->wl.xdg.toplevel)
   1256             xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
   1257         else if (window->wl.shellSurface)
   1258             wl_shell_surface_set_toplevel(window->wl.shellSurface);
   1259         setIdleInhibitor(window, GLFW_FALSE);
   1260         if (!_glfw.wl.decorationManager)
   1261             createDecorations(window);
   1262     }
   1263     _glfwInputWindowMonitor(window, monitor);
   1264 }
   1265 
   1266 int _glfwPlatformWindowFocused(_GLFWwindow* window)
   1267 {
   1268     return _glfw.wl.keyboardFocus == window;
   1269 }
   1270 
   1271 int _glfwPlatformWindowIconified(_GLFWwindow* window)
   1272 {
   1273     // wl_shell doesn't have any iconified concept, and xdg-shell doesn’t give
   1274     // any way to request whether a surface is iconified.
   1275     return GLFW_FALSE;
   1276 }
   1277 
   1278 int _glfwPlatformWindowVisible(_GLFWwindow* window)
   1279 {
   1280     return window->wl.visible;
   1281 }
   1282 
   1283 int _glfwPlatformWindowMaximized(_GLFWwindow* window)
   1284 {
   1285     return window->wl.maximized;
   1286 }
   1287 
   1288 int _glfwPlatformWindowHovered(_GLFWwindow* window)
   1289 {
   1290     return window->wl.hovered;
   1291 }
   1292 
   1293 int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
   1294 {
   1295     return window->wl.transparent;
   1296 }
   1297 
   1298 void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
   1299 {
   1300     // TODO
   1301     _glfwInputError(GLFW_PLATFORM_ERROR,
   1302                     "Wayland: Window attribute setting not implemented yet");
   1303 }
   1304 
   1305 void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
   1306 {
   1307     if (!window->monitor)
   1308     {
   1309         if (enabled)
   1310             createDecorations(window);
   1311         else
   1312             destroyDecorations(window);
   1313     }
   1314 }
   1315 
   1316 void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
   1317 {
   1318     // TODO
   1319     _glfwInputError(GLFW_PLATFORM_ERROR,
   1320                     "Wayland: Window attribute setting not implemented yet");
   1321 }
   1322 
   1323 float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
   1324 {
   1325     return 1.f;
   1326 }
   1327 
   1328 void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
   1329 {
   1330 }
   1331 
   1332 void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled)
   1333 {
   1334     // This is handled in relativePointerHandleRelativeMotion
   1335 }
   1336 
   1337 GLFWbool _glfwPlatformRawMouseMotionSupported(void)
   1338 {
   1339     return GLFW_TRUE;
   1340 }
   1341 
   1342 void _glfwPlatformPollEvents(void)
   1343 {
   1344     handleEvents(0);
   1345 }
   1346 
   1347 void _glfwPlatformWaitEvents(void)
   1348 {
   1349     handleEvents(-1);
   1350 }
   1351 
   1352 void _glfwPlatformWaitEventsTimeout(double timeout)
   1353 {
   1354     handleEvents((int) (timeout * 1e3));
   1355 }
   1356 
   1357 void _glfwPlatformPostEmptyEvent(void)
   1358 {
   1359     wl_display_sync(_glfw.wl.display);
   1360 }
   1361 
   1362 void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
   1363 {
   1364     if (xpos)
   1365         *xpos = window->wl.cursorPosX;
   1366     if (ypos)
   1367         *ypos = window->wl.cursorPosY;
   1368 }
   1369 
   1370 static GLFWbool isPointerLocked(_GLFWwindow* window);
   1371 
   1372 void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
   1373 {
   1374     if (isPointerLocked(window))
   1375     {
   1376         zwp_locked_pointer_v1_set_cursor_position_hint(
   1377             window->wl.pointerLock.lockedPointer,
   1378             wl_fixed_from_double(x), wl_fixed_from_double(y));
   1379         wl_surface_commit(window->wl.surface);
   1380     }
   1381 }
   1382 
   1383 void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
   1384 {
   1385     _glfwPlatformSetCursor(window, window->wl.currentCursor);
   1386 }
   1387 
   1388 const char* _glfwPlatformGetScancodeName(int scancode)
   1389 {
   1390     // TODO
   1391     return NULL;
   1392 }
   1393 
   1394 int _glfwPlatformGetKeyScancode(int key)
   1395 {
   1396     return _glfw.wl.scancodes[key];
   1397 }
   1398 
   1399 int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
   1400                               const GLFWimage* image,
   1401                               int xhot, int yhot)
   1402 {
   1403     cursor->wl.buffer = createShmBuffer(image);
   1404     if (!cursor->wl.buffer)
   1405         return GLFW_FALSE;
   1406 
   1407     cursor->wl.width = image->width;
   1408     cursor->wl.height = image->height;
   1409     cursor->wl.xhot = xhot;
   1410     cursor->wl.yhot = yhot;
   1411     return GLFW_TRUE;
   1412 }
   1413 
   1414 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
   1415 {
   1416     struct wl_cursor* standardCursor;
   1417 
   1418     standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme,
   1419                                                 translateCursorShape(shape));
   1420     if (!standardCursor)
   1421     {
   1422         _glfwInputError(GLFW_PLATFORM_ERROR,
   1423                         "Wayland: Standard cursor \"%s\" not found",
   1424                         translateCursorShape(shape));
   1425         return GLFW_FALSE;
   1426     }
   1427 
   1428     cursor->wl.cursor = standardCursor;
   1429     cursor->wl.currentImage = 0;
   1430 
   1431     if (_glfw.wl.cursorThemeHiDPI)
   1432     {
   1433         standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI,
   1434                                                     translateCursorShape(shape));
   1435         cursor->wl.cursorHiDPI = standardCursor;
   1436     }
   1437 
   1438     return GLFW_TRUE;
   1439 }
   1440 
   1441 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
   1442 {
   1443     // If it's a standard cursor we don't need to do anything here
   1444     if (cursor->wl.cursor)
   1445         return;
   1446 
   1447     if (cursor->wl.buffer)
   1448         wl_buffer_destroy(cursor->wl.buffer);
   1449 }
   1450 
   1451 static void relativePointerHandleRelativeMotion(void* data,
   1452                                                 struct zwp_relative_pointer_v1* pointer,
   1453                                                 uint32_t timeHi,
   1454                                                 uint32_t timeLo,
   1455                                                 wl_fixed_t dx,
   1456                                                 wl_fixed_t dy,
   1457                                                 wl_fixed_t dxUnaccel,
   1458                                                 wl_fixed_t dyUnaccel)
   1459 {
   1460     _GLFWwindow* window = data;
   1461     double xpos = window->virtualCursorPosX;
   1462     double ypos = window->virtualCursorPosY;
   1463 
   1464     if (window->cursorMode != GLFW_CURSOR_DISABLED)
   1465         return;
   1466 
   1467     if (window->rawMouseMotion)
   1468     {
   1469         xpos += wl_fixed_to_double(dxUnaccel);
   1470         ypos += wl_fixed_to_double(dyUnaccel);
   1471     }
   1472     else
   1473     {
   1474         xpos += wl_fixed_to_double(dx);
   1475         ypos += wl_fixed_to_double(dy);
   1476     }
   1477 
   1478     _glfwInputCursorPos(window, xpos, ypos);
   1479 }
   1480 
   1481 static const struct zwp_relative_pointer_v1_listener relativePointerListener = {
   1482     relativePointerHandleRelativeMotion
   1483 };
   1484 
   1485 static void lockedPointerHandleLocked(void* data,
   1486                                       struct zwp_locked_pointer_v1* lockedPointer)
   1487 {
   1488 }
   1489 
   1490 static void unlockPointer(_GLFWwindow* window)
   1491 {
   1492     struct zwp_relative_pointer_v1* relativePointer =
   1493         window->wl.pointerLock.relativePointer;
   1494     struct zwp_locked_pointer_v1* lockedPointer =
   1495         window->wl.pointerLock.lockedPointer;
   1496 
   1497     zwp_relative_pointer_v1_destroy(relativePointer);
   1498     zwp_locked_pointer_v1_destroy(lockedPointer);
   1499 
   1500     window->wl.pointerLock.relativePointer = NULL;
   1501     window->wl.pointerLock.lockedPointer = NULL;
   1502 }
   1503 
   1504 static void lockPointer(_GLFWwindow* window);
   1505 
   1506 static void lockedPointerHandleUnlocked(void* data,
   1507                                         struct zwp_locked_pointer_v1* lockedPointer)
   1508 {
   1509 }
   1510 
   1511 static const struct zwp_locked_pointer_v1_listener lockedPointerListener = {
   1512     lockedPointerHandleLocked,
   1513     lockedPointerHandleUnlocked
   1514 };
   1515 
   1516 static void lockPointer(_GLFWwindow* window)
   1517 {
   1518     struct zwp_relative_pointer_v1* relativePointer;
   1519     struct zwp_locked_pointer_v1* lockedPointer;
   1520 
   1521     if (!_glfw.wl.relativePointerManager)
   1522     {
   1523         _glfwInputError(GLFW_PLATFORM_ERROR,
   1524                         "Wayland: no relative pointer manager");
   1525         return;
   1526     }
   1527 
   1528     relativePointer =
   1529         zwp_relative_pointer_manager_v1_get_relative_pointer(
   1530             _glfw.wl.relativePointerManager,
   1531             _glfw.wl.pointer);
   1532     zwp_relative_pointer_v1_add_listener(relativePointer,
   1533                                          &relativePointerListener,
   1534                                          window);
   1535 
   1536     lockedPointer =
   1537         zwp_pointer_constraints_v1_lock_pointer(
   1538             _glfw.wl.pointerConstraints,
   1539             window->wl.surface,
   1540             _glfw.wl.pointer,
   1541             NULL,
   1542             ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
   1543     zwp_locked_pointer_v1_add_listener(lockedPointer,
   1544                                        &lockedPointerListener,
   1545                                        window);
   1546 
   1547     window->wl.pointerLock.relativePointer = relativePointer;
   1548     window->wl.pointerLock.lockedPointer = lockedPointer;
   1549 
   1550     wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
   1551                           NULL, 0, 0);
   1552 }
   1553 
   1554 static GLFWbool isPointerLocked(_GLFWwindow* window)
   1555 {
   1556     return window->wl.pointerLock.lockedPointer != NULL;
   1557 }
   1558 
   1559 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
   1560 {
   1561     struct wl_cursor* defaultCursor;
   1562     struct wl_cursor* defaultCursorHiDPI = NULL;
   1563 
   1564     if (!_glfw.wl.pointer)
   1565         return;
   1566 
   1567     window->wl.currentCursor = cursor;
   1568 
   1569     // If we're not in the correct window just save the cursor
   1570     // the next time the pointer enters the window the cursor will change
   1571     if (window != _glfw.wl.pointerFocus || window->wl.decorations.focus != mainWindow)
   1572         return;
   1573 
   1574     // Unlock possible pointer lock if no longer disabled.
   1575     if (window->cursorMode != GLFW_CURSOR_DISABLED && isPointerLocked(window))
   1576         unlockPointer(window);
   1577 
   1578     if (window->cursorMode == GLFW_CURSOR_NORMAL)
   1579     {
   1580         if (cursor)
   1581             setCursorImage(window, &cursor->wl);
   1582         else
   1583         {
   1584             defaultCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme,
   1585                                                        "left_ptr");
   1586             if (!defaultCursor)
   1587             {
   1588                 _glfwInputError(GLFW_PLATFORM_ERROR,
   1589                                 "Wayland: Standard cursor not found");
   1590                 return;
   1591             }
   1592             if (_glfw.wl.cursorThemeHiDPI)
   1593                 defaultCursorHiDPI =
   1594                     wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI,
   1595                                                "left_ptr");
   1596             _GLFWcursorWayland cursorWayland = {
   1597                 defaultCursor,
   1598                 defaultCursorHiDPI,
   1599                 NULL,
   1600                 0, 0,
   1601                 0, 0,
   1602                 0
   1603             };
   1604             setCursorImage(window, &cursorWayland);
   1605         }
   1606     }
   1607     else if (window->cursorMode == GLFW_CURSOR_DISABLED)
   1608     {
   1609         if (!isPointerLocked(window))
   1610             lockPointer(window);
   1611     }
   1612     else if (window->cursorMode == GLFW_CURSOR_HIDDEN)
   1613     {
   1614         wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, NULL, 0, 0);
   1615     }
   1616 }
   1617 
   1618 static void dataSourceHandleTarget(void* data,
   1619                                    struct wl_data_source* dataSource,
   1620                                    const char* mimeType)
   1621 {
   1622     if (_glfw.wl.dataSource != dataSource)
   1623     {
   1624         _glfwInputError(GLFW_PLATFORM_ERROR,
   1625                         "Wayland: Unknown clipboard data source");
   1626         return;
   1627     }
   1628 }
   1629 
   1630 static void dataSourceHandleSend(void* data,
   1631                                  struct wl_data_source* dataSource,
   1632                                  const char* mimeType,
   1633                                  int fd)
   1634 {
   1635     const char* string = _glfw.wl.clipboardSendString;
   1636     size_t len = _glfw.wl.clipboardSendSize;
   1637     int ret;
   1638 
   1639     if (_glfw.wl.dataSource != dataSource)
   1640     {
   1641         _glfwInputError(GLFW_PLATFORM_ERROR,
   1642                         "Wayland: Unknown clipboard data source");
   1643         return;
   1644     }
   1645 
   1646     if (!string)
   1647     {
   1648         _glfwInputError(GLFW_PLATFORM_ERROR,
   1649                         "Wayland: Copy requested from an invalid string");
   1650         return;
   1651     }
   1652 
   1653     if (strcmp(mimeType, "text/plain;charset=utf-8") != 0)
   1654     {
   1655         _glfwInputError(GLFW_PLATFORM_ERROR,
   1656                         "Wayland: Wrong MIME type asked from clipboard");
   1657         close(fd);
   1658         return;
   1659     }
   1660 
   1661     while (len > 0)
   1662     {
   1663         ret = write(fd, string, len);
   1664         if (ret == -1 && errno == EINTR)
   1665             continue;
   1666         if (ret == -1)
   1667         {
   1668             // TODO: also report errno maybe.
   1669             _glfwInputError(GLFW_PLATFORM_ERROR,
   1670                             "Wayland: Error while writing the clipboard");
   1671             close(fd);
   1672             return;
   1673         }
   1674         len -= ret;
   1675     }
   1676     close(fd);
   1677 }
   1678 
   1679 static void dataSourceHandleCancelled(void* data,
   1680                                       struct wl_data_source* dataSource)
   1681 {
   1682     wl_data_source_destroy(dataSource);
   1683 
   1684     if (_glfw.wl.dataSource != dataSource)
   1685     {
   1686         _glfwInputError(GLFW_PLATFORM_ERROR,
   1687                         "Wayland: Unknown clipboard data source");
   1688         return;
   1689     }
   1690 
   1691     _glfw.wl.dataSource = NULL;
   1692 }
   1693 
   1694 static const struct wl_data_source_listener dataSourceListener = {
   1695     dataSourceHandleTarget,
   1696     dataSourceHandleSend,
   1697     dataSourceHandleCancelled,
   1698 };
   1699 
   1700 void _glfwPlatformSetClipboardString(const char* string)
   1701 {
   1702     if (_glfw.wl.dataSource)
   1703     {
   1704         wl_data_source_destroy(_glfw.wl.dataSource);
   1705         _glfw.wl.dataSource = NULL;
   1706     }
   1707 
   1708     if (_glfw.wl.clipboardSendString)
   1709     {
   1710         free(_glfw.wl.clipboardSendString);
   1711         _glfw.wl.clipboardSendString = NULL;
   1712     }
   1713 
   1714     _glfw.wl.clipboardSendString = strdup(string);
   1715     if (!_glfw.wl.clipboardSendString)
   1716     {
   1717         _glfwInputError(GLFW_PLATFORM_ERROR,
   1718                         "Wayland: Impossible to allocate clipboard string");
   1719         return;
   1720     }
   1721     _glfw.wl.clipboardSendSize = strlen(string);
   1722     _glfw.wl.dataSource =
   1723         wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager);
   1724     if (!_glfw.wl.dataSource)
   1725     {
   1726         _glfwInputError(GLFW_PLATFORM_ERROR,
   1727                         "Wayland: Impossible to create clipboard source");
   1728         free(_glfw.wl.clipboardSendString);
   1729         return;
   1730     }
   1731     wl_data_source_add_listener(_glfw.wl.dataSource,
   1732                                 &dataSourceListener,
   1733                                 NULL);
   1734     wl_data_source_offer(_glfw.wl.dataSource, "text/plain;charset=utf-8");
   1735     wl_data_device_set_selection(_glfw.wl.dataDevice,
   1736                                  _glfw.wl.dataSource,
   1737                                  _glfw.wl.serial);
   1738 }
   1739 
   1740 static GLFWbool growClipboardString(void)
   1741 {
   1742     char* clipboard = _glfw.wl.clipboardString;
   1743 
   1744     clipboard = realloc(clipboard, _glfw.wl.clipboardSize * 2);
   1745     if (!clipboard)
   1746     {
   1747         _glfwInputError(GLFW_PLATFORM_ERROR,
   1748                         "Wayland: Impossible to grow clipboard string");
   1749         return GLFW_FALSE;
   1750     }
   1751     _glfw.wl.clipboardString = clipboard;
   1752     _glfw.wl.clipboardSize = _glfw.wl.clipboardSize * 2;
   1753     return GLFW_TRUE;
   1754 }
   1755 
   1756 const char* _glfwPlatformGetClipboardString(void)
   1757 {
   1758     int fds[2];
   1759     int ret;
   1760     size_t len = 0;
   1761 
   1762     if (!_glfw.wl.dataOffer)
   1763     {
   1764         _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
   1765                         "No clipboard data has been sent yet");
   1766         return NULL;
   1767     }
   1768 
   1769     ret = pipe2(fds, O_CLOEXEC);
   1770     if (ret < 0)
   1771     {
   1772         // TODO: also report errno maybe?
   1773         _glfwInputError(GLFW_PLATFORM_ERROR,
   1774                         "Wayland: Impossible to create clipboard pipe fds");
   1775         return NULL;
   1776     }
   1777 
   1778     wl_data_offer_receive(_glfw.wl.dataOffer, "text/plain;charset=utf-8", fds[1]);
   1779     close(fds[1]);
   1780 
   1781     // XXX: this is a huge hack, this function shouldn’t be synchronous!
   1782     handleEvents(-1);
   1783 
   1784     while (1)
   1785     {
   1786         // Grow the clipboard if we need to paste something bigger, there is no
   1787         // shrink operation yet.
   1788         if (len + 4096 > _glfw.wl.clipboardSize)
   1789         {
   1790             if (!growClipboardString())
   1791             {
   1792                 close(fds[0]);
   1793                 return NULL;
   1794             }
   1795         }
   1796 
   1797         // Then read from the fd to the clipboard, handling all known errors.
   1798         ret = read(fds[0], _glfw.wl.clipboardString + len, 4096);
   1799         if (ret == 0)
   1800             break;
   1801         if (ret == -1 && errno == EINTR)
   1802             continue;
   1803         if (ret == -1)
   1804         {
   1805             // TODO: also report errno maybe.
   1806             _glfwInputError(GLFW_PLATFORM_ERROR,
   1807                             "Wayland: Impossible to read from clipboard fd");
   1808             close(fds[0]);
   1809             return NULL;
   1810         }
   1811         len += ret;
   1812     }
   1813     close(fds[0]);
   1814     if (len + 1 > _glfw.wl.clipboardSize)
   1815     {
   1816         if (!growClipboardString())
   1817             return NULL;
   1818     }
   1819     _glfw.wl.clipboardString[len] = '\0';
   1820     return _glfw.wl.clipboardString;
   1821 }
   1822 
   1823 void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
   1824 {
   1825     if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_wayland_surface)
   1826         return;
   1827 
   1828     extensions[0] = "VK_KHR_surface";
   1829     extensions[1] = "VK_KHR_wayland_surface";
   1830 }
   1831 
   1832 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
   1833                                                       VkPhysicalDevice device,
   1834                                                       uint32_t queuefamily)
   1835 {
   1836     PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
   1837         vkGetPhysicalDeviceWaylandPresentationSupportKHR =
   1838         (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)
   1839         vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR");
   1840     if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR)
   1841     {
   1842         _glfwInputError(GLFW_API_UNAVAILABLE,
   1843                         "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension");
   1844         return VK_NULL_HANDLE;
   1845     }
   1846 
   1847     return vkGetPhysicalDeviceWaylandPresentationSupportKHR(device,
   1848                                                             queuefamily,
   1849                                                             _glfw.wl.display);
   1850 }
   1851 
   1852 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
   1853                                           _GLFWwindow* window,
   1854                                           const VkAllocationCallbacks* allocator,
   1855                                           VkSurfaceKHR* surface)
   1856 {
   1857     VkResult err;
   1858     VkWaylandSurfaceCreateInfoKHR sci;
   1859     PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR;
   1860 
   1861     vkCreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR)
   1862         vkGetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR");
   1863     if (!vkCreateWaylandSurfaceKHR)
   1864     {
   1865         _glfwInputError(GLFW_API_UNAVAILABLE,
   1866                         "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension");
   1867         return VK_ERROR_EXTENSION_NOT_PRESENT;
   1868     }
   1869 
   1870     memset(&sci, 0, sizeof(sci));
   1871     sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
   1872     sci.display = _glfw.wl.display;
   1873     sci.surface = window->wl.surface;
   1874 
   1875     err = vkCreateWaylandSurfaceKHR(instance, &sci, allocator, surface);
   1876     if (err)
   1877     {
   1878         _glfwInputError(GLFW_PLATFORM_ERROR,
   1879                         "Wayland: Failed to create Vulkan surface: %s",
   1880                         _glfwGetVulkanResultString(err));
   1881     }
   1882 
   1883     return err;
   1884 }
   1885 
   1886 
   1887 //////////////////////////////////////////////////////////////////////////
   1888 //////                        GLFW native API                       //////
   1889 //////////////////////////////////////////////////////////////////////////
   1890 
   1891 GLFWAPI struct wl_display* glfwGetWaylandDisplay(void)
   1892 {
   1893     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
   1894     return _glfw.wl.display;
   1895 }
   1896 
   1897 GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle)
   1898 {
   1899     _GLFWwindow* window = (_GLFWwindow*) handle;
   1900     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
   1901     return window->wl.surface;
   1902 }
   1903