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