• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //========================================================================
2 // GLFW 3.5 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 #if defined(_GLFW_WAYLAND)
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <errno.h>
36 #include <assert.h>
37 #include <unistd.h>
38 #include <string.h>
39 #include <fcntl.h>
40 #include <sys/mman.h>
41 #include <sys/timerfd.h>
42 #include <poll.h>
43 #include <linux/input-event-codes.h>
44 
45 #include "wayland-client-protocol.h"
46 #include "xdg-shell-client-protocol.h"
47 #include "xdg-decoration-unstable-v1-client-protocol.h"
48 #include "viewporter-client-protocol.h"
49 #include "relative-pointer-unstable-v1-client-protocol.h"
50 #include "pointer-constraints-unstable-v1-client-protocol.h"
51 #include "xdg-activation-v1-client-protocol.h"
52 #include "idle-inhibit-unstable-v1-client-protocol.h"
53 #include "fractional-scale-v1-client-protocol.h"
54 
55 #define GLFW_BORDER_SIZE    4
56 #define GLFW_CAPTION_HEIGHT 24
57 
createTmpfileCloexec(char * tmpname)58 static int createTmpfileCloexec(char* tmpname)
59 {
60     int fd;
61 
62     fd = mkostemp(tmpname, O_CLOEXEC);
63     if (fd >= 0)
64         unlink(tmpname);
65 
66     return fd;
67 }
68 
69 /*
70  * Create a new, unique, anonymous file of the given size, and
71  * return the file descriptor for it. The file descriptor is set
72  * CLOEXEC. The file is immediately suitable for mmap()'ing
73  * the given size at offset zero.
74  *
75  * The file should not have a permanent backing store like a disk,
76  * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
77  *
78  * The file name is deleted from the file system.
79  *
80  * The file is suitable for buffer sharing between processes by
81  * transmitting the file descriptor over Unix sockets using the
82  * SCM_RIGHTS methods.
83  *
84  * posix_fallocate() is used to guarantee that disk space is available
85  * for the file at the given size. If disk space is insufficient, errno
86  * is set to ENOSPC. If posix_fallocate() is not supported, program may
87  * receive SIGBUS on accessing mmap()'ed file contents instead.
88  */
createAnonymousFile(off_t size)89 static int createAnonymousFile(off_t size)
90 {
91     static const char template[] = "/glfw-shared-XXXXXX";
92     const char* path;
93     char* name;
94     int fd;
95     int ret;
96 
97 #ifdef HAVE_MEMFD_CREATE
98     fd = memfd_create("glfw-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING);
99     if (fd >= 0)
100     {
101         // We can add this seal before calling posix_fallocate(), as the file
102         // is currently zero-sized anyway.
103         //
104         // There is also no need to check for the return value, we couldn’t do
105         // anything with it anyway.
106         fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
107     }
108     else
109 #elif defined(SHM_ANON)
110     fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600);
111     if (fd < 0)
112 #endif
113     {
114         path = getenv("XDG_RUNTIME_DIR");
115         if (!path)
116         {
117             errno = ENOENT;
118             return -1;
119         }
120 
121         name = _glfw_calloc(strlen(path) + sizeof(template), 1);
122         strcpy(name, path);
123         strcat(name, template);
124 
125         fd = createTmpfileCloexec(name);
126         _glfw_free(name);
127         if (fd < 0)
128             return -1;
129     }
130 
131 #if defined(SHM_ANON)
132     // posix_fallocate does not work on SHM descriptors
133     ret = ftruncate(fd, size);
134 #else
135     ret = posix_fallocate(fd, 0, size);
136 #endif
137     if (ret != 0)
138     {
139         close(fd);
140         errno = ret;
141         return -1;
142     }
143     return fd;
144 }
145 
createShmBuffer(const GLFWimage * image)146 static struct wl_buffer* createShmBuffer(const GLFWimage* image)
147 {
148     const int stride = image->width * 4;
149     const int length = image->width * image->height * 4;
150 
151     const int fd = createAnonymousFile(length);
152     if (fd < 0)
153     {
154         _glfwInputError(GLFW_PLATFORM_ERROR,
155                         "Wayland: Failed to create buffer file of size %d: %s",
156                         length, strerror(errno));
157         return NULL;
158     }
159 
160     void* data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
161     if (data == MAP_FAILED)
162     {
163         _glfwInputError(GLFW_PLATFORM_ERROR,
164                         "Wayland: Failed to map file: %s", strerror(errno));
165         close(fd);
166         return NULL;
167     }
168 
169     struct wl_shm_pool* pool = wl_shm_create_pool(_glfw.wl.shm, fd, length);
170 
171     close(fd);
172 
173     unsigned char* source = (unsigned char*) image->pixels;
174     unsigned char* target = data;
175     for (int i = 0;  i < image->width * image->height;  i++, source += 4)
176     {
177         unsigned int alpha = source[3];
178 
179         *target++ = (unsigned char) ((source[2] * alpha) / 255);
180         *target++ = (unsigned char) ((source[1] * alpha) / 255);
181         *target++ = (unsigned char) ((source[0] * alpha) / 255);
182         *target++ = (unsigned char) alpha;
183     }
184 
185     struct wl_buffer* buffer =
186         wl_shm_pool_create_buffer(pool, 0,
187                                   image->width,
188                                   image->height,
189                                   stride, WL_SHM_FORMAT_ARGB8888);
190     munmap(data, length);
191     wl_shm_pool_destroy(pool);
192 
193     return buffer;
194 }
195 
createFallbackEdge(_GLFWwindow * window,_GLFWfallbackEdgeWayland * edge,struct wl_surface * parent,struct wl_buffer * buffer,int x,int y,int width,int height)196 static void createFallbackEdge(_GLFWwindow* window,
197                                _GLFWfallbackEdgeWayland* edge,
198                                struct wl_surface* parent,
199                                struct wl_buffer* buffer,
200                                int x, int y,
201                                int width, int height)
202 {
203     edge->surface = wl_compositor_create_surface(_glfw.wl.compositor);
204     wl_surface_set_user_data(edge->surface, window);
205     wl_proxy_set_tag((struct wl_proxy*) edge->surface, &_glfw.wl.tag);
206     edge->subsurface = wl_subcompositor_get_subsurface(_glfw.wl.subcompositor,
207                                                        edge->surface, parent);
208     wl_subsurface_set_position(edge->subsurface, x, y);
209     edge->viewport = wp_viewporter_get_viewport(_glfw.wl.viewporter,
210                                                 edge->surface);
211     wp_viewport_set_destination(edge->viewport, width, height);
212     wl_surface_attach(edge->surface, buffer, 0, 0);
213 
214     struct wl_region* region = wl_compositor_create_region(_glfw.wl.compositor);
215     wl_region_add(region, 0, 0, width, height);
216     wl_surface_set_opaque_region(edge->surface, region);
217     wl_surface_commit(edge->surface);
218     wl_region_destroy(region);
219 }
220 
createFallbackDecorations(_GLFWwindow * window)221 static void createFallbackDecorations(_GLFWwindow* window)
222 {
223     unsigned char data[] = { 224, 224, 224, 255 };
224     const GLFWimage image = { 1, 1, data };
225 
226     if (!_glfw.wl.viewporter)
227         return;
228 
229     if (!window->wl.fallback.buffer)
230         window->wl.fallback.buffer = createShmBuffer(&image);
231     if (!window->wl.fallback.buffer)
232         return;
233 
234     createFallbackEdge(window, &window->wl.fallback.top, window->wl.surface,
235                        window->wl.fallback.buffer,
236                        0, -GLFW_CAPTION_HEIGHT,
237                        window->wl.width, GLFW_CAPTION_HEIGHT);
238     createFallbackEdge(window, &window->wl.fallback.left, window->wl.surface,
239                        window->wl.fallback.buffer,
240                        -GLFW_BORDER_SIZE, -GLFW_CAPTION_HEIGHT,
241                        GLFW_BORDER_SIZE, window->wl.height + GLFW_CAPTION_HEIGHT);
242     createFallbackEdge(window, &window->wl.fallback.right, window->wl.surface,
243                        window->wl.fallback.buffer,
244                        window->wl.width, -GLFW_CAPTION_HEIGHT,
245                        GLFW_BORDER_SIZE, window->wl.height + GLFW_CAPTION_HEIGHT);
246     createFallbackEdge(window, &window->wl.fallback.bottom, window->wl.surface,
247                        window->wl.fallback.buffer,
248                        -GLFW_BORDER_SIZE, window->wl.height,
249                        window->wl.width + GLFW_BORDER_SIZE * 2, GLFW_BORDER_SIZE);
250 
251     window->wl.fallback.decorations = GLFW_TRUE;
252 }
253 
destroyFallbackEdge(_GLFWfallbackEdgeWayland * edge)254 static void destroyFallbackEdge(_GLFWfallbackEdgeWayland* edge)
255 {
256     if (edge->subsurface)
257         wl_subsurface_destroy(edge->subsurface);
258     if (edge->surface)
259         wl_surface_destroy(edge->surface);
260     if (edge->viewport)
261         wp_viewport_destroy(edge->viewport);
262 
263     edge->surface = NULL;
264     edge->subsurface = NULL;
265     edge->viewport = NULL;
266 }
267 
destroyFallbackDecorations(_GLFWwindow * window)268 static void destroyFallbackDecorations(_GLFWwindow* window)
269 {
270     window->wl.fallback.decorations = GLFW_FALSE;
271 
272     destroyFallbackEdge(&window->wl.fallback.top);
273     destroyFallbackEdge(&window->wl.fallback.left);
274     destroyFallbackEdge(&window->wl.fallback.right);
275     destroyFallbackEdge(&window->wl.fallback.bottom);
276 }
277 
xdgDecorationHandleConfigure(void * userData,struct zxdg_toplevel_decoration_v1 * decoration,uint32_t mode)278 static void xdgDecorationHandleConfigure(void* userData,
279                                          struct zxdg_toplevel_decoration_v1* decoration,
280                                          uint32_t mode)
281 {
282     _GLFWwindow* window = userData;
283 
284     window->wl.xdg.decorationMode = mode;
285 
286     if (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE)
287     {
288         if (window->decorated && !window->monitor)
289             createFallbackDecorations(window);
290     }
291     else
292         destroyFallbackDecorations(window);
293 }
294 
295 static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener =
296 {
297     xdgDecorationHandleConfigure,
298 };
299 
300 // Makes the surface considered as XRGB instead of ARGB.
setContentAreaOpaque(_GLFWwindow * window)301 static void setContentAreaOpaque(_GLFWwindow* window)
302 {
303     struct wl_region* region;
304 
305     region = wl_compositor_create_region(_glfw.wl.compositor);
306     if (!region)
307         return;
308 
309     wl_region_add(region, 0, 0, window->wl.width, window->wl.height);
310     wl_surface_set_opaque_region(window->wl.surface, region);
311     wl_region_destroy(region);
312 }
313 
resizeFramebuffer(_GLFWwindow * window)314 static void resizeFramebuffer(_GLFWwindow* window)
315 {
316     if (window->wl.fractionalScale)
317     {
318         window->wl.fbWidth = (window->wl.width * window->wl.scalingNumerator) / 120;
319         window->wl.fbHeight = (window->wl.height * window->wl.scalingNumerator) / 120;
320     }
321     else
322     {
323         window->wl.fbWidth = window->wl.width * window->wl.bufferScale;
324         window->wl.fbHeight = window->wl.height * window->wl.bufferScale;
325     }
326 
327     if (window->wl.egl.window)
328     {
329         wl_egl_window_resize(window->wl.egl.window,
330                              window->wl.fbWidth,
331                              window->wl.fbHeight,
332                              0, 0);
333     }
334 
335     if (!window->wl.transparent)
336         setContentAreaOpaque(window);
337 
338     _glfwInputFramebufferSize(window, window->wl.fbWidth, window->wl.fbHeight);
339 }
340 
resizeWindow(_GLFWwindow * window,int width,int height)341 static GLFWbool resizeWindow(_GLFWwindow* window, int width, int height)
342 {
343     width = _glfw_max(width, 1);
344     height = _glfw_max(height, 1);
345 
346     if (width == window->wl.width && height == window->wl.height)
347         return GLFW_FALSE;
348 
349     window->wl.width = width;
350     window->wl.height = height;
351 
352     resizeFramebuffer(window);
353 
354     if (window->wl.scalingViewport)
355     {
356         wp_viewport_set_destination(window->wl.scalingViewport,
357                                     window->wl.width,
358                                     window->wl.height);
359     }
360 
361     if (window->wl.fallback.decorations)
362     {
363         wp_viewport_set_destination(window->wl.fallback.top.viewport,
364                                     window->wl.width,
365                                     GLFW_CAPTION_HEIGHT);
366         wl_surface_commit(window->wl.fallback.top.surface);
367 
368         wp_viewport_set_destination(window->wl.fallback.left.viewport,
369                                     GLFW_BORDER_SIZE,
370                                     window->wl.height + GLFW_CAPTION_HEIGHT);
371         wl_surface_commit(window->wl.fallback.left.surface);
372 
373         wl_subsurface_set_position(window->wl.fallback.right.subsurface,
374                                 window->wl.width, -GLFW_CAPTION_HEIGHT);
375         wp_viewport_set_destination(window->wl.fallback.right.viewport,
376                                     GLFW_BORDER_SIZE,
377                                     window->wl.height + GLFW_CAPTION_HEIGHT);
378         wl_surface_commit(window->wl.fallback.right.surface);
379 
380         wl_subsurface_set_position(window->wl.fallback.bottom.subsurface,
381                                 -GLFW_BORDER_SIZE, window->wl.height);
382         wp_viewport_set_destination(window->wl.fallback.bottom.viewport,
383                                     window->wl.width + GLFW_BORDER_SIZE * 2,
384                                     GLFW_BORDER_SIZE);
385         wl_surface_commit(window->wl.fallback.bottom.surface);
386     }
387 
388     return GLFW_TRUE;
389 }
390 
_glfwUpdateBufferScaleFromOutputsWayland(_GLFWwindow * window)391 void _glfwUpdateBufferScaleFromOutputsWayland(_GLFWwindow* window)
392 {
393     if (wl_compositor_get_version(_glfw.wl.compositor) <
394         WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION)
395     {
396         return;
397     }
398 
399     if (!window->wl.scaleFramebuffer)
400         return;
401 
402     // When using fractional scaling, the buffer scale should remain at 1
403     if (window->wl.fractionalScale)
404         return;
405 
406     // Get the scale factor from the highest scale monitor.
407     int32_t maxScale = 1;
408 
409     for (size_t i = 0; i < window->wl.outputScaleCount; i++)
410         maxScale = _glfw_max(window->wl.outputScales[i].factor, maxScale);
411 
412     // Only change the framebuffer size if the scale changed.
413     if (window->wl.bufferScale != maxScale)
414     {
415         window->wl.bufferScale = maxScale;
416         wl_surface_set_buffer_scale(window->wl.surface, maxScale);
417         _glfwInputWindowContentScale(window, maxScale, maxScale);
418         resizeFramebuffer(window);
419 
420         if (window->wl.visible)
421             _glfwInputWindowDamage(window);
422     }
423 }
424 
surfaceHandleEnter(void * userData,struct wl_surface * surface,struct wl_output * output)425 static void surfaceHandleEnter(void* userData,
426                                struct wl_surface* surface,
427                                struct wl_output* output)
428 {
429     if (wl_proxy_get_tag((struct wl_proxy*) output) != &_glfw.wl.tag)
430         return;
431 
432     _GLFWwindow* window = userData;
433     _GLFWmonitor* monitor = wl_output_get_user_data(output);
434     if (!window || !monitor)
435         return;
436 
437     if (window->wl.outputScaleCount + 1 > window->wl.outputScaleSize)
438     {
439         window->wl.outputScaleSize++;
440         window->wl.outputScales =
441             _glfw_realloc(window->wl.outputScales,
442                           window->wl.outputScaleSize * sizeof(_GLFWscaleWayland));
443     }
444 
445     window->wl.outputScaleCount++;
446     window->wl.outputScales[window->wl.outputScaleCount - 1] =
447         (_GLFWscaleWayland) { output, monitor->wl.scale };
448 
449     _glfwUpdateBufferScaleFromOutputsWayland(window);
450 }
451 
surfaceHandleLeave(void * userData,struct wl_surface * surface,struct wl_output * output)452 static void surfaceHandleLeave(void* userData,
453                                struct wl_surface* surface,
454                                struct wl_output* output)
455 {
456     if (wl_proxy_get_tag((struct wl_proxy*) output) != &_glfw.wl.tag)
457         return;
458 
459     _GLFWwindow* window = userData;
460 
461     for (size_t i = 0; i < window->wl.outputScaleCount; i++)
462     {
463         if (window->wl.outputScales[i].output == output)
464         {
465             window->wl.outputScales[i] =
466                 window->wl.outputScales[window->wl.outputScaleCount - 1];
467             window->wl.outputScaleCount--;
468             break;
469         }
470     }
471 
472     _glfwUpdateBufferScaleFromOutputsWayland(window);
473 }
474 
475 static const struct wl_surface_listener surfaceListener =
476 {
477     surfaceHandleEnter,
478     surfaceHandleLeave
479 };
480 
setIdleInhibitor(_GLFWwindow * window,GLFWbool enable)481 static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable)
482 {
483     if (enable && !window->wl.idleInhibitor && _glfw.wl.idleInhibitManager)
484     {
485         window->wl.idleInhibitor =
486             zwp_idle_inhibit_manager_v1_create_inhibitor(
487                 _glfw.wl.idleInhibitManager, window->wl.surface);
488         if (!window->wl.idleInhibitor)
489             _glfwInputError(GLFW_PLATFORM_ERROR,
490                             "Wayland: Failed to create idle inhibitor");
491     }
492     else if (!enable && window->wl.idleInhibitor)
493     {
494         zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor);
495         window->wl.idleInhibitor = NULL;
496     }
497 }
498 
499 // Make the specified window and its video mode active on its monitor
500 //
acquireMonitor(_GLFWwindow * window)501 static void acquireMonitor(_GLFWwindow* window)
502 {
503     if (window->wl.libdecor.frame)
504     {
505         libdecor_frame_set_fullscreen(window->wl.libdecor.frame,
506                                       window->monitor->wl.output);
507     }
508     else if (window->wl.xdg.toplevel)
509     {
510         xdg_toplevel_set_fullscreen(window->wl.xdg.toplevel,
511                                     window->monitor->wl.output);
512     }
513 
514     setIdleInhibitor(window, GLFW_TRUE);
515 
516     if (window->wl.fallback.decorations)
517         destroyFallbackDecorations(window);
518 }
519 
520 // Remove the window and restore the original video mode
521 //
releaseMonitor(_GLFWwindow * window)522 static void releaseMonitor(_GLFWwindow* window)
523 {
524     if (window->wl.libdecor.frame)
525         libdecor_frame_unset_fullscreen(window->wl.libdecor.frame);
526     else if (window->wl.xdg.toplevel)
527         xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
528 
529     setIdleInhibitor(window, GLFW_FALSE);
530 
531     if (!window->wl.libdecor.frame &&
532         window->wl.xdg.decorationMode != ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE)
533     {
534         if (window->decorated)
535             createFallbackDecorations(window);
536     }
537 }
538 
fractionalScaleHandlePreferredScale(void * userData,struct wp_fractional_scale_v1 * fractionalScale,uint32_t numerator)539 void fractionalScaleHandlePreferredScale(void* userData,
540                                          struct wp_fractional_scale_v1* fractionalScale,
541                                          uint32_t numerator)
542 {
543     _GLFWwindow* window = userData;
544 
545     window->wl.scalingNumerator = numerator;
546     _glfwInputWindowContentScale(window, numerator / 120.f, numerator / 120.f);
547     resizeFramebuffer(window);
548 
549     if (window->wl.visible)
550         _glfwInputWindowDamage(window);
551 }
552 
553 const struct wp_fractional_scale_v1_listener fractionalScaleListener =
554 {
555     fractionalScaleHandlePreferredScale,
556 };
557 
xdgToplevelHandleConfigure(void * userData,struct xdg_toplevel * toplevel,int32_t width,int32_t height,struct wl_array * states)558 static void xdgToplevelHandleConfigure(void* userData,
559                                        struct xdg_toplevel* toplevel,
560                                        int32_t width,
561                                        int32_t height,
562                                        struct wl_array* states)
563 {
564     _GLFWwindow* window = userData;
565     uint32_t* state;
566 
567     window->wl.pending.activated  = GLFW_FALSE;
568     window->wl.pending.maximized  = GLFW_FALSE;
569     window->wl.pending.fullscreen = GLFW_FALSE;
570 
571     wl_array_for_each(state, states)
572     {
573         switch (*state)
574         {
575             case XDG_TOPLEVEL_STATE_MAXIMIZED:
576                 window->wl.pending.maximized = GLFW_TRUE;
577                 break;
578             case XDG_TOPLEVEL_STATE_FULLSCREEN:
579                 window->wl.pending.fullscreen = GLFW_TRUE;
580                 break;
581             case XDG_TOPLEVEL_STATE_RESIZING:
582                 break;
583             case XDG_TOPLEVEL_STATE_ACTIVATED:
584                 window->wl.pending.activated = GLFW_TRUE;
585                 break;
586         }
587     }
588 
589     if (width && height)
590     {
591         if (window->wl.fallback.decorations)
592         {
593             window->wl.pending.width  = _glfw_max(0, width - GLFW_BORDER_SIZE * 2);
594             window->wl.pending.height =
595                 _glfw_max(0, height - GLFW_BORDER_SIZE - GLFW_CAPTION_HEIGHT);
596         }
597         else
598         {
599             window->wl.pending.width  = width;
600             window->wl.pending.height = height;
601         }
602     }
603     else
604     {
605         window->wl.pending.width  = window->wl.width;
606         window->wl.pending.height = window->wl.height;
607     }
608 }
609 
xdgToplevelHandleClose(void * userData,struct xdg_toplevel * toplevel)610 static void xdgToplevelHandleClose(void* userData,
611                                    struct xdg_toplevel* toplevel)
612 {
613     _GLFWwindow* window = userData;
614     _glfwInputWindowCloseRequest(window);
615 }
616 
617 static const struct xdg_toplevel_listener xdgToplevelListener =
618 {
619     xdgToplevelHandleConfigure,
620     xdgToplevelHandleClose
621 };
622 
xdgSurfaceHandleConfigure(void * userData,struct xdg_surface * surface,uint32_t serial)623 static void xdgSurfaceHandleConfigure(void* userData,
624                                       struct xdg_surface* surface,
625                                       uint32_t serial)
626 {
627     _GLFWwindow* window = userData;
628 
629     xdg_surface_ack_configure(surface, serial);
630 
631     if (window->wl.activated != window->wl.pending.activated)
632     {
633         window->wl.activated = window->wl.pending.activated;
634         if (!window->wl.activated)
635         {
636             if (window->monitor && window->autoIconify)
637                 xdg_toplevel_set_minimized(window->wl.xdg.toplevel);
638         }
639     }
640 
641     if (window->wl.maximized != window->wl.pending.maximized)
642     {
643         window->wl.maximized = window->wl.pending.maximized;
644         _glfwInputWindowMaximize(window, window->wl.maximized);
645     }
646 
647     window->wl.fullscreen = window->wl.pending.fullscreen;
648 
649     int width  = window->wl.pending.width;
650     int height = window->wl.pending.height;
651 
652     if (!window->wl.maximized && !window->wl.fullscreen)
653     {
654         if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)
655         {
656             const float aspectRatio = (float) width / (float) height;
657             const float targetRatio = (float) window->numer / (float) window->denom;
658             if (aspectRatio < targetRatio)
659                 height = width / targetRatio;
660             else if (aspectRatio > targetRatio)
661                 width = height * targetRatio;
662         }
663     }
664 
665     if (resizeWindow(window, width, height))
666     {
667         _glfwInputWindowSize(window, window->wl.width, window->wl.height);
668 
669         if (window->wl.visible)
670             _glfwInputWindowDamage(window);
671     }
672 
673     if (!window->wl.visible)
674     {
675         // Allow the window to be mapped only if it either has no XDG
676         // decorations or they have already received a configure event
677         if (!window->wl.xdg.decoration || window->wl.xdg.decorationMode)
678         {
679             window->wl.visible = GLFW_TRUE;
680             _glfwInputWindowDamage(window);
681         }
682     }
683 }
684 
685 static const struct xdg_surface_listener xdgSurfaceListener =
686 {
687     xdgSurfaceHandleConfigure
688 };
689 
libdecorFrameHandleConfigure(struct libdecor_frame * frame,struct libdecor_configuration * config,void * userData)690 void libdecorFrameHandleConfigure(struct libdecor_frame* frame,
691                                   struct libdecor_configuration* config,
692                                   void* userData)
693 {
694     _GLFWwindow* window = userData;
695     int width, height;
696 
697     enum libdecor_window_state windowState;
698     GLFWbool fullscreen, activated, maximized;
699 
700     if (libdecor_configuration_get_window_state(config, &windowState))
701     {
702         fullscreen = (windowState & LIBDECOR_WINDOW_STATE_FULLSCREEN) != 0;
703         activated = (windowState & LIBDECOR_WINDOW_STATE_ACTIVE) != 0;
704         maximized = (windowState & LIBDECOR_WINDOW_STATE_MAXIMIZED) != 0;
705     }
706     else
707     {
708         fullscreen = window->wl.fullscreen;
709         activated = window->wl.activated;
710         maximized = window->wl.maximized;
711     }
712 
713     if (!libdecor_configuration_get_content_size(config, frame, &width, &height))
714     {
715         width = window->wl.width;
716         height = window->wl.height;
717     }
718 
719     if (!maximized && !fullscreen)
720     {
721         if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)
722         {
723             const float aspectRatio = (float) width / (float) height;
724             const float targetRatio = (float) window->numer / (float) window->denom;
725             if (aspectRatio < targetRatio)
726                 height = width / targetRatio;
727             else if (aspectRatio > targetRatio)
728                 width = height * targetRatio;
729         }
730     }
731 
732     struct libdecor_state* frameState = libdecor_state_new(width, height);
733     libdecor_frame_commit(frame, frameState, config);
734     libdecor_state_free(frameState);
735 
736     if (window->wl.activated != activated)
737     {
738         window->wl.activated = activated;
739         if (!window->wl.activated)
740         {
741             if (window->monitor && window->autoIconify)
742                 libdecor_frame_set_minimized(window->wl.libdecor.frame);
743         }
744     }
745 
746     if (window->wl.maximized != maximized)
747     {
748         window->wl.maximized = maximized;
749         _glfwInputWindowMaximize(window, window->wl.maximized);
750     }
751 
752     window->wl.fullscreen = fullscreen;
753 
754     GLFWbool damaged = GLFW_FALSE;
755 
756     if (!window->wl.visible)
757     {
758         window->wl.visible = GLFW_TRUE;
759         damaged = GLFW_TRUE;
760     }
761 
762     if (resizeWindow(window, width, height))
763     {
764         _glfwInputWindowSize(window, window->wl.width, window->wl.height);
765         damaged = GLFW_TRUE;
766     }
767 
768     if (damaged)
769         _glfwInputWindowDamage(window);
770     else
771         wl_surface_commit(window->wl.surface);
772 }
773 
libdecorFrameHandleClose(struct libdecor_frame * frame,void * userData)774 void libdecorFrameHandleClose(struct libdecor_frame* frame, void* userData)
775 {
776     _GLFWwindow* window = userData;
777     _glfwInputWindowCloseRequest(window);
778 }
779 
libdecorFrameHandleCommit(struct libdecor_frame * frame,void * userData)780 void libdecorFrameHandleCommit(struct libdecor_frame* frame, void* userData)
781 {
782     _GLFWwindow* window = userData;
783     wl_surface_commit(window->wl.surface);
784 }
785 
libdecorFrameHandleDismissPopup(struct libdecor_frame * frame,const char * seatName,void * userData)786 void libdecorFrameHandleDismissPopup(struct libdecor_frame* frame,
787                                      const char* seatName,
788                                      void* userData)
789 {
790 }
791 
792 static const struct libdecor_frame_interface libdecorFrameInterface =
793 {
794     libdecorFrameHandleConfigure,
795     libdecorFrameHandleClose,
796     libdecorFrameHandleCommit,
797     libdecorFrameHandleDismissPopup
798 };
799 
createLibdecorFrame(_GLFWwindow * window)800 static GLFWbool createLibdecorFrame(_GLFWwindow* window)
801 {
802     // Allow libdecor to finish initialization of itself and its plugin
803     while (!_glfw.wl.libdecor.ready)
804         _glfwWaitEventsWayland();
805 
806     window->wl.libdecor.frame = libdecor_decorate(_glfw.wl.libdecor.context,
807                                                   window->wl.surface,
808                                                   &libdecorFrameInterface,
809                                                   window);
810     if (!window->wl.libdecor.frame)
811     {
812         _glfwInputError(GLFW_PLATFORM_ERROR,
813                         "Wayland: Failed to create libdecor frame");
814         return GLFW_FALSE;
815     }
816 
817     struct libdecor_state* frameState =
818         libdecor_state_new(window->wl.width, window->wl.height);
819     libdecor_frame_commit(window->wl.libdecor.frame, frameState, NULL);
820     libdecor_state_free(frameState);
821 
822     if (strlen(window->wl.appId))
823         libdecor_frame_set_app_id(window->wl.libdecor.frame, window->wl.appId);
824 
825     libdecor_frame_set_title(window->wl.libdecor.frame, window->title);
826 
827     if (window->minwidth != GLFW_DONT_CARE &&
828         window->minheight != GLFW_DONT_CARE)
829     {
830         libdecor_frame_set_min_content_size(window->wl.libdecor.frame,
831                                             window->minwidth,
832                                             window->minheight);
833     }
834 
835     if (window->maxwidth != GLFW_DONT_CARE &&
836         window->maxheight != GLFW_DONT_CARE)
837     {
838         libdecor_frame_set_max_content_size(window->wl.libdecor.frame,
839                                             window->maxwidth,
840                                             window->maxheight);
841     }
842 
843     if (!window->resizable)
844     {
845         libdecor_frame_unset_capabilities(window->wl.libdecor.frame,
846                                           LIBDECOR_ACTION_RESIZE);
847     }
848 
849     if (window->monitor)
850     {
851         libdecor_frame_set_fullscreen(window->wl.libdecor.frame,
852                                       window->monitor->wl.output);
853         setIdleInhibitor(window, GLFW_TRUE);
854     }
855     else
856     {
857         if (window->wl.maximized)
858             libdecor_frame_set_maximized(window->wl.libdecor.frame);
859 
860         if (!window->decorated)
861             libdecor_frame_set_visibility(window->wl.libdecor.frame, false);
862 
863         setIdleInhibitor(window, GLFW_FALSE);
864     }
865 
866     libdecor_frame_map(window->wl.libdecor.frame);
867     wl_display_roundtrip(_glfw.wl.display);
868     return GLFW_TRUE;
869 }
870 
updateXdgSizeLimits(_GLFWwindow * window)871 static void updateXdgSizeLimits(_GLFWwindow* window)
872 {
873     int minwidth, minheight, maxwidth, maxheight;
874 
875     if (window->resizable)
876     {
877         if (window->minwidth == GLFW_DONT_CARE || window->minheight == GLFW_DONT_CARE)
878             minwidth = minheight = 0;
879         else
880         {
881             minwidth  = window->minwidth;
882             minheight = window->minheight;
883 
884             if (window->wl.fallback.decorations)
885             {
886                 minwidth  += GLFW_BORDER_SIZE * 2;
887                 minheight += GLFW_CAPTION_HEIGHT + GLFW_BORDER_SIZE;
888             }
889         }
890 
891         if (window->maxwidth == GLFW_DONT_CARE || window->maxheight == GLFW_DONT_CARE)
892             maxwidth = maxheight = 0;
893         else
894         {
895             maxwidth  = window->maxwidth;
896             maxheight = window->maxheight;
897 
898             if (window->wl.fallback.decorations)
899             {
900                 maxwidth  += GLFW_BORDER_SIZE * 2;
901                 maxheight += GLFW_CAPTION_HEIGHT + GLFW_BORDER_SIZE;
902             }
903         }
904     }
905     else
906     {
907         minwidth = maxwidth = window->wl.width;
908         minheight = maxheight = window->wl.height;
909     }
910 
911     xdg_toplevel_set_min_size(window->wl.xdg.toplevel, minwidth, minheight);
912     xdg_toplevel_set_max_size(window->wl.xdg.toplevel, maxwidth, maxheight);
913 }
914 
createXdgShellObjects(_GLFWwindow * window)915 static GLFWbool createXdgShellObjects(_GLFWwindow* window)
916 {
917     window->wl.xdg.surface = xdg_wm_base_get_xdg_surface(_glfw.wl.wmBase,
918                                                          window->wl.surface);
919     if (!window->wl.xdg.surface)
920     {
921         _glfwInputError(GLFW_PLATFORM_ERROR,
922                         "Wayland: Failed to create xdg-surface for window");
923         return GLFW_FALSE;
924     }
925 
926     xdg_surface_add_listener(window->wl.xdg.surface, &xdgSurfaceListener, window);
927 
928     window->wl.xdg.toplevel = xdg_surface_get_toplevel(window->wl.xdg.surface);
929     if (!window->wl.xdg.toplevel)
930     {
931         _glfwInputError(GLFW_PLATFORM_ERROR,
932                         "Wayland: Failed to create xdg-toplevel for window");
933         return GLFW_FALSE;
934     }
935 
936     xdg_toplevel_add_listener(window->wl.xdg.toplevel, &xdgToplevelListener, window);
937 
938     if (window->wl.appId)
939         xdg_toplevel_set_app_id(window->wl.xdg.toplevel, window->wl.appId);
940 
941     xdg_toplevel_set_title(window->wl.xdg.toplevel, window->title);
942 
943     if (window->monitor)
944     {
945         xdg_toplevel_set_fullscreen(window->wl.xdg.toplevel, window->monitor->wl.output);
946         setIdleInhibitor(window, GLFW_TRUE);
947     }
948     else
949     {
950         if (window->wl.maximized)
951             xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
952 
953         setIdleInhibitor(window, GLFW_FALSE);
954     }
955 
956     if (_glfw.wl.decorationManager)
957     {
958         window->wl.xdg.decoration =
959             zxdg_decoration_manager_v1_get_toplevel_decoration(
960                 _glfw.wl.decorationManager, window->wl.xdg.toplevel);
961         zxdg_toplevel_decoration_v1_add_listener(window->wl.xdg.decoration,
962                                                  &xdgDecorationListener,
963                                                  window);
964 
965         uint32_t mode;
966 
967         if (window->decorated)
968             mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
969         else
970             mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
971 
972         zxdg_toplevel_decoration_v1_set_mode(window->wl.xdg.decoration, mode);
973     }
974     else
975     {
976         if (window->decorated && !window->monitor)
977             createFallbackDecorations(window);
978     }
979 
980     updateXdgSizeLimits(window);
981 
982     wl_surface_commit(window->wl.surface);
983     wl_display_roundtrip(_glfw.wl.display);
984     return GLFW_TRUE;
985 }
986 
createShellObjects(_GLFWwindow * window)987 static GLFWbool createShellObjects(_GLFWwindow* window)
988 {
989     if (_glfw.wl.libdecor.context)
990     {
991         if (createLibdecorFrame(window))
992             return GLFW_TRUE;
993     }
994 
995     return createXdgShellObjects(window);
996 }
997 
destroyShellObjects(_GLFWwindow * window)998 static void destroyShellObjects(_GLFWwindow* window)
999 {
1000     destroyFallbackDecorations(window);
1001 
1002     if (window->wl.libdecor.frame)
1003         libdecor_frame_unref(window->wl.libdecor.frame);
1004 
1005     if (window->wl.xdg.decoration)
1006         zxdg_toplevel_decoration_v1_destroy(window->wl.xdg.decoration);
1007 
1008     if (window->wl.xdg.toplevel)
1009         xdg_toplevel_destroy(window->wl.xdg.toplevel);
1010 
1011     if (window->wl.xdg.surface)
1012         xdg_surface_destroy(window->wl.xdg.surface);
1013 
1014     window->wl.libdecor.frame = NULL;
1015     window->wl.xdg.decoration = NULL;
1016     window->wl.xdg.decorationMode = 0;
1017     window->wl.xdg.toplevel = NULL;
1018     window->wl.xdg.surface = NULL;
1019 }
1020 
createNativeSurface(_GLFWwindow * window,const _GLFWwndconfig * wndconfig,const _GLFWfbconfig * fbconfig)1021 static GLFWbool createNativeSurface(_GLFWwindow* window,
1022                                     const _GLFWwndconfig* wndconfig,
1023                                     const _GLFWfbconfig* fbconfig)
1024 {
1025     window->wl.surface = wl_compositor_create_surface(_glfw.wl.compositor);
1026     if (!window->wl.surface)
1027     {
1028         _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to create window surface");
1029         return GLFW_FALSE;
1030     }
1031 
1032     wl_proxy_set_tag((struct wl_proxy*) window->wl.surface, &_glfw.wl.tag);
1033     wl_surface_add_listener(window->wl.surface,
1034                             &surfaceListener,
1035                             window);
1036 
1037     window->wl.width = wndconfig->width;
1038     window->wl.height = wndconfig->height;
1039     window->wl.fbWidth = wndconfig->width;
1040     window->wl.fbHeight = wndconfig->height;
1041     window->wl.appId = _glfw_strdup(wndconfig->wl.appId);
1042 
1043     window->wl.bufferScale = 1;
1044     window->wl.scalingNumerator = 120;
1045     window->wl.scaleFramebuffer = wndconfig->scaleFramebuffer;
1046 
1047     window->wl.maximized = wndconfig->maximized;
1048 
1049     window->wl.transparent = fbconfig->transparent;
1050     if (!window->wl.transparent)
1051         setContentAreaOpaque(window);
1052 
1053     if (_glfw.wl.fractionalScaleManager)
1054     {
1055         if (window->wl.scaleFramebuffer)
1056         {
1057             window->wl.scalingViewport =
1058                 wp_viewporter_get_viewport(_glfw.wl.viewporter, window->wl.surface);
1059 
1060             wp_viewport_set_destination(window->wl.scalingViewport,
1061                                         window->wl.width,
1062                                         window->wl.height);
1063 
1064             window->wl.fractionalScale =
1065                 wp_fractional_scale_manager_v1_get_fractional_scale(
1066                     _glfw.wl.fractionalScaleManager,
1067                     window->wl.surface);
1068 
1069             wp_fractional_scale_v1_add_listener(window->wl.fractionalScale,
1070                                                 &fractionalScaleListener,
1071                                                 window);
1072         }
1073     }
1074 
1075     return GLFW_TRUE;
1076 }
1077 
setCursorImage(_GLFWwindow * window,_GLFWcursorWayland * cursorWayland)1078 static void setCursorImage(_GLFWwindow* window,
1079                            _GLFWcursorWayland* cursorWayland)
1080 {
1081     struct itimerspec timer = {0};
1082     struct wl_cursor* wlCursor = cursorWayland->cursor;
1083     struct wl_cursor_image* image;
1084     struct wl_buffer* buffer;
1085     struct wl_surface* surface = _glfw.wl.cursorSurface;
1086     int scale = 1;
1087 
1088     if (!wlCursor)
1089         buffer = cursorWayland->buffer;
1090     else
1091     {
1092         if (window->wl.bufferScale > 1 && cursorWayland->cursorHiDPI)
1093         {
1094             wlCursor = cursorWayland->cursorHiDPI;
1095             scale = 2;
1096         }
1097 
1098         image = wlCursor->images[cursorWayland->currentImage];
1099         buffer = wl_cursor_image_get_buffer(image);
1100         if (!buffer)
1101             return;
1102 
1103         timer.it_value.tv_sec = image->delay / 1000;
1104         timer.it_value.tv_nsec = (image->delay % 1000) * 1000000;
1105         timerfd_settime(_glfw.wl.cursorTimerfd, 0, &timer, NULL);
1106 
1107         cursorWayland->width = image->width;
1108         cursorWayland->height = image->height;
1109         cursorWayland->xhot = image->hotspot_x;
1110         cursorWayland->yhot = image->hotspot_y;
1111     }
1112 
1113     wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial,
1114                           surface,
1115                           cursorWayland->xhot / scale,
1116                           cursorWayland->yhot / scale);
1117     wl_surface_set_buffer_scale(surface, scale);
1118     wl_surface_attach(surface, buffer, 0, 0);
1119     wl_surface_damage(surface, 0, 0,
1120                       cursorWayland->width, cursorWayland->height);
1121     wl_surface_commit(surface);
1122 }
1123 
incrementCursorImage(_GLFWwindow * window)1124 static void incrementCursorImage(_GLFWwindow* window)
1125 {
1126     _GLFWcursor* cursor;
1127 
1128     if (!window || !window->wl.hovered)
1129         return;
1130 
1131     cursor = window->wl.currentCursor;
1132     if (cursor && cursor->wl.cursor)
1133     {
1134         cursor->wl.currentImage += 1;
1135         cursor->wl.currentImage %= cursor->wl.cursor->image_count;
1136         setCursorImage(window, &cursor->wl);
1137     }
1138 }
1139 
flushDisplay(void)1140 static GLFWbool flushDisplay(void)
1141 {
1142     while (wl_display_flush(_glfw.wl.display) == -1)
1143     {
1144         if (errno != EAGAIN)
1145             return GLFW_FALSE;
1146 
1147         struct pollfd fd = { wl_display_get_fd(_glfw.wl.display), POLLOUT };
1148 
1149         while (poll(&fd, 1, -1) == -1)
1150         {
1151             if (errno != EINTR && errno != EAGAIN)
1152                 return GLFW_FALSE;
1153         }
1154     }
1155 
1156     return GLFW_TRUE;
1157 }
1158 
translateKey(uint32_t scancode)1159 static int translateKey(uint32_t scancode)
1160 {
1161     if (scancode < sizeof(_glfw.wl.keycodes) / sizeof(_glfw.wl.keycodes[0]))
1162         return _glfw.wl.keycodes[scancode];
1163 
1164     return GLFW_KEY_UNKNOWN;
1165 }
1166 
composeSymbol(xkb_keysym_t sym)1167 static xkb_keysym_t composeSymbol(xkb_keysym_t sym)
1168 {
1169     if (sym == XKB_KEY_NoSymbol || !_glfw.wl.xkb.composeState)
1170         return sym;
1171     if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym)
1172             != XKB_COMPOSE_FEED_ACCEPTED)
1173         return sym;
1174     switch (xkb_compose_state_get_status(_glfw.wl.xkb.composeState))
1175     {
1176         case XKB_COMPOSE_COMPOSED:
1177             return xkb_compose_state_get_one_sym(_glfw.wl.xkb.composeState);
1178         case XKB_COMPOSE_COMPOSING:
1179         case XKB_COMPOSE_CANCELLED:
1180             return XKB_KEY_NoSymbol;
1181         case XKB_COMPOSE_NOTHING:
1182         default:
1183             return sym;
1184     }
1185 }
1186 
inputText(_GLFWwindow * window,uint32_t scancode)1187 static void inputText(_GLFWwindow* window, uint32_t scancode)
1188 {
1189     const xkb_keysym_t* keysyms;
1190     const xkb_keycode_t keycode = scancode + 8;
1191 
1192     if (xkb_state_key_get_syms(_glfw.wl.xkb.state, keycode, &keysyms) == 1)
1193     {
1194         const xkb_keysym_t keysym = composeSymbol(keysyms[0]);
1195         const uint32_t codepoint = _glfwKeySym2Unicode(keysym);
1196         if (codepoint != GLFW_INVALID_CODEPOINT)
1197         {
1198             const int mods = _glfw.wl.xkb.modifiers;
1199             const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
1200             _glfwInputChar(window, codepoint, mods, plain);
1201         }
1202     }
1203 }
1204 
handleEvents(double * timeout)1205 static void handleEvents(double* timeout)
1206 {
1207 #if defined(GLFW_BUILD_LINUX_JOYSTICK)
1208     if (_glfw.joysticksInitialized)
1209         _glfwDetectJoystickConnectionLinux();
1210 #endif
1211 
1212     GLFWbool event = GLFW_FALSE;
1213     enum { DISPLAY_FD, KEYREPEAT_FD, CURSOR_FD, LIBDECOR_FD };
1214     struct pollfd fds[] =
1215     {
1216         [DISPLAY_FD] = { wl_display_get_fd(_glfw.wl.display), POLLIN },
1217         [KEYREPEAT_FD] = { _glfw.wl.keyRepeatTimerfd, POLLIN },
1218         [CURSOR_FD] = { _glfw.wl.cursorTimerfd, POLLIN },
1219         [LIBDECOR_FD] = { -1, POLLIN }
1220     };
1221 
1222     if (_glfw.wl.libdecor.context)
1223         fds[LIBDECOR_FD].fd = libdecor_get_fd(_glfw.wl.libdecor.context);
1224 
1225     while (!event)
1226     {
1227         while (wl_display_prepare_read(_glfw.wl.display) != 0)
1228         {
1229             if (wl_display_dispatch_pending(_glfw.wl.display) > 0)
1230                 return;
1231         }
1232 
1233         // If an error other than EAGAIN happens, we have likely been disconnected
1234         // from the Wayland session; try to handle that the best we can.
1235         if (!flushDisplay())
1236         {
1237             wl_display_cancel_read(_glfw.wl.display);
1238 
1239             _GLFWwindow* window = _glfw.windowListHead;
1240             while (window)
1241             {
1242                 _glfwInputWindowCloseRequest(window);
1243                 window = window->next;
1244             }
1245 
1246             return;
1247         }
1248 
1249         if (!_glfwPollPOSIX(fds, sizeof(fds) / sizeof(fds[0]), timeout))
1250         {
1251             wl_display_cancel_read(_glfw.wl.display);
1252             return;
1253         }
1254 
1255         if (fds[DISPLAY_FD].revents & POLLIN)
1256         {
1257             wl_display_read_events(_glfw.wl.display);
1258             if (wl_display_dispatch_pending(_glfw.wl.display) > 0)
1259                 event = GLFW_TRUE;
1260         }
1261         else
1262             wl_display_cancel_read(_glfw.wl.display);
1263 
1264         if (fds[KEYREPEAT_FD].revents & POLLIN)
1265         {
1266             uint64_t repeats;
1267 
1268             if (read(_glfw.wl.keyRepeatTimerfd, &repeats, sizeof(repeats)) == 8)
1269             {
1270                 for (uint64_t i = 0; i < repeats; i++)
1271                 {
1272                     _glfwInputKey(_glfw.wl.keyboardFocus,
1273                                   translateKey(_glfw.wl.keyRepeatScancode),
1274                                   _glfw.wl.keyRepeatScancode,
1275                                   GLFW_PRESS,
1276                                   _glfw.wl.xkb.modifiers);
1277                     inputText(_glfw.wl.keyboardFocus, _glfw.wl.keyRepeatScancode);
1278                 }
1279 
1280                 event = GLFW_TRUE;
1281             }
1282         }
1283 
1284         if (fds[CURSOR_FD].revents & POLLIN)
1285         {
1286             uint64_t repeats;
1287 
1288             if (read(_glfw.wl.cursorTimerfd, &repeats, sizeof(repeats)) == 8)
1289                 incrementCursorImage(_glfw.wl.pointerFocus);
1290         }
1291 
1292         if (fds[LIBDECOR_FD].revents & POLLIN)
1293         {
1294             if (libdecor_dispatch(_glfw.wl.libdecor.context, 0) > 0)
1295                 event = GLFW_TRUE;
1296         }
1297     }
1298 }
1299 
1300 // Reads the specified data offer as the specified MIME type
1301 //
readDataOfferAsString(struct wl_data_offer * offer,const char * mimeType)1302 static char* readDataOfferAsString(struct wl_data_offer* offer, const char* mimeType)
1303 {
1304     int fds[2];
1305 
1306     if (pipe2(fds, O_CLOEXEC) == -1)
1307     {
1308         _glfwInputError(GLFW_PLATFORM_ERROR,
1309                         "Wayland: Failed to create pipe for data offer: %s",
1310                         strerror(errno));
1311         return NULL;
1312     }
1313 
1314     wl_data_offer_receive(offer, mimeType, fds[1]);
1315     flushDisplay();
1316     close(fds[1]);
1317 
1318     char* string = NULL;
1319     size_t size = 0;
1320     size_t length = 0;
1321 
1322     for (;;)
1323     {
1324         const size_t readSize = 4096;
1325         const size_t requiredSize = length + readSize + 1;
1326         if (requiredSize > size)
1327         {
1328             char* longer = _glfw_realloc(string, requiredSize);
1329             if (!longer)
1330             {
1331                 _glfwInputError(GLFW_OUT_OF_MEMORY, NULL);
1332                 _glfw_free(string);
1333                 close(fds[0]);
1334                 return NULL;
1335             }
1336 
1337             string = longer;
1338             size = requiredSize;
1339         }
1340 
1341         const ssize_t result = read(fds[0], string + length, readSize);
1342         if (result == 0)
1343             break;
1344         else if (result == -1)
1345         {
1346             if (errno == EINTR)
1347                 continue;
1348 
1349             _glfwInputError(GLFW_PLATFORM_ERROR,
1350                             "Wayland: Failed to read from data offer pipe: %s",
1351                             strerror(errno));
1352             _glfw_free(string);
1353             close(fds[0]);
1354             return NULL;
1355         }
1356 
1357         length += result;
1358     }
1359 
1360     close(fds[0]);
1361 
1362     string[length] = '\0';
1363     return string;
1364 }
1365 
pointerHandleEnter(void * userData,struct wl_pointer * pointer,uint32_t serial,struct wl_surface * surface,wl_fixed_t sx,wl_fixed_t sy)1366 static void pointerHandleEnter(void* userData,
1367                                struct wl_pointer* pointer,
1368                                uint32_t serial,
1369                                struct wl_surface* surface,
1370                                wl_fixed_t sx,
1371                                wl_fixed_t sy)
1372 {
1373     // Happens in the case we just destroyed the surface.
1374     if (!surface)
1375         return;
1376 
1377     if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag)
1378         return;
1379 
1380     _GLFWwindow* window = wl_surface_get_user_data(surface);
1381 
1382     _glfw.wl.serial = serial;
1383     _glfw.wl.pointerEnterSerial = serial;
1384     _glfw.wl.pointerFocus = window;
1385 
1386     if (surface == window->wl.surface)
1387     {
1388         window->wl.hovered = GLFW_TRUE;
1389         _glfwSetCursorWayland(window, window->wl.currentCursor);
1390         _glfwInputCursorEnter(window, GLFW_TRUE);
1391     }
1392     else
1393     {
1394         if (window->wl.fallback.decorations)
1395             window->wl.fallback.focus = surface;
1396     }
1397 }
1398 
pointerHandleLeave(void * userData,struct wl_pointer * pointer,uint32_t serial,struct wl_surface * surface)1399 static void pointerHandleLeave(void* userData,
1400                                struct wl_pointer* pointer,
1401                                uint32_t serial,
1402                                struct wl_surface* surface)
1403 {
1404     if (!surface)
1405         return;
1406 
1407     if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag)
1408         return;
1409 
1410     _GLFWwindow* window = _glfw.wl.pointerFocus;
1411     if (!window)
1412         return;
1413 
1414     _glfw.wl.serial = serial;
1415     _glfw.wl.pointerFocus = NULL;
1416     _glfw.wl.cursorPreviousName = NULL;
1417 
1418     if (window->wl.hovered)
1419     {
1420         window->wl.hovered = GLFW_FALSE;
1421         _glfwInputCursorEnter(window, GLFW_FALSE);
1422     }
1423     else
1424     {
1425         if (window->wl.fallback.decorations)
1426             window->wl.fallback.focus = NULL;
1427     }
1428 }
1429 
pointerHandleMotion(void * userData,struct wl_pointer * pointer,uint32_t time,wl_fixed_t sx,wl_fixed_t sy)1430 static void pointerHandleMotion(void* userData,
1431                                 struct wl_pointer* pointer,
1432                                 uint32_t time,
1433                                 wl_fixed_t sx,
1434                                 wl_fixed_t sy)
1435 {
1436     _GLFWwindow* window = _glfw.wl.pointerFocus;
1437     if (!window)
1438         return;
1439 
1440     if (window->cursorMode == GLFW_CURSOR_DISABLED)
1441         return;
1442 
1443     const double xpos = wl_fixed_to_double(sx);
1444     const double ypos = wl_fixed_to_double(sy);
1445     window->wl.cursorPosX = xpos;
1446     window->wl.cursorPosY = ypos;
1447 
1448     if (window->wl.hovered)
1449     {
1450         _glfw.wl.cursorPreviousName = NULL;
1451         _glfwInputCursorPos(window, xpos, ypos);
1452         return;
1453     }
1454 
1455     if (window->wl.fallback.decorations)
1456     {
1457         const char* cursorName = "left_ptr";
1458 
1459         if (window->resizable)
1460         {
1461             if (window->wl.fallback.focus == window->wl.fallback.top.surface)
1462             {
1463                 if (ypos < GLFW_BORDER_SIZE)
1464                     cursorName = "n-resize";
1465             }
1466             else if (window->wl.fallback.focus == window->wl.fallback.left.surface)
1467             {
1468                 if (ypos < GLFW_BORDER_SIZE)
1469                     cursorName = "nw-resize";
1470                 else
1471                     cursorName = "w-resize";
1472             }
1473             else if (window->wl.fallback.focus == window->wl.fallback.right.surface)
1474             {
1475                 if (ypos < GLFW_BORDER_SIZE)
1476                     cursorName = "ne-resize";
1477                 else
1478                     cursorName = "e-resize";
1479             }
1480             else if (window->wl.fallback.focus == window->wl.fallback.bottom.surface)
1481             {
1482                 if (xpos < GLFW_BORDER_SIZE)
1483                     cursorName = "sw-resize";
1484                 else if (xpos > window->wl.width + GLFW_BORDER_SIZE)
1485                     cursorName = "se-resize";
1486                 else
1487                     cursorName = "s-resize";
1488             }
1489         }
1490 
1491         if (_glfw.wl.cursorPreviousName != cursorName)
1492         {
1493             struct wl_surface* surface = _glfw.wl.cursorSurface;
1494             struct wl_cursor_theme* theme = _glfw.wl.cursorTheme;
1495             int scale = 1;
1496 
1497             if (window->wl.bufferScale > 1 && _glfw.wl.cursorThemeHiDPI)
1498             {
1499                 // We only support up to scale=2 for now, since libwayland-cursor
1500                 // requires us to load a different theme for each size.
1501                 scale = 2;
1502                 theme = _glfw.wl.cursorThemeHiDPI;
1503             }
1504 
1505             struct wl_cursor* cursor = wl_cursor_theme_get_cursor(theme, cursorName);
1506             if (!cursor)
1507                 return;
1508 
1509             // TODO: handle animated cursors too.
1510             struct wl_cursor_image* image = cursor->images[0];
1511             if (!image)
1512                 return;
1513 
1514             struct wl_buffer* buffer = wl_cursor_image_get_buffer(image);
1515             if (!buffer)
1516                 return;
1517 
1518             wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial,
1519                                   surface,
1520                                   image->hotspot_x / scale,
1521                                   image->hotspot_y / scale);
1522             wl_surface_set_buffer_scale(surface, scale);
1523             wl_surface_attach(surface, buffer, 0, 0);
1524             wl_surface_damage(surface, 0, 0, image->width, image->height);
1525             wl_surface_commit(surface);
1526 
1527             _glfw.wl.cursorPreviousName = cursorName;
1528         }
1529     }
1530 }
1531 
pointerHandleButton(void * userData,struct wl_pointer * pointer,uint32_t serial,uint32_t time,uint32_t button,uint32_t state)1532 static void pointerHandleButton(void* userData,
1533                                 struct wl_pointer* pointer,
1534                                 uint32_t serial,
1535                                 uint32_t time,
1536                                 uint32_t button,
1537                                 uint32_t state)
1538 {
1539     _GLFWwindow* window = _glfw.wl.pointerFocus;
1540     if (!window)
1541         return;
1542 
1543     if (window->wl.hovered)
1544     {
1545         _glfw.wl.serial = serial;
1546 
1547         _glfwInputMouseClick(window,
1548                              button - BTN_LEFT,
1549                              state == WL_POINTER_BUTTON_STATE_PRESSED,
1550                              _glfw.wl.xkb.modifiers);
1551         return;
1552     }
1553 
1554     if (window->wl.fallback.decorations)
1555     {
1556         if (button == BTN_LEFT)
1557         {
1558             uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE;
1559 
1560             if (window->wl.fallback.focus == window->wl.fallback.top.surface)
1561             {
1562                 if (window->wl.cursorPosY < GLFW_BORDER_SIZE)
1563                     edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP;
1564                 else
1565                     xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial);
1566             }
1567             else if (window->wl.fallback.focus == window->wl.fallback.left.surface)
1568             {
1569                 if (window->wl.cursorPosY < GLFW_BORDER_SIZE)
1570                     edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
1571                 else
1572                     edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
1573             }
1574             else if (window->wl.fallback.focus == window->wl.fallback.right.surface)
1575             {
1576                 if (window->wl.cursorPosY < GLFW_BORDER_SIZE)
1577                     edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
1578                 else
1579                     edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
1580             }
1581             else if (window->wl.fallback.focus == window->wl.fallback.bottom.surface)
1582             {
1583                 if (window->wl.cursorPosX < GLFW_BORDER_SIZE)
1584                     edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
1585                 else if (window->wl.cursorPosX > window->wl.width + GLFW_BORDER_SIZE)
1586                     edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
1587                 else
1588                     edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
1589             }
1590 
1591             if (edges != XDG_TOPLEVEL_RESIZE_EDGE_NONE)
1592             {
1593                 xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat,
1594                                     serial, edges);
1595             }
1596         }
1597         else if (button == BTN_RIGHT)
1598         {
1599             if (window->wl.xdg.toplevel)
1600             {
1601                 xdg_toplevel_show_window_menu(window->wl.xdg.toplevel,
1602                                               _glfw.wl.seat, serial,
1603                                               window->wl.cursorPosX,
1604                                               window->wl.cursorPosY);
1605             }
1606         }
1607     }
1608 }
1609 
pointerHandleAxis(void * userData,struct wl_pointer * pointer,uint32_t time,uint32_t axis,wl_fixed_t value)1610 static void pointerHandleAxis(void* userData,
1611                               struct wl_pointer* pointer,
1612                               uint32_t time,
1613                               uint32_t axis,
1614                               wl_fixed_t value)
1615 {
1616     _GLFWwindow* window = _glfw.wl.pointerFocus;
1617     if (!window)
1618         return;
1619 
1620     // NOTE: 10 units of motion per mouse wheel step seems to be a common ratio
1621     if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
1622         _glfwInputScroll(window, -wl_fixed_to_double(value) / 10.0, 0.0);
1623     else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
1624         _glfwInputScroll(window, 0.0, -wl_fixed_to_double(value) / 10.0);
1625 }
1626 
1627 static const struct wl_pointer_listener pointerListener =
1628 {
1629     pointerHandleEnter,
1630     pointerHandleLeave,
1631     pointerHandleMotion,
1632     pointerHandleButton,
1633     pointerHandleAxis,
1634 };
1635 
keyboardHandleKeymap(void * userData,struct wl_keyboard * keyboard,uint32_t format,int fd,uint32_t size)1636 static void keyboardHandleKeymap(void* userData,
1637                                  struct wl_keyboard* keyboard,
1638                                  uint32_t format,
1639                                  int fd,
1640                                  uint32_t size)
1641 {
1642     struct xkb_keymap* keymap;
1643     struct xkb_state* state;
1644     struct xkb_compose_table* composeTable;
1645     struct xkb_compose_state* composeState;
1646 
1647     char* mapStr;
1648     const char* locale;
1649 
1650     if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
1651     {
1652         close(fd);
1653         return;
1654     }
1655 
1656     mapStr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
1657     if (mapStr == MAP_FAILED) {
1658         close(fd);
1659         return;
1660     }
1661 
1662     keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context,
1663                                         mapStr,
1664                                         XKB_KEYMAP_FORMAT_TEXT_V1,
1665                                         0);
1666     munmap(mapStr, size);
1667     close(fd);
1668 
1669     if (!keymap)
1670     {
1671         _glfwInputError(GLFW_PLATFORM_ERROR,
1672                         "Wayland: Failed to compile keymap");
1673         return;
1674     }
1675 
1676     state = xkb_state_new(keymap);
1677     if (!state)
1678     {
1679         _glfwInputError(GLFW_PLATFORM_ERROR,
1680                         "Wayland: Failed to create XKB state");
1681         xkb_keymap_unref(keymap);
1682         return;
1683     }
1684 
1685     // Look up the preferred locale, falling back to "C" as default.
1686     locale = getenv("LC_ALL");
1687     if (!locale)
1688         locale = getenv("LC_CTYPE");
1689     if (!locale)
1690         locale = getenv("LANG");
1691     if (!locale)
1692         locale = "C";
1693 
1694     composeTable =
1695         xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale,
1696                                           XKB_COMPOSE_COMPILE_NO_FLAGS);
1697     if (composeTable)
1698     {
1699         composeState =
1700             xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS);
1701         xkb_compose_table_unref(composeTable);
1702         if (composeState)
1703             _glfw.wl.xkb.composeState = composeState;
1704         else
1705             _glfwInputError(GLFW_PLATFORM_ERROR,
1706                             "Wayland: Failed to create XKB compose state");
1707     }
1708     else
1709     {
1710         _glfwInputError(GLFW_PLATFORM_ERROR,
1711                         "Wayland: Failed to create XKB compose table");
1712     }
1713 
1714     xkb_keymap_unref(_glfw.wl.xkb.keymap);
1715     xkb_state_unref(_glfw.wl.xkb.state);
1716     _glfw.wl.xkb.keymap = keymap;
1717     _glfw.wl.xkb.state = state;
1718 
1719     _glfw.wl.xkb.controlIndex  = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control");
1720     _glfw.wl.xkb.altIndex      = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1");
1721     _glfw.wl.xkb.shiftIndex    = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift");
1722     _glfw.wl.xkb.superIndex    = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4");
1723     _glfw.wl.xkb.capsLockIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Lock");
1724     _glfw.wl.xkb.numLockIndex  = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2");
1725 }
1726 
keyboardHandleEnter(void * userData,struct wl_keyboard * keyboard,uint32_t serial,struct wl_surface * surface,struct wl_array * keys)1727 static void keyboardHandleEnter(void* userData,
1728                                 struct wl_keyboard* keyboard,
1729                                 uint32_t serial,
1730                                 struct wl_surface* surface,
1731                                 struct wl_array* keys)
1732 {
1733     // Happens in the case we just destroyed the surface.
1734     if (!surface)
1735         return;
1736 
1737     if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag)
1738         return;
1739 
1740     _GLFWwindow* window = wl_surface_get_user_data(surface);
1741     if (surface != window->wl.surface)
1742         return;
1743 
1744     _glfw.wl.serial = serial;
1745     _glfw.wl.keyboardFocus = window;
1746     _glfwInputWindowFocus(window, GLFW_TRUE);
1747 }
1748 
keyboardHandleLeave(void * userData,struct wl_keyboard * keyboard,uint32_t serial,struct wl_surface * surface)1749 static void keyboardHandleLeave(void* userData,
1750                                 struct wl_keyboard* keyboard,
1751                                 uint32_t serial,
1752                                 struct wl_surface* surface)
1753 {
1754     _GLFWwindow* window = _glfw.wl.keyboardFocus;
1755 
1756     if (!window)
1757         return;
1758 
1759     struct itimerspec timer = {0};
1760     timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL);
1761 
1762     _glfw.wl.serial = serial;
1763     _glfw.wl.keyboardFocus = NULL;
1764     _glfwInputWindowFocus(window, GLFW_FALSE);
1765 }
1766 
keyboardHandleKey(void * userData,struct wl_keyboard * keyboard,uint32_t serial,uint32_t time,uint32_t scancode,uint32_t state)1767 static void keyboardHandleKey(void* userData,
1768                               struct wl_keyboard* keyboard,
1769                               uint32_t serial,
1770                               uint32_t time,
1771                               uint32_t scancode,
1772                               uint32_t state)
1773 {
1774     _GLFWwindow* window = _glfw.wl.keyboardFocus;
1775     if (!window)
1776         return;
1777 
1778     const int key = translateKey(scancode);
1779     const int action =
1780         state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE;
1781 
1782     _glfw.wl.serial = serial;
1783 
1784     struct itimerspec timer = {0};
1785 
1786     if (action == GLFW_PRESS)
1787     {
1788         const xkb_keycode_t keycode = scancode + 8;
1789 
1790         if (xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, keycode) &&
1791             _glfw.wl.keyRepeatRate > 0)
1792         {
1793             _glfw.wl.keyRepeatScancode = scancode;
1794             if (_glfw.wl.keyRepeatRate > 1)
1795                 timer.it_interval.tv_nsec = 1000000000 / _glfw.wl.keyRepeatRate;
1796             else
1797                 timer.it_interval.tv_sec = 1;
1798 
1799             timer.it_value.tv_sec = _glfw.wl.keyRepeatDelay / 1000;
1800             timer.it_value.tv_nsec = (_glfw.wl.keyRepeatDelay % 1000) * 1000000;
1801         }
1802     }
1803 
1804     timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL);
1805 
1806     _glfwInputKey(window, key, scancode, action, _glfw.wl.xkb.modifiers);
1807 
1808     if (action == GLFW_PRESS)
1809         inputText(window, scancode);
1810 }
1811 
keyboardHandleModifiers(void * userData,struct wl_keyboard * keyboard,uint32_t serial,uint32_t modsDepressed,uint32_t modsLatched,uint32_t modsLocked,uint32_t group)1812 static void keyboardHandleModifiers(void* userData,
1813                                     struct wl_keyboard* keyboard,
1814                                     uint32_t serial,
1815                                     uint32_t modsDepressed,
1816                                     uint32_t modsLatched,
1817                                     uint32_t modsLocked,
1818                                     uint32_t group)
1819 {
1820     _glfw.wl.serial = serial;
1821 
1822     if (!_glfw.wl.xkb.keymap)
1823         return;
1824 
1825     xkb_state_update_mask(_glfw.wl.xkb.state,
1826                           modsDepressed,
1827                           modsLatched,
1828                           modsLocked,
1829                           0,
1830                           0,
1831                           group);
1832 
1833     _glfw.wl.xkb.modifiers = 0;
1834 
1835     struct
1836     {
1837         xkb_mod_index_t index;
1838         unsigned int bit;
1839     } modifiers[] =
1840     {
1841         { _glfw.wl.xkb.controlIndex,  GLFW_MOD_CONTROL },
1842         { _glfw.wl.xkb.altIndex,      GLFW_MOD_ALT },
1843         { _glfw.wl.xkb.shiftIndex,    GLFW_MOD_SHIFT },
1844         { _glfw.wl.xkb.superIndex,    GLFW_MOD_SUPER },
1845         { _glfw.wl.xkb.capsLockIndex, GLFW_MOD_CAPS_LOCK },
1846         { _glfw.wl.xkb.numLockIndex,  GLFW_MOD_NUM_LOCK }
1847     };
1848 
1849     for (size_t i = 0; i < sizeof(modifiers) / sizeof(modifiers[0]); i++)
1850     {
1851         if (xkb_state_mod_index_is_active(_glfw.wl.xkb.state,
1852                                           modifiers[i].index,
1853                                           XKB_STATE_MODS_EFFECTIVE) == 1)
1854         {
1855             _glfw.wl.xkb.modifiers |= modifiers[i].bit;
1856         }
1857     }
1858 }
1859 
keyboardHandleRepeatInfo(void * userData,struct wl_keyboard * keyboard,int32_t rate,int32_t delay)1860 static void keyboardHandleRepeatInfo(void* userData,
1861                                      struct wl_keyboard* keyboard,
1862                                      int32_t rate,
1863                                      int32_t delay)
1864 {
1865     if (keyboard != _glfw.wl.keyboard)
1866         return;
1867 
1868     _glfw.wl.keyRepeatRate = rate;
1869     _glfw.wl.keyRepeatDelay = delay;
1870 }
1871 
1872 static const struct wl_keyboard_listener keyboardListener =
1873 {
1874     keyboardHandleKeymap,
1875     keyboardHandleEnter,
1876     keyboardHandleLeave,
1877     keyboardHandleKey,
1878     keyboardHandleModifiers,
1879     keyboardHandleRepeatInfo,
1880 };
1881 
seatHandleCapabilities(void * userData,struct wl_seat * seat,enum wl_seat_capability caps)1882 static void seatHandleCapabilities(void* userData,
1883                                    struct wl_seat* seat,
1884                                    enum wl_seat_capability caps)
1885 {
1886     if ((caps & WL_SEAT_CAPABILITY_POINTER) && !_glfw.wl.pointer)
1887     {
1888         _glfw.wl.pointer = wl_seat_get_pointer(seat);
1889         wl_pointer_add_listener(_glfw.wl.pointer, &pointerListener, NULL);
1890     }
1891     else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer)
1892     {
1893         wl_pointer_destroy(_glfw.wl.pointer);
1894         _glfw.wl.pointer = NULL;
1895     }
1896 
1897     if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard)
1898     {
1899         _glfw.wl.keyboard = wl_seat_get_keyboard(seat);
1900         wl_keyboard_add_listener(_glfw.wl.keyboard, &keyboardListener, NULL);
1901     }
1902     else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard)
1903     {
1904         wl_keyboard_destroy(_glfw.wl.keyboard);
1905         _glfw.wl.keyboard = NULL;
1906     }
1907 }
1908 
seatHandleName(void * userData,struct wl_seat * seat,const char * name)1909 static void seatHandleName(void* userData,
1910                            struct wl_seat* seat,
1911                            const char* name)
1912 {
1913 }
1914 
1915 static const struct wl_seat_listener seatListener =
1916 {
1917     seatHandleCapabilities,
1918     seatHandleName,
1919 };
1920 
dataOfferHandleOffer(void * userData,struct wl_data_offer * offer,const char * mimeType)1921 static void dataOfferHandleOffer(void* userData,
1922                                  struct wl_data_offer* offer,
1923                                  const char* mimeType)
1924 {
1925     for (unsigned int i = 0; i < _glfw.wl.offerCount; i++)
1926     {
1927         if (_glfw.wl.offers[i].offer == offer)
1928         {
1929             if (strcmp(mimeType, "text/plain;charset=utf-8") == 0)
1930                 _glfw.wl.offers[i].text_plain_utf8 = GLFW_TRUE;
1931             else if (strcmp(mimeType, "text/uri-list") == 0)
1932                 _glfw.wl.offers[i].text_uri_list = GLFW_TRUE;
1933 
1934             break;
1935         }
1936     }
1937 }
1938 
1939 static const struct wl_data_offer_listener dataOfferListener =
1940 {
1941     dataOfferHandleOffer
1942 };
1943 
dataDeviceHandleDataOffer(void * userData,struct wl_data_device * device,struct wl_data_offer * offer)1944 static void dataDeviceHandleDataOffer(void* userData,
1945                                       struct wl_data_device* device,
1946                                       struct wl_data_offer* offer)
1947 {
1948     _GLFWofferWayland* offers =
1949         _glfw_realloc(_glfw.wl.offers,
1950                       sizeof(_GLFWofferWayland) * (_glfw.wl.offerCount + 1));
1951     if (!offers)
1952     {
1953         _glfwInputError(GLFW_OUT_OF_MEMORY, NULL);
1954         return;
1955     }
1956 
1957     _glfw.wl.offers = offers;
1958     _glfw.wl.offerCount++;
1959 
1960     _glfw.wl.offers[_glfw.wl.offerCount - 1] = (_GLFWofferWayland) { offer };
1961     wl_data_offer_add_listener(offer, &dataOfferListener, NULL);
1962 }
1963 
dataDeviceHandleEnter(void * userData,struct wl_data_device * device,uint32_t serial,struct wl_surface * surface,wl_fixed_t x,wl_fixed_t y,struct wl_data_offer * offer)1964 static void dataDeviceHandleEnter(void* userData,
1965                                   struct wl_data_device* device,
1966                                   uint32_t serial,
1967                                   struct wl_surface* surface,
1968                                   wl_fixed_t x,
1969                                   wl_fixed_t y,
1970                                   struct wl_data_offer* offer)
1971 {
1972     if (_glfw.wl.dragOffer)
1973     {
1974         wl_data_offer_destroy(_glfw.wl.dragOffer);
1975         _glfw.wl.dragOffer = NULL;
1976         _glfw.wl.dragFocus = NULL;
1977     }
1978 
1979     unsigned int i;
1980 
1981     for (i = 0; i < _glfw.wl.offerCount; i++)
1982     {
1983         if (_glfw.wl.offers[i].offer == offer)
1984             break;
1985     }
1986 
1987     if (i == _glfw.wl.offerCount)
1988         return;
1989 
1990     if (surface && wl_proxy_get_tag((struct wl_proxy*) surface) == &_glfw.wl.tag)
1991     {
1992         _GLFWwindow* window = wl_surface_get_user_data(surface);
1993         if (window->wl.surface == surface)
1994         {
1995             if (_glfw.wl.offers[i].text_uri_list)
1996             {
1997                 _glfw.wl.dragOffer = offer;
1998                 _glfw.wl.dragFocus = window;
1999                 _glfw.wl.dragSerial = serial;
2000 
2001                 wl_data_offer_accept(offer, serial, "text/uri-list");
2002             }
2003         }
2004     }
2005 
2006     if (!_glfw.wl.dragOffer)
2007     {
2008         wl_data_offer_accept(offer, serial, NULL);
2009         wl_data_offer_destroy(offer);
2010     }
2011 
2012     _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1];
2013     _glfw.wl.offerCount--;
2014 }
2015 
dataDeviceHandleLeave(void * userData,struct wl_data_device * device)2016 static void dataDeviceHandleLeave(void* userData,
2017                                   struct wl_data_device* device)
2018 {
2019     if (_glfw.wl.dragOffer)
2020     {
2021         wl_data_offer_destroy(_glfw.wl.dragOffer);
2022         _glfw.wl.dragOffer = NULL;
2023         _glfw.wl.dragFocus = NULL;
2024     }
2025 }
2026 
dataDeviceHandleMotion(void * userData,struct wl_data_device * device,uint32_t time,wl_fixed_t x,wl_fixed_t y)2027 static void dataDeviceHandleMotion(void* userData,
2028                                    struct wl_data_device* device,
2029                                    uint32_t time,
2030                                    wl_fixed_t x,
2031                                    wl_fixed_t y)
2032 {
2033 }
2034 
dataDeviceHandleDrop(void * userData,struct wl_data_device * device)2035 static void dataDeviceHandleDrop(void* userData,
2036                                  struct wl_data_device* device)
2037 {
2038     if (!_glfw.wl.dragOffer)
2039         return;
2040 
2041     char* string = readDataOfferAsString(_glfw.wl.dragOffer, "text/uri-list");
2042     if (string)
2043     {
2044         int count;
2045         char** paths = _glfwParseUriList(string, &count);
2046         if (paths)
2047         {
2048             _glfwInputDrop(_glfw.wl.dragFocus, count, (const char**) paths);
2049 
2050             for (int i = 0; i < count; i++)
2051                 _glfw_free(paths[i]);
2052 
2053             _glfw_free(paths);
2054         }
2055 
2056         _glfw_free(string);
2057     }
2058 }
2059 
dataDeviceHandleSelection(void * userData,struct wl_data_device * device,struct wl_data_offer * offer)2060 static void dataDeviceHandleSelection(void* userData,
2061                                       struct wl_data_device* device,
2062                                       struct wl_data_offer* offer)
2063 {
2064     if (_glfw.wl.selectionOffer)
2065     {
2066         wl_data_offer_destroy(_glfw.wl.selectionOffer);
2067         _glfw.wl.selectionOffer = NULL;
2068     }
2069 
2070     for (unsigned int i = 0; i < _glfw.wl.offerCount; i++)
2071     {
2072         if (_glfw.wl.offers[i].offer == offer)
2073         {
2074             if (_glfw.wl.offers[i].text_plain_utf8)
2075                 _glfw.wl.selectionOffer = offer;
2076             else
2077                 wl_data_offer_destroy(offer);
2078 
2079             _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1];
2080             _glfw.wl.offerCount--;
2081             break;
2082         }
2083     }
2084 }
2085 
2086 const struct wl_data_device_listener dataDeviceListener =
2087 {
2088     dataDeviceHandleDataOffer,
2089     dataDeviceHandleEnter,
2090     dataDeviceHandleLeave,
2091     dataDeviceHandleMotion,
2092     dataDeviceHandleDrop,
2093     dataDeviceHandleSelection,
2094 };
2095 
xdgActivationHandleDone(void * userData,struct xdg_activation_token_v1 * activationToken,const char * token)2096 static void xdgActivationHandleDone(void* userData,
2097                                     struct xdg_activation_token_v1* activationToken,
2098                                     const char* token)
2099 {
2100     _GLFWwindow* window = userData;
2101 
2102     if (activationToken != window->wl.activationToken)
2103         return;
2104 
2105     xdg_activation_v1_activate(_glfw.wl.activationManager, token, window->wl.surface);
2106     xdg_activation_token_v1_destroy(window->wl.activationToken);
2107     window->wl.activationToken = NULL;
2108 }
2109 
2110 static const struct xdg_activation_token_v1_listener xdgActivationListener =
2111 {
2112     xdgActivationHandleDone
2113 };
2114 
_glfwAddSeatListenerWayland(struct wl_seat * seat)2115 void _glfwAddSeatListenerWayland(struct wl_seat* seat)
2116 {
2117     wl_seat_add_listener(seat, &seatListener, NULL);
2118 }
2119 
_glfwAddDataDeviceListenerWayland(struct wl_data_device * device)2120 void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device)
2121 {
2122     wl_data_device_add_listener(device, &dataDeviceListener, NULL);
2123 }
2124 
2125 
2126 //////////////////////////////////////////////////////////////////////////
2127 //////                       GLFW platform API                      //////
2128 //////////////////////////////////////////////////////////////////////////
2129 
_glfwCreateWindowWayland(_GLFWwindow * window,const _GLFWwndconfig * wndconfig,const _GLFWctxconfig * ctxconfig,const _GLFWfbconfig * fbconfig)2130 GLFWbool _glfwCreateWindowWayland(_GLFWwindow* window,
2131                                   const _GLFWwndconfig* wndconfig,
2132                                   const _GLFWctxconfig* ctxconfig,
2133                                   const _GLFWfbconfig* fbconfig)
2134 {
2135     if (!createNativeSurface(window, wndconfig, fbconfig))
2136         return GLFW_FALSE;
2137 
2138     if (ctxconfig->client != GLFW_NO_API)
2139     {
2140         if (ctxconfig->source == GLFW_EGL_CONTEXT_API ||
2141             ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
2142         {
2143             window->wl.egl.window = wl_egl_window_create(window->wl.surface,
2144                                                          window->wl.fbWidth,
2145                                                          window->wl.fbHeight);
2146             if (!window->wl.egl.window)
2147             {
2148                 _glfwInputError(GLFW_PLATFORM_ERROR,
2149                                 "Wayland: Failed to create EGL window");
2150                 return GLFW_FALSE;
2151             }
2152 
2153             if (!_glfwInitEGL())
2154                 return GLFW_FALSE;
2155             if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
2156                 return GLFW_FALSE;
2157         }
2158         else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
2159         {
2160             if (!_glfwInitOSMesa())
2161                 return GLFW_FALSE;
2162             if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
2163                 return GLFW_FALSE;
2164         }
2165 
2166         if (!_glfwRefreshContextAttribs(window, ctxconfig))
2167             return GLFW_FALSE;
2168     }
2169 
2170     if (wndconfig->mousePassthrough)
2171         _glfwSetWindowMousePassthroughWayland(window, GLFW_TRUE);
2172 
2173     if (window->monitor || wndconfig->visible)
2174     {
2175         if (!createShellObjects(window))
2176             return GLFW_FALSE;
2177     }
2178 
2179     return GLFW_TRUE;
2180 }
2181 
_glfwDestroyWindowWayland(_GLFWwindow * window)2182 void _glfwDestroyWindowWayland(_GLFWwindow* window)
2183 {
2184     if (window == _glfw.wl.pointerFocus)
2185         _glfw.wl.pointerFocus = NULL;
2186 
2187     if (window == _glfw.wl.keyboardFocus)
2188         _glfw.wl.keyboardFocus = NULL;
2189 
2190     if (window->wl.fractionalScale)
2191         wp_fractional_scale_v1_destroy(window->wl.fractionalScale);
2192 
2193     if (window->wl.scalingViewport)
2194         wp_viewport_destroy(window->wl.scalingViewport);
2195 
2196     if (window->wl.activationToken)
2197         xdg_activation_token_v1_destroy(window->wl.activationToken);
2198 
2199     if (window->wl.idleInhibitor)
2200         zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor);
2201 
2202     if (window->wl.relativePointer)
2203         zwp_relative_pointer_v1_destroy(window->wl.relativePointer);
2204 
2205     if (window->wl.lockedPointer)
2206         zwp_locked_pointer_v1_destroy(window->wl.lockedPointer);
2207 
2208     if (window->wl.confinedPointer)
2209         zwp_confined_pointer_v1_destroy(window->wl.confinedPointer);
2210 
2211     if (window->context.destroy)
2212         window->context.destroy(window);
2213 
2214     destroyShellObjects(window);
2215 
2216     if (window->wl.fallback.buffer)
2217         wl_buffer_destroy(window->wl.fallback.buffer);
2218 
2219     if (window->wl.egl.window)
2220         wl_egl_window_destroy(window->wl.egl.window);
2221 
2222     if (window->wl.surface)
2223         wl_surface_destroy(window->wl.surface);
2224 
2225     _glfw_free(window->wl.appId);
2226     _glfw_free(window->wl.outputScales);
2227 }
2228 
_glfwSetWindowTitleWayland(_GLFWwindow * window,const char * title)2229 void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title)
2230 {
2231     if (window->wl.libdecor.frame)
2232         libdecor_frame_set_title(window->wl.libdecor.frame, title);
2233     else if (window->wl.xdg.toplevel)
2234         xdg_toplevel_set_title(window->wl.xdg.toplevel, title);
2235 }
2236 
_glfwSetWindowIconWayland(_GLFWwindow * window,int count,const GLFWimage * images)2237 void _glfwSetWindowIconWayland(_GLFWwindow* window,
2238                                int count, const GLFWimage* images)
2239 {
2240     _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2241                     "Wayland: The platform does not support setting the window icon");
2242 }
2243 
_glfwGetWindowPosWayland(_GLFWwindow * window,int * xpos,int * ypos)2244 void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos)
2245 {
2246     // A Wayland client is not aware of its position, so just warn and leave it
2247     // as (0, 0)
2248 
2249     _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2250                     "Wayland: The platform does not provide the window position");
2251 }
2252 
_glfwSetWindowPosWayland(_GLFWwindow * window,int xpos,int ypos)2253 void _glfwSetWindowPosWayland(_GLFWwindow* window, int xpos, int ypos)
2254 {
2255     // A Wayland client can not set its position, so just warn
2256 
2257     _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2258                     "Wayland: The platform does not support setting the window position");
2259 }
2260 
_glfwGetWindowSizeWayland(_GLFWwindow * window,int * width,int * height)2261 void _glfwGetWindowSizeWayland(_GLFWwindow* window, int* width, int* height)
2262 {
2263     if (width)
2264         *width = window->wl.width;
2265     if (height)
2266         *height = window->wl.height;
2267 }
2268 
_glfwSetWindowSizeWayland(_GLFWwindow * window,int width,int height)2269 void _glfwSetWindowSizeWayland(_GLFWwindow* window, int width, int height)
2270 {
2271     if (window->monitor)
2272     {
2273         // Video mode setting is not available on Wayland
2274     }
2275     else
2276     {
2277         if (!resizeWindow(window, width, height))
2278             return;
2279 
2280         if (window->wl.libdecor.frame)
2281         {
2282             struct libdecor_state* frameState =
2283                 libdecor_state_new(window->wl.width, window->wl.height);
2284             libdecor_frame_commit(window->wl.libdecor.frame, frameState, NULL);
2285             libdecor_state_free(frameState);
2286         }
2287 
2288         if (window->wl.visible)
2289             _glfwInputWindowDamage(window);
2290     }
2291 }
2292 
_glfwSetWindowSizeLimitsWayland(_GLFWwindow * window,int minwidth,int minheight,int maxwidth,int maxheight)2293 void _glfwSetWindowSizeLimitsWayland(_GLFWwindow* window,
2294                                      int minwidth, int minheight,
2295                                      int maxwidth, int maxheight)
2296 {
2297     if (window->wl.libdecor.frame)
2298     {
2299         if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE)
2300             minwidth = minheight = 0;
2301 
2302         if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)
2303             maxwidth = maxheight = 0;
2304 
2305         libdecor_frame_set_min_content_size(window->wl.libdecor.frame,
2306                                             minwidth, minheight);
2307         libdecor_frame_set_max_content_size(window->wl.libdecor.frame,
2308                                             maxwidth, maxheight);
2309     }
2310     else if (window->wl.xdg.toplevel)
2311         updateXdgSizeLimits(window);
2312 }
2313 
_glfwSetWindowAspectRatioWayland(_GLFWwindow * window,int numer,int denom)2314 void _glfwSetWindowAspectRatioWayland(_GLFWwindow* window, int numer, int denom)
2315 {
2316     if (window->wl.maximized || window->wl.fullscreen)
2317         return;
2318 
2319     int width = window->wl.width, height = window->wl.height;
2320 
2321     if (numer != GLFW_DONT_CARE && denom != GLFW_DONT_CARE)
2322     {
2323         const float aspectRatio = (float) width / (float) height;
2324         const float targetRatio = (float) numer / (float) denom;
2325         if (aspectRatio < targetRatio)
2326             height /= targetRatio;
2327         else if (aspectRatio > targetRatio)
2328             width *= targetRatio;
2329     }
2330 
2331     if (resizeWindow(window, width, height))
2332     {
2333         if (window->wl.libdecor.frame)
2334         {
2335             struct libdecor_state* frameState =
2336                 libdecor_state_new(window->wl.width, window->wl.height);
2337             libdecor_frame_commit(window->wl.libdecor.frame, frameState, NULL);
2338             libdecor_state_free(frameState);
2339         }
2340 
2341         _glfwInputWindowSize(window, window->wl.width, window->wl.height);
2342 
2343         if (window->wl.visible)
2344             _glfwInputWindowDamage(window);
2345     }
2346 }
2347 
_glfwGetFramebufferSizeWayland(_GLFWwindow * window,int * width,int * height)2348 void _glfwGetFramebufferSizeWayland(_GLFWwindow* window, int* width, int* height)
2349 {
2350     if (width)
2351         *width = window->wl.fbWidth;
2352     if (height)
2353         *height = window->wl.fbHeight;
2354 }
2355 
_glfwGetWindowFrameSizeWayland(_GLFWwindow * window,int * left,int * top,int * right,int * bottom)2356 void _glfwGetWindowFrameSizeWayland(_GLFWwindow* window,
2357                                     int* left, int* top,
2358                                     int* right, int* bottom)
2359 {
2360     if (window->wl.fallback.decorations)
2361     {
2362         if (top)
2363             *top = GLFW_CAPTION_HEIGHT;
2364         if (left)
2365             *left = GLFW_BORDER_SIZE;
2366         if (right)
2367             *right = GLFW_BORDER_SIZE;
2368         if (bottom)
2369             *bottom = GLFW_BORDER_SIZE;
2370     }
2371 }
2372 
_glfwGetWindowContentScaleWayland(_GLFWwindow * window,float * xscale,float * yscale)2373 void _glfwGetWindowContentScaleWayland(_GLFWwindow* window,
2374                                        float* xscale, float* yscale)
2375 {
2376     if (window->wl.fractionalScale)
2377     {
2378         if (xscale)
2379             *xscale = (float) window->wl.scalingNumerator / 120.f;
2380         if (yscale)
2381             *yscale = (float) window->wl.scalingNumerator / 120.f;
2382     }
2383     else
2384     {
2385         if (xscale)
2386             *xscale = (float) window->wl.bufferScale;
2387         if (yscale)
2388             *yscale = (float) window->wl.bufferScale;
2389     }
2390 }
2391 
_glfwIconifyWindowWayland(_GLFWwindow * window)2392 void _glfwIconifyWindowWayland(_GLFWwindow* window)
2393 {
2394     if (window->wl.libdecor.frame)
2395         libdecor_frame_set_minimized(window->wl.libdecor.frame);
2396     else if (window->wl.xdg.toplevel)
2397         xdg_toplevel_set_minimized(window->wl.xdg.toplevel);
2398 }
2399 
_glfwRestoreWindowWayland(_GLFWwindow * window)2400 void _glfwRestoreWindowWayland(_GLFWwindow* window)
2401 {
2402     if (window->monitor)
2403     {
2404         // There is no way to unset minimized, or even to know if we are
2405         // minimized, so there is nothing to do in this case.
2406     }
2407     else
2408     {
2409         // We assume we are not minimized and act only on maximization
2410 
2411         if (window->wl.maximized)
2412         {
2413             if (window->wl.libdecor.frame)
2414                 libdecor_frame_unset_maximized(window->wl.libdecor.frame);
2415             else if (window->wl.xdg.toplevel)
2416                 xdg_toplevel_unset_maximized(window->wl.xdg.toplevel);
2417             else
2418                 window->wl.maximized = GLFW_FALSE;
2419         }
2420     }
2421 }
2422 
_glfwMaximizeWindowWayland(_GLFWwindow * window)2423 void _glfwMaximizeWindowWayland(_GLFWwindow* window)
2424 {
2425     if (window->wl.libdecor.frame)
2426         libdecor_frame_set_maximized(window->wl.libdecor.frame);
2427     else if (window->wl.xdg.toplevel)
2428         xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
2429     else
2430         window->wl.maximized = GLFW_TRUE;
2431 }
2432 
_glfwShowWindowWayland(_GLFWwindow * window)2433 void _glfwShowWindowWayland(_GLFWwindow* window)
2434 {
2435     if (!window->wl.libdecor.frame && !window->wl.xdg.toplevel)
2436     {
2437         // NOTE: The XDG surface and role are created here so command-line applications
2438         //       with off-screen windows do not appear in for example the Unity dock
2439         createShellObjects(window);
2440     }
2441 }
2442 
_glfwHideWindowWayland(_GLFWwindow * window)2443 void _glfwHideWindowWayland(_GLFWwindow* window)
2444 {
2445     if (window->wl.visible)
2446     {
2447         window->wl.visible = GLFW_FALSE;
2448         destroyShellObjects(window);
2449 
2450         wl_surface_attach(window->wl.surface, NULL, 0, 0);
2451         wl_surface_commit(window->wl.surface);
2452     }
2453 }
2454 
_glfwRequestWindowAttentionWayland(_GLFWwindow * window)2455 void _glfwRequestWindowAttentionWayland(_GLFWwindow* window)
2456 {
2457     if (!_glfw.wl.activationManager)
2458         return;
2459 
2460     // We're about to overwrite this with a new request
2461     if (window->wl.activationToken)
2462         xdg_activation_token_v1_destroy(window->wl.activationToken);
2463 
2464     window->wl.activationToken =
2465         xdg_activation_v1_get_activation_token(_glfw.wl.activationManager);
2466     xdg_activation_token_v1_add_listener(window->wl.activationToken,
2467                                          &xdgActivationListener,
2468                                          window);
2469 
2470     xdg_activation_token_v1_commit(window->wl.activationToken);
2471 }
2472 
_glfwFocusWindowWayland(_GLFWwindow * window)2473 void _glfwFocusWindowWayland(_GLFWwindow* window)
2474 {
2475     if (!_glfw.wl.activationManager)
2476         return;
2477 
2478     if (window->wl.activationToken)
2479         xdg_activation_token_v1_destroy(window->wl.activationToken);
2480 
2481     window->wl.activationToken =
2482         xdg_activation_v1_get_activation_token(_glfw.wl.activationManager);
2483     xdg_activation_token_v1_add_listener(window->wl.activationToken,
2484                                          &xdgActivationListener,
2485                                          window);
2486 
2487     xdg_activation_token_v1_set_serial(window->wl.activationToken,
2488                                        _glfw.wl.serial,
2489                                        _glfw.wl.seat);
2490 
2491     _GLFWwindow* requester = _glfw.wl.keyboardFocus;
2492     if (requester)
2493     {
2494         xdg_activation_token_v1_set_surface(window->wl.activationToken,
2495                                             requester->wl.surface);
2496 
2497         if (requester->wl.appId)
2498         {
2499             xdg_activation_token_v1_set_app_id(window->wl.activationToken,
2500                                                requester->wl.appId);
2501         }
2502     }
2503 
2504     xdg_activation_token_v1_commit(window->wl.activationToken);
2505 }
2506 
_glfwSetWindowMonitorWayland(_GLFWwindow * window,_GLFWmonitor * monitor,int xpos,int ypos,int width,int height,int refreshRate)2507 void _glfwSetWindowMonitorWayland(_GLFWwindow* window,
2508                                   _GLFWmonitor* monitor,
2509                                   int xpos, int ypos,
2510                                   int width, int height,
2511                                   int refreshRate)
2512 {
2513     if (window->monitor == monitor)
2514     {
2515         if (!monitor)
2516             _glfwSetWindowSizeWayland(window, width, height);
2517 
2518         return;
2519     }
2520 
2521     if (window->monitor)
2522         releaseMonitor(window);
2523 
2524     _glfwInputWindowMonitor(window, monitor);
2525 
2526     if (window->monitor)
2527         acquireMonitor(window);
2528     else
2529         _glfwSetWindowSizeWayland(window, width, height);
2530 }
2531 
_glfwWindowFocusedWayland(_GLFWwindow * window)2532 GLFWbool _glfwWindowFocusedWayland(_GLFWwindow* window)
2533 {
2534     return _glfw.wl.keyboardFocus == window;
2535 }
2536 
_glfwWindowIconifiedWayland(_GLFWwindow * window)2537 GLFWbool _glfwWindowIconifiedWayland(_GLFWwindow* window)
2538 {
2539     // xdg-shell doesn’t give any way to request whether a surface is
2540     // iconified.
2541     return GLFW_FALSE;
2542 }
2543 
_glfwWindowVisibleWayland(_GLFWwindow * window)2544 GLFWbool _glfwWindowVisibleWayland(_GLFWwindow* window)
2545 {
2546     return window->wl.visible;
2547 }
2548 
_glfwWindowMaximizedWayland(_GLFWwindow * window)2549 GLFWbool _glfwWindowMaximizedWayland(_GLFWwindow* window)
2550 {
2551     return window->wl.maximized;
2552 }
2553 
_glfwWindowHoveredWayland(_GLFWwindow * window)2554 GLFWbool _glfwWindowHoveredWayland(_GLFWwindow* window)
2555 {
2556     return window->wl.hovered;
2557 }
2558 
_glfwFramebufferTransparentWayland(_GLFWwindow * window)2559 GLFWbool _glfwFramebufferTransparentWayland(_GLFWwindow* window)
2560 {
2561     return window->wl.transparent;
2562 }
2563 
_glfwSetWindowResizableWayland(_GLFWwindow * window,GLFWbool enabled)2564 void _glfwSetWindowResizableWayland(_GLFWwindow* window, GLFWbool enabled)
2565 {
2566     if (window->wl.libdecor.frame)
2567     {
2568         if (enabled)
2569         {
2570             libdecor_frame_set_capabilities(window->wl.libdecor.frame,
2571                                             LIBDECOR_ACTION_RESIZE);
2572         }
2573         else
2574         {
2575             libdecor_frame_unset_capabilities(window->wl.libdecor.frame,
2576                                               LIBDECOR_ACTION_RESIZE);
2577         }
2578     }
2579     else if (window->wl.xdg.toplevel)
2580         updateXdgSizeLimits(window);
2581 }
2582 
_glfwSetWindowDecoratedWayland(_GLFWwindow * window,GLFWbool enabled)2583 void _glfwSetWindowDecoratedWayland(_GLFWwindow* window, GLFWbool enabled)
2584 {
2585     if (window->wl.libdecor.frame)
2586     {
2587         libdecor_frame_set_visibility(window->wl.libdecor.frame, enabled);
2588     }
2589     else if (window->wl.xdg.decoration)
2590     {
2591         uint32_t mode;
2592 
2593         if (enabled)
2594             mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
2595         else
2596             mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
2597 
2598         zxdg_toplevel_decoration_v1_set_mode(window->wl.xdg.decoration, mode);
2599     }
2600     else if (window->wl.xdg.toplevel)
2601     {
2602         if (enabled)
2603             createFallbackDecorations(window);
2604         else
2605             destroyFallbackDecorations(window);
2606     }
2607 }
2608 
_glfwSetWindowFloatingWayland(_GLFWwindow * window,GLFWbool enabled)2609 void _glfwSetWindowFloatingWayland(_GLFWwindow* window, GLFWbool enabled)
2610 {
2611     _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2612                     "Wayland: Platform does not support making a window floating");
2613 }
2614 
_glfwSetWindowMousePassthroughWayland(_GLFWwindow * window,GLFWbool enabled)2615 void _glfwSetWindowMousePassthroughWayland(_GLFWwindow* window, GLFWbool enabled)
2616 {
2617     if (enabled)
2618     {
2619         struct wl_region* region = wl_compositor_create_region(_glfw.wl.compositor);
2620         wl_surface_set_input_region(window->wl.surface, region);
2621         wl_region_destroy(region);
2622     }
2623     else
2624         wl_surface_set_input_region(window->wl.surface, NULL);
2625 }
2626 
_glfwGetWindowOpacityWayland(_GLFWwindow * window)2627 float _glfwGetWindowOpacityWayland(_GLFWwindow* window)
2628 {
2629     return 1.f;
2630 }
2631 
_glfwSetWindowOpacityWayland(_GLFWwindow * window,float opacity)2632 void _glfwSetWindowOpacityWayland(_GLFWwindow* window, float opacity)
2633 {
2634     _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2635                     "Wayland: The platform does not support setting the window opacity");
2636 }
2637 
_glfwSetRawMouseMotionWayland(_GLFWwindow * window,GLFWbool enabled)2638 void _glfwSetRawMouseMotionWayland(_GLFWwindow* window, GLFWbool enabled)
2639 {
2640     // This is handled in relativePointerHandleRelativeMotion
2641 }
2642 
_glfwRawMouseMotionSupportedWayland(void)2643 GLFWbool _glfwRawMouseMotionSupportedWayland(void)
2644 {
2645     return GLFW_TRUE;
2646 }
2647 
_glfwPollEventsWayland(void)2648 void _glfwPollEventsWayland(void)
2649 {
2650     double timeout = 0.0;
2651     handleEvents(&timeout);
2652 }
2653 
_glfwWaitEventsWayland(void)2654 void _glfwWaitEventsWayland(void)
2655 {
2656     handleEvents(NULL);
2657 }
2658 
_glfwWaitEventsTimeoutWayland(double timeout)2659 void _glfwWaitEventsTimeoutWayland(double timeout)
2660 {
2661     handleEvents(&timeout);
2662 }
2663 
_glfwPostEmptyEventWayland(void)2664 void _glfwPostEmptyEventWayland(void)
2665 {
2666     wl_display_sync(_glfw.wl.display);
2667     flushDisplay();
2668 }
2669 
_glfwGetCursorPosWayland(_GLFWwindow * window,double * xpos,double * ypos)2670 void _glfwGetCursorPosWayland(_GLFWwindow* window, double* xpos, double* ypos)
2671 {
2672     if (xpos)
2673         *xpos = window->wl.cursorPosX;
2674     if (ypos)
2675         *ypos = window->wl.cursorPosY;
2676 }
2677 
_glfwSetCursorPosWayland(_GLFWwindow * window,double x,double y)2678 void _glfwSetCursorPosWayland(_GLFWwindow* window, double x, double y)
2679 {
2680     _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2681                     "Wayland: The platform does not support setting the cursor position");
2682 }
2683 
_glfwSetCursorModeWayland(_GLFWwindow * window,int mode)2684 void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode)
2685 {
2686     _glfwSetCursorWayland(window, window->wl.currentCursor);
2687 }
2688 
_glfwGetScancodeNameWayland(int scancode)2689 const char* _glfwGetScancodeNameWayland(int scancode)
2690 {
2691     if (scancode < 0 || scancode > 255)
2692     {
2693         _glfwInputError(GLFW_INVALID_VALUE,
2694                         "Wayland: Invalid scancode %i",
2695                         scancode);
2696         return NULL;
2697     }
2698 
2699     const int key = _glfw.wl.keycodes[scancode];
2700     if (key == GLFW_KEY_UNKNOWN)
2701         return NULL;
2702 
2703     const xkb_keycode_t keycode = scancode + 8;
2704     const xkb_layout_index_t layout =
2705         xkb_state_key_get_layout(_glfw.wl.xkb.state, keycode);
2706     if (layout == XKB_LAYOUT_INVALID)
2707     {
2708         _glfwInputError(GLFW_PLATFORM_ERROR,
2709                         "Wayland: Failed to retrieve layout for key name");
2710         return NULL;
2711     }
2712 
2713     const xkb_keysym_t* keysyms = NULL;
2714     xkb_keymap_key_get_syms_by_level(_glfw.wl.xkb.keymap,
2715                                      keycode,
2716                                      layout,
2717                                      0,
2718                                      &keysyms);
2719     if (keysyms == NULL)
2720     {
2721         _glfwInputError(GLFW_PLATFORM_ERROR,
2722                         "Wayland: Failed to retrieve keysym for key name");
2723         return NULL;
2724     }
2725 
2726     const uint32_t codepoint = _glfwKeySym2Unicode(keysyms[0]);
2727     if (codepoint == GLFW_INVALID_CODEPOINT)
2728     {
2729         _glfwInputError(GLFW_PLATFORM_ERROR,
2730                         "Wayland: Failed to retrieve codepoint for key name");
2731         return NULL;
2732     }
2733 
2734     const size_t count = _glfwEncodeUTF8(_glfw.wl.keynames[key],  codepoint);
2735     if (count == 0)
2736     {
2737         _glfwInputError(GLFW_PLATFORM_ERROR,
2738                         "Wayland: Failed to encode codepoint for key name");
2739         return NULL;
2740     }
2741 
2742     _glfw.wl.keynames[key][count] = '\0';
2743     return _glfw.wl.keynames[key];
2744 }
2745 
_glfwGetKeyScancodeWayland(int key)2746 int _glfwGetKeyScancodeWayland(int key)
2747 {
2748     return _glfw.wl.scancodes[key];
2749 }
2750 
_glfwCreateCursorWayland(_GLFWcursor * cursor,const GLFWimage * image,int xhot,int yhot)2751 GLFWbool _glfwCreateCursorWayland(_GLFWcursor* cursor,
2752                                   const GLFWimage* image,
2753                                   int xhot, int yhot)
2754 {
2755     cursor->wl.buffer = createShmBuffer(image);
2756     if (!cursor->wl.buffer)
2757         return GLFW_FALSE;
2758 
2759     cursor->wl.width = image->width;
2760     cursor->wl.height = image->height;
2761     cursor->wl.xhot = xhot;
2762     cursor->wl.yhot = yhot;
2763     return GLFW_TRUE;
2764 }
2765 
_glfwCreateStandardCursorWayland(_GLFWcursor * cursor,int shape)2766 GLFWbool _glfwCreateStandardCursorWayland(_GLFWcursor* cursor, int shape)
2767 {
2768     const char* name = NULL;
2769 
2770     // Try the XDG names first
2771     switch (shape)
2772     {
2773         case GLFW_ARROW_CURSOR:
2774             name = "default";
2775             break;
2776         case GLFW_IBEAM_CURSOR:
2777             name = "text";
2778             break;
2779         case GLFW_CROSSHAIR_CURSOR:
2780             name = "crosshair";
2781             break;
2782         case GLFW_POINTING_HAND_CURSOR:
2783             name = "pointer";
2784             break;
2785         case GLFW_RESIZE_EW_CURSOR:
2786             name = "ew-resize";
2787             break;
2788         case GLFW_RESIZE_NS_CURSOR:
2789             name = "ns-resize";
2790             break;
2791         case GLFW_RESIZE_NWSE_CURSOR:
2792             name = "nwse-resize";
2793             break;
2794         case GLFW_RESIZE_NESW_CURSOR:
2795             name = "nesw-resize";
2796             break;
2797         case GLFW_RESIZE_ALL_CURSOR:
2798             name = "all-scroll";
2799             break;
2800         case GLFW_NOT_ALLOWED_CURSOR:
2801             name = "not-allowed";
2802             break;
2803     }
2804 
2805     cursor->wl.cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name);
2806 
2807     if (_glfw.wl.cursorThemeHiDPI)
2808     {
2809         cursor->wl.cursorHiDPI =
2810             wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, name);
2811     }
2812 
2813     if (!cursor->wl.cursor)
2814     {
2815         // Fall back to the core X11 names
2816         switch (shape)
2817         {
2818             case GLFW_ARROW_CURSOR:
2819                 name = "left_ptr";
2820                 break;
2821             case GLFW_IBEAM_CURSOR:
2822                 name = "xterm";
2823                 break;
2824             case GLFW_CROSSHAIR_CURSOR:
2825                 name = "crosshair";
2826                 break;
2827             case GLFW_POINTING_HAND_CURSOR:
2828                 name = "hand2";
2829                 break;
2830             case GLFW_RESIZE_EW_CURSOR:
2831                 name = "sb_h_double_arrow";
2832                 break;
2833             case GLFW_RESIZE_NS_CURSOR:
2834                 name = "sb_v_double_arrow";
2835                 break;
2836             case GLFW_RESIZE_ALL_CURSOR:
2837                 name = "fleur";
2838                 break;
2839             default:
2840                 _glfwInputError(GLFW_CURSOR_UNAVAILABLE,
2841                                 "Wayland: Standard cursor shape unavailable");
2842                 return GLFW_FALSE;
2843         }
2844 
2845         cursor->wl.cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name);
2846         if (!cursor->wl.cursor)
2847         {
2848             _glfwInputError(GLFW_CURSOR_UNAVAILABLE,
2849                             "Wayland: Failed to create standard cursor \"%s\"",
2850                             name);
2851             return GLFW_FALSE;
2852         }
2853 
2854         if (_glfw.wl.cursorThemeHiDPI)
2855         {
2856             if (!cursor->wl.cursorHiDPI)
2857             {
2858                 cursor->wl.cursorHiDPI =
2859                     wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, name);
2860             }
2861         }
2862     }
2863 
2864     return GLFW_TRUE;
2865 }
2866 
_glfwDestroyCursorWayland(_GLFWcursor * cursor)2867 void _glfwDestroyCursorWayland(_GLFWcursor* cursor)
2868 {
2869     // If it's a standard cursor we don't need to do anything here
2870     if (cursor->wl.cursor)
2871         return;
2872 
2873     if (cursor->wl.buffer)
2874         wl_buffer_destroy(cursor->wl.buffer);
2875 }
2876 
relativePointerHandleRelativeMotion(void * userData,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)2877 static void relativePointerHandleRelativeMotion(void* userData,
2878                                                 struct zwp_relative_pointer_v1* pointer,
2879                                                 uint32_t timeHi,
2880                                                 uint32_t timeLo,
2881                                                 wl_fixed_t dx,
2882                                                 wl_fixed_t dy,
2883                                                 wl_fixed_t dxUnaccel,
2884                                                 wl_fixed_t dyUnaccel)
2885 {
2886     _GLFWwindow* window = userData;
2887     double xpos = window->virtualCursorPosX;
2888     double ypos = window->virtualCursorPosY;
2889 
2890     if (window->cursorMode != GLFW_CURSOR_DISABLED)
2891         return;
2892 
2893     if (window->rawMouseMotion)
2894     {
2895         xpos += wl_fixed_to_double(dxUnaccel);
2896         ypos += wl_fixed_to_double(dyUnaccel);
2897     }
2898     else
2899     {
2900         xpos += wl_fixed_to_double(dx);
2901         ypos += wl_fixed_to_double(dy);
2902     }
2903 
2904     _glfwInputCursorPos(window, xpos, ypos);
2905 }
2906 
2907 static const struct zwp_relative_pointer_v1_listener relativePointerListener =
2908 {
2909     relativePointerHandleRelativeMotion
2910 };
2911 
lockedPointerHandleLocked(void * userData,struct zwp_locked_pointer_v1 * lockedPointer)2912 static void lockedPointerHandleLocked(void* userData,
2913                                       struct zwp_locked_pointer_v1* lockedPointer)
2914 {
2915 }
2916 
lockedPointerHandleUnlocked(void * userData,struct zwp_locked_pointer_v1 * lockedPointer)2917 static void lockedPointerHandleUnlocked(void* userData,
2918                                         struct zwp_locked_pointer_v1* lockedPointer)
2919 {
2920 }
2921 
2922 static const struct zwp_locked_pointer_v1_listener lockedPointerListener =
2923 {
2924     lockedPointerHandleLocked,
2925     lockedPointerHandleUnlocked
2926 };
2927 
lockPointer(_GLFWwindow * window)2928 static void lockPointer(_GLFWwindow* window)
2929 {
2930     if (!_glfw.wl.relativePointerManager)
2931     {
2932         _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2933                         "Wayland: The compositor does not support pointer locking");
2934         return;
2935     }
2936 
2937     window->wl.relativePointer =
2938         zwp_relative_pointer_manager_v1_get_relative_pointer(
2939             _glfw.wl.relativePointerManager,
2940             _glfw.wl.pointer);
2941     zwp_relative_pointer_v1_add_listener(window->wl.relativePointer,
2942                                          &relativePointerListener,
2943                                          window);
2944 
2945     window->wl.lockedPointer =
2946         zwp_pointer_constraints_v1_lock_pointer(
2947             _glfw.wl.pointerConstraints,
2948             window->wl.surface,
2949             _glfw.wl.pointer,
2950             NULL,
2951             ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
2952     zwp_locked_pointer_v1_add_listener(window->wl.lockedPointer,
2953                                        &lockedPointerListener,
2954                                        window);
2955 }
2956 
unlockPointer(_GLFWwindow * window)2957 static void unlockPointer(_GLFWwindow* window)
2958 {
2959     zwp_relative_pointer_v1_destroy(window->wl.relativePointer);
2960     window->wl.relativePointer = NULL;
2961 
2962     zwp_locked_pointer_v1_destroy(window->wl.lockedPointer);
2963     window->wl.lockedPointer = NULL;
2964 }
2965 
confinedPointerHandleConfined(void * userData,struct zwp_confined_pointer_v1 * confinedPointer)2966 static void confinedPointerHandleConfined(void* userData,
2967                                           struct zwp_confined_pointer_v1* confinedPointer)
2968 {
2969 }
2970 
confinedPointerHandleUnconfined(void * userData,struct zwp_confined_pointer_v1 * confinedPointer)2971 static void confinedPointerHandleUnconfined(void* userData,
2972                                             struct zwp_confined_pointer_v1* confinedPointer)
2973 {
2974 }
2975 
2976 static const struct zwp_confined_pointer_v1_listener confinedPointerListener =
2977 {
2978     confinedPointerHandleConfined,
2979     confinedPointerHandleUnconfined
2980 };
2981 
confinePointer(_GLFWwindow * window)2982 static void confinePointer(_GLFWwindow* window)
2983 {
2984     window->wl.confinedPointer =
2985         zwp_pointer_constraints_v1_confine_pointer(
2986             _glfw.wl.pointerConstraints,
2987             window->wl.surface,
2988             _glfw.wl.pointer,
2989             NULL,
2990             ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
2991 
2992     zwp_confined_pointer_v1_add_listener(window->wl.confinedPointer,
2993                                          &confinedPointerListener,
2994                                          window);
2995 }
2996 
unconfinePointer(_GLFWwindow * window)2997 static void unconfinePointer(_GLFWwindow* window)
2998 {
2999     zwp_confined_pointer_v1_destroy(window->wl.confinedPointer);
3000     window->wl.confinedPointer = NULL;
3001 }
3002 
_glfwSetCursorWayland(_GLFWwindow * window,_GLFWcursor * cursor)3003 void _glfwSetCursorWayland(_GLFWwindow* window, _GLFWcursor* cursor)
3004 {
3005     if (!_glfw.wl.pointer)
3006         return;
3007 
3008     window->wl.currentCursor = cursor;
3009 
3010     // If we're not in the correct window just save the cursor
3011     // the next time the pointer enters the window the cursor will change
3012     if (!window->wl.hovered)
3013         return;
3014 
3015     // Update pointer lock to match cursor mode
3016     if (window->cursorMode == GLFW_CURSOR_DISABLED)
3017     {
3018         if (window->wl.confinedPointer)
3019             unconfinePointer(window);
3020         if (!window->wl.lockedPointer)
3021             lockPointer(window);
3022     }
3023     else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
3024     {
3025         if (window->wl.lockedPointer)
3026             unlockPointer(window);
3027         if (!window->wl.confinedPointer)
3028             confinePointer(window);
3029     }
3030     else if (window->cursorMode == GLFW_CURSOR_NORMAL ||
3031              window->cursorMode == GLFW_CURSOR_HIDDEN)
3032     {
3033         if (window->wl.lockedPointer)
3034             unlockPointer(window);
3035         else if (window->wl.confinedPointer)
3036             unconfinePointer(window);
3037     }
3038 
3039     if (window->cursorMode == GLFW_CURSOR_NORMAL ||
3040         window->cursorMode == GLFW_CURSOR_CAPTURED)
3041     {
3042         if (cursor)
3043             setCursorImage(window, &cursor->wl);
3044         else
3045         {
3046             struct wl_cursor* defaultCursor =
3047                 wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, "left_ptr");
3048             if (!defaultCursor)
3049             {
3050                 _glfwInputError(GLFW_PLATFORM_ERROR,
3051                                 "Wayland: Standard cursor not found");
3052                 return;
3053             }
3054 
3055             struct wl_cursor* defaultCursorHiDPI = NULL;
3056             if (_glfw.wl.cursorThemeHiDPI)
3057             {
3058                 defaultCursorHiDPI =
3059                     wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, "left_ptr");
3060             }
3061 
3062             _GLFWcursorWayland cursorWayland =
3063             {
3064                 defaultCursor,
3065                 defaultCursorHiDPI,
3066                 NULL,
3067                 0, 0,
3068                 0, 0,
3069                 0
3070             };
3071 
3072             setCursorImage(window, &cursorWayland);
3073         }
3074     }
3075     else if (window->cursorMode == GLFW_CURSOR_HIDDEN ||
3076              window->cursorMode == GLFW_CURSOR_DISABLED)
3077     {
3078         wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial, NULL, 0, 0);
3079     }
3080 }
3081 
dataSourceHandleTarget(void * userData,struct wl_data_source * source,const char * mimeType)3082 static void dataSourceHandleTarget(void* userData,
3083                                    struct wl_data_source* source,
3084                                    const char* mimeType)
3085 {
3086     if (_glfw.wl.selectionSource != source)
3087     {
3088         _glfwInputError(GLFW_PLATFORM_ERROR,
3089                         "Wayland: Unknown clipboard data source");
3090         return;
3091     }
3092 }
3093 
dataSourceHandleSend(void * userData,struct wl_data_source * source,const char * mimeType,int fd)3094 static void dataSourceHandleSend(void* userData,
3095                                  struct wl_data_source* source,
3096                                  const char* mimeType,
3097                                  int fd)
3098 {
3099     // Ignore it if this is an outdated or invalid request
3100     if (_glfw.wl.selectionSource != source ||
3101         strcmp(mimeType, "text/plain;charset=utf-8") != 0)
3102     {
3103         close(fd);
3104         return;
3105     }
3106 
3107     char* string = _glfw.wl.clipboardString;
3108     size_t length = strlen(string);
3109 
3110     while (length > 0)
3111     {
3112         const ssize_t result = write(fd, string, length);
3113         if (result == -1)
3114         {
3115             if (errno == EINTR)
3116                 continue;
3117 
3118             _glfwInputError(GLFW_PLATFORM_ERROR,
3119                             "Wayland: Error while writing the clipboard: %s",
3120                             strerror(errno));
3121             break;
3122         }
3123 
3124         length -= result;
3125         string += result;
3126     }
3127 
3128     close(fd);
3129 }
3130 
dataSourceHandleCancelled(void * userData,struct wl_data_source * source)3131 static void dataSourceHandleCancelled(void* userData,
3132                                       struct wl_data_source* source)
3133 {
3134     wl_data_source_destroy(source);
3135 
3136     if (_glfw.wl.selectionSource != source)
3137         return;
3138 
3139     _glfw.wl.selectionSource = NULL;
3140 }
3141 
3142 static const struct wl_data_source_listener dataSourceListener =
3143 {
3144     dataSourceHandleTarget,
3145     dataSourceHandleSend,
3146     dataSourceHandleCancelled,
3147 };
3148 
_glfwSetClipboardStringWayland(const char * string)3149 void _glfwSetClipboardStringWayland(const char* string)
3150 {
3151     if (_glfw.wl.selectionSource)
3152     {
3153         wl_data_source_destroy(_glfw.wl.selectionSource);
3154         _glfw.wl.selectionSource = NULL;
3155     }
3156 
3157     char* copy = _glfw_strdup(string);
3158     if (!copy)
3159     {
3160         _glfwInputError(GLFW_OUT_OF_MEMORY, NULL);
3161         return;
3162     }
3163 
3164     _glfw_free(_glfw.wl.clipboardString);
3165     _glfw.wl.clipboardString = copy;
3166 
3167     _glfw.wl.selectionSource =
3168         wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager);
3169     if (!_glfw.wl.selectionSource)
3170     {
3171         _glfwInputError(GLFW_PLATFORM_ERROR,
3172                         "Wayland: Failed to create clipboard data source");
3173         return;
3174     }
3175     wl_data_source_add_listener(_glfw.wl.selectionSource,
3176                                 &dataSourceListener,
3177                                 NULL);
3178     wl_data_source_offer(_glfw.wl.selectionSource, "text/plain;charset=utf-8");
3179     wl_data_device_set_selection(_glfw.wl.dataDevice,
3180                                  _glfw.wl.selectionSource,
3181                                  _glfw.wl.serial);
3182 }
3183 
_glfwGetClipboardStringWayland(void)3184 const char* _glfwGetClipboardStringWayland(void)
3185 {
3186     if (!_glfw.wl.selectionOffer)
3187     {
3188         _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
3189                         "Wayland: No clipboard data available");
3190         return NULL;
3191     }
3192 
3193     if (_glfw.wl.selectionSource)
3194         return _glfw.wl.clipboardString;
3195 
3196     _glfw_free(_glfw.wl.clipboardString);
3197     _glfw.wl.clipboardString =
3198         readDataOfferAsString(_glfw.wl.selectionOffer, "text/plain;charset=utf-8");
3199     return _glfw.wl.clipboardString;
3200 }
3201 
_glfwGetEGLPlatformWayland(EGLint ** attribs)3202 EGLenum _glfwGetEGLPlatformWayland(EGLint** attribs)
3203 {
3204     if (_glfw.egl.EXT_platform_base && _glfw.egl.EXT_platform_wayland)
3205         return EGL_PLATFORM_WAYLAND_EXT;
3206     else
3207         return 0;
3208 }
3209 
_glfwGetEGLNativeDisplayWayland(void)3210 EGLNativeDisplayType _glfwGetEGLNativeDisplayWayland(void)
3211 {
3212     return _glfw.wl.display;
3213 }
3214 
_glfwGetEGLNativeWindowWayland(_GLFWwindow * window)3215 EGLNativeWindowType _glfwGetEGLNativeWindowWayland(_GLFWwindow* window)
3216 {
3217     return window->wl.egl.window;
3218 }
3219 
_glfwGetRequiredInstanceExtensionsWayland(char ** extensions)3220 void _glfwGetRequiredInstanceExtensionsWayland(char** extensions)
3221 {
3222     if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_wayland_surface)
3223         return;
3224 
3225     extensions[0] = "VK_KHR_surface";
3226     extensions[1] = "VK_KHR_wayland_surface";
3227 }
3228 
_glfwGetPhysicalDevicePresentationSupportWayland(VkInstance instance,VkPhysicalDevice device,uint32_t queuefamily)3229 GLFWbool _glfwGetPhysicalDevicePresentationSupportWayland(VkInstance instance,
3230                                                           VkPhysicalDevice device,
3231                                                           uint32_t queuefamily)
3232 {
3233     PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
3234         vkGetPhysicalDeviceWaylandPresentationSupportKHR =
3235         (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)
3236         vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR");
3237     if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR)
3238     {
3239         _glfwInputError(GLFW_API_UNAVAILABLE,
3240                         "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension");
3241         return VK_NULL_HANDLE;
3242     }
3243 
3244     return vkGetPhysicalDeviceWaylandPresentationSupportKHR(device,
3245                                                             queuefamily,
3246                                                             _glfw.wl.display);
3247 }
3248 
_glfwCreateWindowSurfaceWayland(VkInstance instance,_GLFWwindow * window,const VkAllocationCallbacks * allocator,VkSurfaceKHR * surface)3249 VkResult _glfwCreateWindowSurfaceWayland(VkInstance instance,
3250                                          _GLFWwindow* window,
3251                                          const VkAllocationCallbacks* allocator,
3252                                          VkSurfaceKHR* surface)
3253 {
3254     VkResult err;
3255     VkWaylandSurfaceCreateInfoKHR sci;
3256     PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR;
3257 
3258     vkCreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR)
3259         vkGetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR");
3260     if (!vkCreateWaylandSurfaceKHR)
3261     {
3262         _glfwInputError(GLFW_API_UNAVAILABLE,
3263                         "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension");
3264         return VK_ERROR_EXTENSION_NOT_PRESENT;
3265     }
3266 
3267     memset(&sci, 0, sizeof(sci));
3268     sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
3269     sci.display = _glfw.wl.display;
3270     sci.surface = window->wl.surface;
3271 
3272     err = vkCreateWaylandSurfaceKHR(instance, &sci, allocator, surface);
3273     if (err)
3274     {
3275         _glfwInputError(GLFW_PLATFORM_ERROR,
3276                         "Wayland: Failed to create Vulkan surface: %s",
3277                         _glfwGetVulkanResultString(err));
3278     }
3279 
3280     return err;
3281 }
3282 
3283 
3284 //////////////////////////////////////////////////////////////////////////
3285 //////                        GLFW native API                       //////
3286 //////////////////////////////////////////////////////////////////////////
3287 
glfwGetWaylandDisplay(void)3288 GLFWAPI struct wl_display* glfwGetWaylandDisplay(void)
3289 {
3290     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
3291 
3292     if (_glfw.platform.platformID != GLFW_PLATFORM_WAYLAND)
3293     {
3294         _glfwInputError(GLFW_PLATFORM_UNAVAILABLE,
3295                         "Wayland: Platform not initialized");
3296         return NULL;
3297     }
3298 
3299     return _glfw.wl.display;
3300 }
3301 
glfwGetWaylandWindow(GLFWwindow * handle)3302 GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle)
3303 {
3304     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
3305 
3306     if (_glfw.platform.platformID != GLFW_PLATFORM_WAYLAND)
3307     {
3308         _glfwInputError(GLFW_PLATFORM_UNAVAILABLE,
3309                         "Wayland: Platform not initialized");
3310         return NULL;
3311     }
3312 
3313     _GLFWwindow* window = (_GLFWwindow*) handle;
3314     assert(window != NULL);
3315 
3316     return window->wl.surface;
3317 }
3318 
3319 #endif // _GLFW_WAYLAND
3320 
3321