• 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                 close(fds[0]);
1333                 return NULL;
1334             }
1335 
1336             string = longer;
1337             size = requiredSize;
1338         }
1339 
1340         const ssize_t result = read(fds[0], string + length, readSize);
1341         if (result == 0)
1342             break;
1343         else if (result == -1)
1344         {
1345             if (errno == EINTR)
1346                 continue;
1347 
1348             _glfwInputError(GLFW_PLATFORM_ERROR,
1349                             "Wayland: Failed to read from data offer pipe: %s",
1350                             strerror(errno));
1351             close(fds[0]);
1352             return NULL;
1353         }
1354 
1355         length += result;
1356     }
1357 
1358     close(fds[0]);
1359 
1360     string[length] = '\0';
1361     return string;
1362 }
1363 
pointerHandleEnter(void * userData,struct wl_pointer * pointer,uint32_t serial,struct wl_surface * surface,wl_fixed_t sx,wl_fixed_t sy)1364 static void pointerHandleEnter(void* userData,
1365                                struct wl_pointer* pointer,
1366                                uint32_t serial,
1367                                struct wl_surface* surface,
1368                                wl_fixed_t sx,
1369                                wl_fixed_t sy)
1370 {
1371     // Happens in the case we just destroyed the surface.
1372     if (!surface)
1373         return;
1374 
1375     if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag)
1376         return;
1377 
1378     _GLFWwindow* window = wl_surface_get_user_data(surface);
1379 
1380     _glfw.wl.serial = serial;
1381     _glfw.wl.pointerEnterSerial = serial;
1382     _glfw.wl.pointerFocus = window;
1383 
1384     if (surface == window->wl.surface)
1385     {
1386         window->wl.hovered = GLFW_TRUE;
1387         _glfwSetCursorWayland(window, window->wl.currentCursor);
1388         _glfwInputCursorEnter(window, GLFW_TRUE);
1389     }
1390     else
1391     {
1392         if (window->wl.fallback.decorations)
1393             window->wl.fallback.focus = surface;
1394     }
1395 }
1396 
pointerHandleLeave(void * userData,struct wl_pointer * pointer,uint32_t serial,struct wl_surface * surface)1397 static void pointerHandleLeave(void* userData,
1398                                struct wl_pointer* pointer,
1399                                uint32_t serial,
1400                                struct wl_surface* surface)
1401 {
1402     if (!surface)
1403         return;
1404 
1405     if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag)
1406         return;
1407 
1408     _GLFWwindow* window = _glfw.wl.pointerFocus;
1409     if (!window)
1410         return;
1411 
1412     _glfw.wl.serial = serial;
1413     _glfw.wl.pointerFocus = NULL;
1414     _glfw.wl.cursorPreviousName = NULL;
1415 
1416     if (window->wl.hovered)
1417     {
1418         window->wl.hovered = GLFW_FALSE;
1419         _glfwInputCursorEnter(window, GLFW_FALSE);
1420     }
1421     else
1422     {
1423         if (window->wl.fallback.decorations)
1424             window->wl.fallback.focus = NULL;
1425     }
1426 }
1427 
pointerHandleMotion(void * userData,struct wl_pointer * pointer,uint32_t time,wl_fixed_t sx,wl_fixed_t sy)1428 static void pointerHandleMotion(void* userData,
1429                                 struct wl_pointer* pointer,
1430                                 uint32_t time,
1431                                 wl_fixed_t sx,
1432                                 wl_fixed_t sy)
1433 {
1434     _GLFWwindow* window = _glfw.wl.pointerFocus;
1435     if (!window)
1436         return;
1437 
1438     if (window->cursorMode == GLFW_CURSOR_DISABLED)
1439         return;
1440 
1441     const double xpos = wl_fixed_to_double(sx);
1442     const double ypos = wl_fixed_to_double(sy);
1443     window->wl.cursorPosX = xpos;
1444     window->wl.cursorPosY = ypos;
1445 
1446     if (window->wl.hovered)
1447     {
1448         _glfw.wl.cursorPreviousName = NULL;
1449         _glfwInputCursorPos(window, xpos, ypos);
1450         return;
1451     }
1452 
1453     if (window->wl.fallback.decorations)
1454     {
1455         const char* cursorName = "left_ptr";
1456 
1457         if (window->resizable)
1458         {
1459             if (window->wl.fallback.focus == window->wl.fallback.top.surface)
1460             {
1461                 if (ypos < GLFW_BORDER_SIZE)
1462                     cursorName = "n-resize";
1463             }
1464             else if (window->wl.fallback.focus == window->wl.fallback.left.surface)
1465             {
1466                 if (ypos < GLFW_BORDER_SIZE)
1467                     cursorName = "nw-resize";
1468                 else
1469                     cursorName = "w-resize";
1470             }
1471             else if (window->wl.fallback.focus == window->wl.fallback.right.surface)
1472             {
1473                 if (ypos < GLFW_BORDER_SIZE)
1474                     cursorName = "ne-resize";
1475                 else
1476                     cursorName = "e-resize";
1477             }
1478             else if (window->wl.fallback.focus == window->wl.fallback.bottom.surface)
1479             {
1480                 if (xpos < GLFW_BORDER_SIZE)
1481                     cursorName = "sw-resize";
1482                 else if (xpos > window->wl.width + GLFW_BORDER_SIZE)
1483                     cursorName = "se-resize";
1484                 else
1485                     cursorName = "s-resize";
1486             }
1487         }
1488 
1489         if (_glfw.wl.cursorPreviousName != cursorName)
1490         {
1491             struct wl_surface* surface = _glfw.wl.cursorSurface;
1492             struct wl_cursor_theme* theme = _glfw.wl.cursorTheme;
1493             int scale = 1;
1494 
1495             if (window->wl.bufferScale > 1 && _glfw.wl.cursorThemeHiDPI)
1496             {
1497                 // We only support up to scale=2 for now, since libwayland-cursor
1498                 // requires us to load a different theme for each size.
1499                 scale = 2;
1500                 theme = _glfw.wl.cursorThemeHiDPI;
1501             }
1502 
1503             struct wl_cursor* cursor = wl_cursor_theme_get_cursor(theme, cursorName);
1504             if (!cursor)
1505                 return;
1506 
1507             // TODO: handle animated cursors too.
1508             struct wl_cursor_image* image = cursor->images[0];
1509             if (!image)
1510                 return;
1511 
1512             struct wl_buffer* buffer = wl_cursor_image_get_buffer(image);
1513             if (!buffer)
1514                 return;
1515 
1516             wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial,
1517                                   surface,
1518                                   image->hotspot_x / scale,
1519                                   image->hotspot_y / scale);
1520             wl_surface_set_buffer_scale(surface, scale);
1521             wl_surface_attach(surface, buffer, 0, 0);
1522             wl_surface_damage(surface, 0, 0, image->width, image->height);
1523             wl_surface_commit(surface);
1524 
1525             _glfw.wl.cursorPreviousName = cursorName;
1526         }
1527     }
1528 }
1529 
pointerHandleButton(void * userData,struct wl_pointer * pointer,uint32_t serial,uint32_t time,uint32_t button,uint32_t state)1530 static void pointerHandleButton(void* userData,
1531                                 struct wl_pointer* pointer,
1532                                 uint32_t serial,
1533                                 uint32_t time,
1534                                 uint32_t button,
1535                                 uint32_t state)
1536 {
1537     _GLFWwindow* window = _glfw.wl.pointerFocus;
1538     if (!window)
1539         return;
1540 
1541     if (window->wl.hovered)
1542     {
1543         _glfw.wl.serial = serial;
1544 
1545         _glfwInputMouseClick(window,
1546                              button - BTN_LEFT,
1547                              state == WL_POINTER_BUTTON_STATE_PRESSED,
1548                              _glfw.wl.xkb.modifiers);
1549         return;
1550     }
1551 
1552     if (window->wl.fallback.decorations)
1553     {
1554         if (button == BTN_LEFT)
1555         {
1556             uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE;
1557 
1558             if (window->wl.fallback.focus == window->wl.fallback.top.surface)
1559             {
1560                 if (window->wl.cursorPosY < GLFW_BORDER_SIZE)
1561                     edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP;
1562                 else
1563                     xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial);
1564             }
1565             else if (window->wl.fallback.focus == window->wl.fallback.left.surface)
1566             {
1567                 if (window->wl.cursorPosY < GLFW_BORDER_SIZE)
1568                     edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
1569                 else
1570                     edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
1571             }
1572             else if (window->wl.fallback.focus == window->wl.fallback.right.surface)
1573             {
1574                 if (window->wl.cursorPosY < GLFW_BORDER_SIZE)
1575                     edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
1576                 else
1577                     edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
1578             }
1579             else if (window->wl.fallback.focus == window->wl.fallback.bottom.surface)
1580             {
1581                 if (window->wl.cursorPosX < GLFW_BORDER_SIZE)
1582                     edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
1583                 else if (window->wl.cursorPosX > window->wl.width + GLFW_BORDER_SIZE)
1584                     edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
1585                 else
1586                     edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
1587             }
1588 
1589             if (edges != XDG_TOPLEVEL_RESIZE_EDGE_NONE)
1590             {
1591                 xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat,
1592                                     serial, edges);
1593             }
1594         }
1595         else if (button == BTN_RIGHT)
1596         {
1597             if (window->wl.xdg.toplevel)
1598             {
1599                 xdg_toplevel_show_window_menu(window->wl.xdg.toplevel,
1600                                               _glfw.wl.seat, serial,
1601                                               window->wl.cursorPosX,
1602                                               window->wl.cursorPosY);
1603             }
1604         }
1605     }
1606 }
1607 
pointerHandleAxis(void * userData,struct wl_pointer * pointer,uint32_t time,uint32_t axis,wl_fixed_t value)1608 static void pointerHandleAxis(void* userData,
1609                               struct wl_pointer* pointer,
1610                               uint32_t time,
1611                               uint32_t axis,
1612                               wl_fixed_t value)
1613 {
1614     _GLFWwindow* window = _glfw.wl.pointerFocus;
1615     if (!window)
1616         return;
1617 
1618     // NOTE: 10 units of motion per mouse wheel step seems to be a common ratio
1619     if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
1620         _glfwInputScroll(window, -wl_fixed_to_double(value) / 10.0, 0.0);
1621     else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
1622         _glfwInputScroll(window, 0.0, -wl_fixed_to_double(value) / 10.0);
1623 }
1624 
1625 static const struct wl_pointer_listener pointerListener =
1626 {
1627     pointerHandleEnter,
1628     pointerHandleLeave,
1629     pointerHandleMotion,
1630     pointerHandleButton,
1631     pointerHandleAxis,
1632 };
1633 
keyboardHandleKeymap(void * userData,struct wl_keyboard * keyboard,uint32_t format,int fd,uint32_t size)1634 static void keyboardHandleKeymap(void* userData,
1635                                  struct wl_keyboard* keyboard,
1636                                  uint32_t format,
1637                                  int fd,
1638                                  uint32_t size)
1639 {
1640     struct xkb_keymap* keymap;
1641     struct xkb_state* state;
1642     struct xkb_compose_table* composeTable;
1643     struct xkb_compose_state* composeState;
1644 
1645     char* mapStr;
1646     const char* locale;
1647 
1648     if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
1649     {
1650         close(fd);
1651         return;
1652     }
1653 
1654     mapStr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
1655     if (mapStr == MAP_FAILED) {
1656         close(fd);
1657         return;
1658     }
1659 
1660     keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context,
1661                                         mapStr,
1662                                         XKB_KEYMAP_FORMAT_TEXT_V1,
1663                                         0);
1664     munmap(mapStr, size);
1665     close(fd);
1666 
1667     if (!keymap)
1668     {
1669         _glfwInputError(GLFW_PLATFORM_ERROR,
1670                         "Wayland: Failed to compile keymap");
1671         return;
1672     }
1673 
1674     state = xkb_state_new(keymap);
1675     if (!state)
1676     {
1677         _glfwInputError(GLFW_PLATFORM_ERROR,
1678                         "Wayland: Failed to create XKB state");
1679         xkb_keymap_unref(keymap);
1680         return;
1681     }
1682 
1683     // Look up the preferred locale, falling back to "C" as default.
1684     locale = getenv("LC_ALL");
1685     if (!locale)
1686         locale = getenv("LC_CTYPE");
1687     if (!locale)
1688         locale = getenv("LANG");
1689     if (!locale)
1690         locale = "C";
1691 
1692     composeTable =
1693         xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale,
1694                                           XKB_COMPOSE_COMPILE_NO_FLAGS);
1695     if (composeTable)
1696     {
1697         composeState =
1698             xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS);
1699         xkb_compose_table_unref(composeTable);
1700         if (composeState)
1701             _glfw.wl.xkb.composeState = composeState;
1702         else
1703             _glfwInputError(GLFW_PLATFORM_ERROR,
1704                             "Wayland: Failed to create XKB compose state");
1705     }
1706     else
1707     {
1708         _glfwInputError(GLFW_PLATFORM_ERROR,
1709                         "Wayland: Failed to create XKB compose table");
1710     }
1711 
1712     xkb_keymap_unref(_glfw.wl.xkb.keymap);
1713     xkb_state_unref(_glfw.wl.xkb.state);
1714     _glfw.wl.xkb.keymap = keymap;
1715     _glfw.wl.xkb.state = state;
1716 
1717     _glfw.wl.xkb.controlIndex  = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control");
1718     _glfw.wl.xkb.altIndex      = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1");
1719     _glfw.wl.xkb.shiftIndex    = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift");
1720     _glfw.wl.xkb.superIndex    = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4");
1721     _glfw.wl.xkb.capsLockIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Lock");
1722     _glfw.wl.xkb.numLockIndex  = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2");
1723 }
1724 
keyboardHandleEnter(void * userData,struct wl_keyboard * keyboard,uint32_t serial,struct wl_surface * surface,struct wl_array * keys)1725 static void keyboardHandleEnter(void* userData,
1726                                 struct wl_keyboard* keyboard,
1727                                 uint32_t serial,
1728                                 struct wl_surface* surface,
1729                                 struct wl_array* keys)
1730 {
1731     // Happens in the case we just destroyed the surface.
1732     if (!surface)
1733         return;
1734 
1735     if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag)
1736         return;
1737 
1738     _GLFWwindow* window = wl_surface_get_user_data(surface);
1739     if (surface != window->wl.surface)
1740         return;
1741 
1742     _glfw.wl.serial = serial;
1743     _glfw.wl.keyboardFocus = window;
1744     _glfwInputWindowFocus(window, GLFW_TRUE);
1745 }
1746 
keyboardHandleLeave(void * userData,struct wl_keyboard * keyboard,uint32_t serial,struct wl_surface * surface)1747 static void keyboardHandleLeave(void* userData,
1748                                 struct wl_keyboard* keyboard,
1749                                 uint32_t serial,
1750                                 struct wl_surface* surface)
1751 {
1752     _GLFWwindow* window = _glfw.wl.keyboardFocus;
1753 
1754     if (!window)
1755         return;
1756 
1757     struct itimerspec timer = {0};
1758     timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL);
1759 
1760     _glfw.wl.serial = serial;
1761     _glfw.wl.keyboardFocus = NULL;
1762     _glfwInputWindowFocus(window, GLFW_FALSE);
1763 }
1764 
keyboardHandleKey(void * userData,struct wl_keyboard * keyboard,uint32_t serial,uint32_t time,uint32_t scancode,uint32_t state)1765 static void keyboardHandleKey(void* userData,
1766                               struct wl_keyboard* keyboard,
1767                               uint32_t serial,
1768                               uint32_t time,
1769                               uint32_t scancode,
1770                               uint32_t state)
1771 {
1772     _GLFWwindow* window = _glfw.wl.keyboardFocus;
1773     if (!window)
1774         return;
1775 
1776     const int key = translateKey(scancode);
1777     const int action =
1778         state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE;
1779 
1780     _glfw.wl.serial = serial;
1781 
1782     struct itimerspec timer = {0};
1783 
1784     if (action == GLFW_PRESS)
1785     {
1786         const xkb_keycode_t keycode = scancode + 8;
1787 
1788         if (xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, keycode) &&
1789             _glfw.wl.keyRepeatRate > 0)
1790         {
1791             _glfw.wl.keyRepeatScancode = scancode;
1792             if (_glfw.wl.keyRepeatRate > 1)
1793                 timer.it_interval.tv_nsec = 1000000000 / _glfw.wl.keyRepeatRate;
1794             else
1795                 timer.it_interval.tv_sec = 1;
1796 
1797             timer.it_value.tv_sec = _glfw.wl.keyRepeatDelay / 1000;
1798             timer.it_value.tv_nsec = (_glfw.wl.keyRepeatDelay % 1000) * 1000000;
1799         }
1800     }
1801 
1802     timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL);
1803 
1804     _glfwInputKey(window, key, scancode, action, _glfw.wl.xkb.modifiers);
1805 
1806     if (action == GLFW_PRESS)
1807         inputText(window, scancode);
1808 }
1809 
keyboardHandleModifiers(void * userData,struct wl_keyboard * keyboard,uint32_t serial,uint32_t modsDepressed,uint32_t modsLatched,uint32_t modsLocked,uint32_t group)1810 static void keyboardHandleModifiers(void* userData,
1811                                     struct wl_keyboard* keyboard,
1812                                     uint32_t serial,
1813                                     uint32_t modsDepressed,
1814                                     uint32_t modsLatched,
1815                                     uint32_t modsLocked,
1816                                     uint32_t group)
1817 {
1818     _glfw.wl.serial = serial;
1819 
1820     if (!_glfw.wl.xkb.keymap)
1821         return;
1822 
1823     xkb_state_update_mask(_glfw.wl.xkb.state,
1824                           modsDepressed,
1825                           modsLatched,
1826                           modsLocked,
1827                           0,
1828                           0,
1829                           group);
1830 
1831     _glfw.wl.xkb.modifiers = 0;
1832 
1833     struct
1834     {
1835         xkb_mod_index_t index;
1836         unsigned int bit;
1837     } modifiers[] =
1838     {
1839         { _glfw.wl.xkb.controlIndex,  GLFW_MOD_CONTROL },
1840         { _glfw.wl.xkb.altIndex,      GLFW_MOD_ALT },
1841         { _glfw.wl.xkb.shiftIndex,    GLFW_MOD_SHIFT },
1842         { _glfw.wl.xkb.superIndex,    GLFW_MOD_SUPER },
1843         { _glfw.wl.xkb.capsLockIndex, GLFW_MOD_CAPS_LOCK },
1844         { _glfw.wl.xkb.numLockIndex,  GLFW_MOD_NUM_LOCK }
1845     };
1846 
1847     for (size_t i = 0; i < sizeof(modifiers) / sizeof(modifiers[0]); i++)
1848     {
1849         if (xkb_state_mod_index_is_active(_glfw.wl.xkb.state,
1850                                           modifiers[i].index,
1851                                           XKB_STATE_MODS_EFFECTIVE) == 1)
1852         {
1853             _glfw.wl.xkb.modifiers |= modifiers[i].bit;
1854         }
1855     }
1856 }
1857 
keyboardHandleRepeatInfo(void * userData,struct wl_keyboard * keyboard,int32_t rate,int32_t delay)1858 static void keyboardHandleRepeatInfo(void* userData,
1859                                      struct wl_keyboard* keyboard,
1860                                      int32_t rate,
1861                                      int32_t delay)
1862 {
1863     if (keyboard != _glfw.wl.keyboard)
1864         return;
1865 
1866     _glfw.wl.keyRepeatRate = rate;
1867     _glfw.wl.keyRepeatDelay = delay;
1868 }
1869 
1870 static const struct wl_keyboard_listener keyboardListener =
1871 {
1872     keyboardHandleKeymap,
1873     keyboardHandleEnter,
1874     keyboardHandleLeave,
1875     keyboardHandleKey,
1876     keyboardHandleModifiers,
1877     keyboardHandleRepeatInfo,
1878 };
1879 
seatHandleCapabilities(void * userData,struct wl_seat * seat,enum wl_seat_capability caps)1880 static void seatHandleCapabilities(void* userData,
1881                                    struct wl_seat* seat,
1882                                    enum wl_seat_capability caps)
1883 {
1884     if ((caps & WL_SEAT_CAPABILITY_POINTER) && !_glfw.wl.pointer)
1885     {
1886         _glfw.wl.pointer = wl_seat_get_pointer(seat);
1887         wl_pointer_add_listener(_glfw.wl.pointer, &pointerListener, NULL);
1888     }
1889     else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer)
1890     {
1891         wl_pointer_destroy(_glfw.wl.pointer);
1892         _glfw.wl.pointer = NULL;
1893     }
1894 
1895     if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard)
1896     {
1897         _glfw.wl.keyboard = wl_seat_get_keyboard(seat);
1898         wl_keyboard_add_listener(_glfw.wl.keyboard, &keyboardListener, NULL);
1899     }
1900     else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard)
1901     {
1902         wl_keyboard_destroy(_glfw.wl.keyboard);
1903         _glfw.wl.keyboard = NULL;
1904     }
1905 }
1906 
seatHandleName(void * userData,struct wl_seat * seat,const char * name)1907 static void seatHandleName(void* userData,
1908                            struct wl_seat* seat,
1909                            const char* name)
1910 {
1911 }
1912 
1913 static const struct wl_seat_listener seatListener =
1914 {
1915     seatHandleCapabilities,
1916     seatHandleName,
1917 };
1918 
dataOfferHandleOffer(void * userData,struct wl_data_offer * offer,const char * mimeType)1919 static void dataOfferHandleOffer(void* userData,
1920                                  struct wl_data_offer* offer,
1921                                  const char* mimeType)
1922 {
1923     for (unsigned int i = 0; i < _glfw.wl.offerCount; i++)
1924     {
1925         if (_glfw.wl.offers[i].offer == offer)
1926         {
1927             if (strcmp(mimeType, "text/plain;charset=utf-8") == 0)
1928                 _glfw.wl.offers[i].text_plain_utf8 = GLFW_TRUE;
1929             else if (strcmp(mimeType, "text/uri-list") == 0)
1930                 _glfw.wl.offers[i].text_uri_list = GLFW_TRUE;
1931 
1932             break;
1933         }
1934     }
1935 }
1936 
1937 static const struct wl_data_offer_listener dataOfferListener =
1938 {
1939     dataOfferHandleOffer
1940 };
1941 
dataDeviceHandleDataOffer(void * userData,struct wl_data_device * device,struct wl_data_offer * offer)1942 static void dataDeviceHandleDataOffer(void* userData,
1943                                       struct wl_data_device* device,
1944                                       struct wl_data_offer* offer)
1945 {
1946     _GLFWofferWayland* offers =
1947         _glfw_realloc(_glfw.wl.offers,
1948                       sizeof(_GLFWofferWayland) * (_glfw.wl.offerCount + 1));
1949     if (!offers)
1950     {
1951         _glfwInputError(GLFW_OUT_OF_MEMORY, NULL);
1952         return;
1953     }
1954 
1955     _glfw.wl.offers = offers;
1956     _glfw.wl.offerCount++;
1957 
1958     _glfw.wl.offers[_glfw.wl.offerCount - 1] = (_GLFWofferWayland) { offer };
1959     wl_data_offer_add_listener(offer, &dataOfferListener, NULL);
1960 }
1961 
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)1962 static void dataDeviceHandleEnter(void* userData,
1963                                   struct wl_data_device* device,
1964                                   uint32_t serial,
1965                                   struct wl_surface* surface,
1966                                   wl_fixed_t x,
1967                                   wl_fixed_t y,
1968                                   struct wl_data_offer* offer)
1969 {
1970     if (_glfw.wl.dragOffer)
1971     {
1972         wl_data_offer_destroy(_glfw.wl.dragOffer);
1973         _glfw.wl.dragOffer = NULL;
1974         _glfw.wl.dragFocus = NULL;
1975     }
1976 
1977     unsigned int i;
1978 
1979     for (i = 0; i < _glfw.wl.offerCount; i++)
1980     {
1981         if (_glfw.wl.offers[i].offer == offer)
1982             break;
1983     }
1984 
1985     if (i == _glfw.wl.offerCount)
1986         return;
1987 
1988     if (surface && wl_proxy_get_tag((struct wl_proxy*) surface) == &_glfw.wl.tag)
1989     {
1990         _GLFWwindow* window = wl_surface_get_user_data(surface);
1991         if (window->wl.surface == surface)
1992         {
1993             if (_glfw.wl.offers[i].text_uri_list)
1994             {
1995                 _glfw.wl.dragOffer = offer;
1996                 _glfw.wl.dragFocus = window;
1997                 _glfw.wl.dragSerial = serial;
1998 
1999                 wl_data_offer_accept(offer, serial, "text/uri-list");
2000             }
2001         }
2002     }
2003 
2004     if (!_glfw.wl.dragOffer)
2005     {
2006         wl_data_offer_accept(offer, serial, NULL);
2007         wl_data_offer_destroy(offer);
2008     }
2009 
2010     _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1];
2011     _glfw.wl.offerCount--;
2012 }
2013 
dataDeviceHandleLeave(void * userData,struct wl_data_device * device)2014 static void dataDeviceHandleLeave(void* userData,
2015                                   struct wl_data_device* device)
2016 {
2017     if (_glfw.wl.dragOffer)
2018     {
2019         wl_data_offer_destroy(_glfw.wl.dragOffer);
2020         _glfw.wl.dragOffer = NULL;
2021         _glfw.wl.dragFocus = NULL;
2022     }
2023 }
2024 
dataDeviceHandleMotion(void * userData,struct wl_data_device * device,uint32_t time,wl_fixed_t x,wl_fixed_t y)2025 static void dataDeviceHandleMotion(void* userData,
2026                                    struct wl_data_device* device,
2027                                    uint32_t time,
2028                                    wl_fixed_t x,
2029                                    wl_fixed_t y)
2030 {
2031 }
2032 
dataDeviceHandleDrop(void * userData,struct wl_data_device * device)2033 static void dataDeviceHandleDrop(void* userData,
2034                                  struct wl_data_device* device)
2035 {
2036     if (!_glfw.wl.dragOffer)
2037         return;
2038 
2039     char* string = readDataOfferAsString(_glfw.wl.dragOffer, "text/uri-list");
2040     if (string)
2041     {
2042         int count;
2043         char** paths = _glfwParseUriList(string, &count);
2044         if (paths)
2045         {
2046             _glfwInputDrop(_glfw.wl.dragFocus, count, (const char**) paths);
2047 
2048             for (int i = 0; i < count; i++)
2049                 _glfw_free(paths[i]);
2050 
2051             _glfw_free(paths);
2052         }
2053 
2054         _glfw_free(string);
2055     }
2056 }
2057 
dataDeviceHandleSelection(void * userData,struct wl_data_device * device,struct wl_data_offer * offer)2058 static void dataDeviceHandleSelection(void* userData,
2059                                       struct wl_data_device* device,
2060                                       struct wl_data_offer* offer)
2061 {
2062     if (_glfw.wl.selectionOffer)
2063     {
2064         wl_data_offer_destroy(_glfw.wl.selectionOffer);
2065         _glfw.wl.selectionOffer = NULL;
2066     }
2067 
2068     for (unsigned int i = 0; i < _glfw.wl.offerCount; i++)
2069     {
2070         if (_glfw.wl.offers[i].offer == offer)
2071         {
2072             if (_glfw.wl.offers[i].text_plain_utf8)
2073                 _glfw.wl.selectionOffer = offer;
2074             else
2075                 wl_data_offer_destroy(offer);
2076 
2077             _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1];
2078             _glfw.wl.offerCount--;
2079             break;
2080         }
2081     }
2082 }
2083 
2084 const struct wl_data_device_listener dataDeviceListener =
2085 {
2086     dataDeviceHandleDataOffer,
2087     dataDeviceHandleEnter,
2088     dataDeviceHandleLeave,
2089     dataDeviceHandleMotion,
2090     dataDeviceHandleDrop,
2091     dataDeviceHandleSelection,
2092 };
2093 
xdgActivationHandleDone(void * userData,struct xdg_activation_token_v1 * activationToken,const char * token)2094 static void xdgActivationHandleDone(void* userData,
2095                                     struct xdg_activation_token_v1* activationToken,
2096                                     const char* token)
2097 {
2098     _GLFWwindow* window = userData;
2099 
2100     if (activationToken != window->wl.activationToken)
2101         return;
2102 
2103     xdg_activation_v1_activate(_glfw.wl.activationManager, token, window->wl.surface);
2104     xdg_activation_token_v1_destroy(window->wl.activationToken);
2105     window->wl.activationToken = NULL;
2106 }
2107 
2108 static const struct xdg_activation_token_v1_listener xdgActivationListener =
2109 {
2110     xdgActivationHandleDone
2111 };
2112 
_glfwAddSeatListenerWayland(struct wl_seat * seat)2113 void _glfwAddSeatListenerWayland(struct wl_seat* seat)
2114 {
2115     wl_seat_add_listener(seat, &seatListener, NULL);
2116 }
2117 
_glfwAddDataDeviceListenerWayland(struct wl_data_device * device)2118 void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device)
2119 {
2120     wl_data_device_add_listener(device, &dataDeviceListener, NULL);
2121 }
2122 
2123 
2124 //////////////////////////////////////////////////////////////////////////
2125 //////                       GLFW platform API                      //////
2126 //////////////////////////////////////////////////////////////////////////
2127 
_glfwCreateWindowWayland(_GLFWwindow * window,const _GLFWwndconfig * wndconfig,const _GLFWctxconfig * ctxconfig,const _GLFWfbconfig * fbconfig)2128 GLFWbool _glfwCreateWindowWayland(_GLFWwindow* window,
2129                                   const _GLFWwndconfig* wndconfig,
2130                                   const _GLFWctxconfig* ctxconfig,
2131                                   const _GLFWfbconfig* fbconfig)
2132 {
2133     if (!createNativeSurface(window, wndconfig, fbconfig))
2134         return GLFW_FALSE;
2135 
2136     if (ctxconfig->client != GLFW_NO_API)
2137     {
2138         if (ctxconfig->source == GLFW_EGL_CONTEXT_API ||
2139             ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
2140         {
2141             window->wl.egl.window = wl_egl_window_create(window->wl.surface,
2142                                                          window->wl.fbWidth,
2143                                                          window->wl.fbHeight);
2144             if (!window->wl.egl.window)
2145             {
2146                 _glfwInputError(GLFW_PLATFORM_ERROR,
2147                                 "Wayland: Failed to create EGL window");
2148                 return GLFW_FALSE;
2149             }
2150 
2151             if (!_glfwInitEGL())
2152                 return GLFW_FALSE;
2153             if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
2154                 return GLFW_FALSE;
2155         }
2156         else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
2157         {
2158             if (!_glfwInitOSMesa())
2159                 return GLFW_FALSE;
2160             if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
2161                 return GLFW_FALSE;
2162         }
2163 
2164         if (!_glfwRefreshContextAttribs(window, ctxconfig))
2165             return GLFW_FALSE;
2166     }
2167 
2168     if (wndconfig->mousePassthrough)
2169         _glfwSetWindowMousePassthroughWayland(window, GLFW_TRUE);
2170 
2171     if (window->monitor || wndconfig->visible)
2172     {
2173         if (!createShellObjects(window))
2174             return GLFW_FALSE;
2175     }
2176 
2177     return GLFW_TRUE;
2178 }
2179 
_glfwDestroyWindowWayland(_GLFWwindow * window)2180 void _glfwDestroyWindowWayland(_GLFWwindow* window)
2181 {
2182     if (window == _glfw.wl.pointerFocus)
2183         _glfw.wl.pointerFocus = NULL;
2184 
2185     if (window == _glfw.wl.keyboardFocus)
2186         _glfw.wl.keyboardFocus = NULL;
2187 
2188     if (window->wl.fractionalScale)
2189         wp_fractional_scale_v1_destroy(window->wl.fractionalScale);
2190 
2191     if (window->wl.scalingViewport)
2192         wp_viewport_destroy(window->wl.scalingViewport);
2193 
2194     if (window->wl.activationToken)
2195         xdg_activation_token_v1_destroy(window->wl.activationToken);
2196 
2197     if (window->wl.idleInhibitor)
2198         zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor);
2199 
2200     if (window->wl.relativePointer)
2201         zwp_relative_pointer_v1_destroy(window->wl.relativePointer);
2202 
2203     if (window->wl.lockedPointer)
2204         zwp_locked_pointer_v1_destroy(window->wl.lockedPointer);
2205 
2206     if (window->wl.confinedPointer)
2207         zwp_confined_pointer_v1_destroy(window->wl.confinedPointer);
2208 
2209     if (window->context.destroy)
2210         window->context.destroy(window);
2211 
2212     destroyShellObjects(window);
2213 
2214     if (window->wl.fallback.buffer)
2215         wl_buffer_destroy(window->wl.fallback.buffer);
2216 
2217     if (window->wl.egl.window)
2218         wl_egl_window_destroy(window->wl.egl.window);
2219 
2220     if (window->wl.surface)
2221         wl_surface_destroy(window->wl.surface);
2222 
2223     _glfw_free(window->wl.appId);
2224     _glfw_free(window->wl.outputScales);
2225 }
2226 
_glfwSetWindowTitleWayland(_GLFWwindow * window,const char * title)2227 void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title)
2228 {
2229     if (window->wl.libdecor.frame)
2230         libdecor_frame_set_title(window->wl.libdecor.frame, title);
2231     else if (window->wl.xdg.toplevel)
2232         xdg_toplevel_set_title(window->wl.xdg.toplevel, title);
2233 }
2234 
_glfwSetWindowIconWayland(_GLFWwindow * window,int count,const GLFWimage * images)2235 void _glfwSetWindowIconWayland(_GLFWwindow* window,
2236                                int count, const GLFWimage* images)
2237 {
2238     _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2239                     "Wayland: The platform does not support setting the window icon");
2240 }
2241 
_glfwGetWindowPosWayland(_GLFWwindow * window,int * xpos,int * ypos)2242 void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos)
2243 {
2244     // A Wayland client is not aware of its position, so just warn and leave it
2245     // as (0, 0)
2246 
2247     _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2248                     "Wayland: The platform does not provide the window position");
2249 }
2250 
_glfwSetWindowPosWayland(_GLFWwindow * window,int xpos,int ypos)2251 void _glfwSetWindowPosWayland(_GLFWwindow* window, int xpos, int ypos)
2252 {
2253     // A Wayland client can not set its position, so just warn
2254 
2255     _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2256                     "Wayland: The platform does not support setting the window position");
2257 }
2258 
_glfwGetWindowSizeWayland(_GLFWwindow * window,int * width,int * height)2259 void _glfwGetWindowSizeWayland(_GLFWwindow* window, int* width, int* height)
2260 {
2261     if (width)
2262         *width = window->wl.width;
2263     if (height)
2264         *height = window->wl.height;
2265 }
2266 
_glfwSetWindowSizeWayland(_GLFWwindow * window,int width,int height)2267 void _glfwSetWindowSizeWayland(_GLFWwindow* window, int width, int height)
2268 {
2269     if (window->monitor)
2270     {
2271         // Video mode setting is not available on Wayland
2272     }
2273     else
2274     {
2275         if (!resizeWindow(window, width, height))
2276             return;
2277 
2278         if (window->wl.libdecor.frame)
2279         {
2280             struct libdecor_state* frameState =
2281                 libdecor_state_new(window->wl.width, window->wl.height);
2282             libdecor_frame_commit(window->wl.libdecor.frame, frameState, NULL);
2283             libdecor_state_free(frameState);
2284         }
2285 
2286         if (window->wl.visible)
2287             _glfwInputWindowDamage(window);
2288     }
2289 }
2290 
_glfwSetWindowSizeLimitsWayland(_GLFWwindow * window,int minwidth,int minheight,int maxwidth,int maxheight)2291 void _glfwSetWindowSizeLimitsWayland(_GLFWwindow* window,
2292                                      int minwidth, int minheight,
2293                                      int maxwidth, int maxheight)
2294 {
2295     if (window->wl.libdecor.frame)
2296     {
2297         if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE)
2298             minwidth = minheight = 0;
2299 
2300         if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)
2301             maxwidth = maxheight = 0;
2302 
2303         libdecor_frame_set_min_content_size(window->wl.libdecor.frame,
2304                                             minwidth, minheight);
2305         libdecor_frame_set_max_content_size(window->wl.libdecor.frame,
2306                                             maxwidth, maxheight);
2307     }
2308     else if (window->wl.xdg.toplevel)
2309         updateXdgSizeLimits(window);
2310 }
2311 
_glfwSetWindowAspectRatioWayland(_GLFWwindow * window,int numer,int denom)2312 void _glfwSetWindowAspectRatioWayland(_GLFWwindow* window, int numer, int denom)
2313 {
2314     if (window->wl.maximized || window->wl.fullscreen)
2315         return;
2316 
2317     int width = window->wl.width, height = window->wl.height;
2318 
2319     if (numer != GLFW_DONT_CARE && denom != GLFW_DONT_CARE)
2320     {
2321         const float aspectRatio = (float) width / (float) height;
2322         const float targetRatio = (float) numer / (float) denom;
2323         if (aspectRatio < targetRatio)
2324             height /= targetRatio;
2325         else if (aspectRatio > targetRatio)
2326             width *= targetRatio;
2327     }
2328 
2329     if (resizeWindow(window, width, height))
2330     {
2331         if (window->wl.libdecor.frame)
2332         {
2333             struct libdecor_state* frameState =
2334                 libdecor_state_new(window->wl.width, window->wl.height);
2335             libdecor_frame_commit(window->wl.libdecor.frame, frameState, NULL);
2336             libdecor_state_free(frameState);
2337         }
2338 
2339         _glfwInputWindowSize(window, window->wl.width, window->wl.height);
2340 
2341         if (window->wl.visible)
2342             _glfwInputWindowDamage(window);
2343     }
2344 }
2345 
_glfwGetFramebufferSizeWayland(_GLFWwindow * window,int * width,int * height)2346 void _glfwGetFramebufferSizeWayland(_GLFWwindow* window, int* width, int* height)
2347 {
2348     if (width)
2349         *width = window->wl.fbWidth;
2350     if (height)
2351         *height = window->wl.fbHeight;
2352 }
2353 
_glfwGetWindowFrameSizeWayland(_GLFWwindow * window,int * left,int * top,int * right,int * bottom)2354 void _glfwGetWindowFrameSizeWayland(_GLFWwindow* window,
2355                                     int* left, int* top,
2356                                     int* right, int* bottom)
2357 {
2358     if (window->wl.fallback.decorations)
2359     {
2360         if (top)
2361             *top = GLFW_CAPTION_HEIGHT;
2362         if (left)
2363             *left = GLFW_BORDER_SIZE;
2364         if (right)
2365             *right = GLFW_BORDER_SIZE;
2366         if (bottom)
2367             *bottom = GLFW_BORDER_SIZE;
2368     }
2369 }
2370 
_glfwGetWindowContentScaleWayland(_GLFWwindow * window,float * xscale,float * yscale)2371 void _glfwGetWindowContentScaleWayland(_GLFWwindow* window,
2372                                        float* xscale, float* yscale)
2373 {
2374     if (window->wl.fractionalScale)
2375     {
2376         if (xscale)
2377             *xscale = (float) window->wl.scalingNumerator / 120.f;
2378         if (yscale)
2379             *yscale = (float) window->wl.scalingNumerator / 120.f;
2380     }
2381     else
2382     {
2383         if (xscale)
2384             *xscale = (float) window->wl.bufferScale;
2385         if (yscale)
2386             *yscale = (float) window->wl.bufferScale;
2387     }
2388 }
2389 
_glfwIconifyWindowWayland(_GLFWwindow * window)2390 void _glfwIconifyWindowWayland(_GLFWwindow* window)
2391 {
2392     if (window->wl.libdecor.frame)
2393         libdecor_frame_set_minimized(window->wl.libdecor.frame);
2394     else if (window->wl.xdg.toplevel)
2395         xdg_toplevel_set_minimized(window->wl.xdg.toplevel);
2396 }
2397 
_glfwRestoreWindowWayland(_GLFWwindow * window)2398 void _glfwRestoreWindowWayland(_GLFWwindow* window)
2399 {
2400     if (window->monitor)
2401     {
2402         // There is no way to unset minimized, or even to know if we are
2403         // minimized, so there is nothing to do in this case.
2404     }
2405     else
2406     {
2407         // We assume we are not minimized and act only on maximization
2408 
2409         if (window->wl.maximized)
2410         {
2411             if (window->wl.libdecor.frame)
2412                 libdecor_frame_unset_maximized(window->wl.libdecor.frame);
2413             else if (window->wl.xdg.toplevel)
2414                 xdg_toplevel_unset_maximized(window->wl.xdg.toplevel);
2415             else
2416                 window->wl.maximized = GLFW_FALSE;
2417         }
2418     }
2419 }
2420 
_glfwMaximizeWindowWayland(_GLFWwindow * window)2421 void _glfwMaximizeWindowWayland(_GLFWwindow* window)
2422 {
2423     if (window->wl.libdecor.frame)
2424         libdecor_frame_set_maximized(window->wl.libdecor.frame);
2425     else if (window->wl.xdg.toplevel)
2426         xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
2427     else
2428         window->wl.maximized = GLFW_TRUE;
2429 }
2430 
_glfwShowWindowWayland(_GLFWwindow * window)2431 void _glfwShowWindowWayland(_GLFWwindow* window)
2432 {
2433     if (!window->wl.libdecor.frame && !window->wl.xdg.toplevel)
2434     {
2435         // NOTE: The XDG surface and role are created here so command-line applications
2436         //       with off-screen windows do not appear in for example the Unity dock
2437         createShellObjects(window);
2438     }
2439 }
2440 
_glfwHideWindowWayland(_GLFWwindow * window)2441 void _glfwHideWindowWayland(_GLFWwindow* window)
2442 {
2443     if (window->wl.visible)
2444     {
2445         window->wl.visible = GLFW_FALSE;
2446         destroyShellObjects(window);
2447 
2448         wl_surface_attach(window->wl.surface, NULL, 0, 0);
2449         wl_surface_commit(window->wl.surface);
2450     }
2451 }
2452 
_glfwRequestWindowAttentionWayland(_GLFWwindow * window)2453 void _glfwRequestWindowAttentionWayland(_GLFWwindow* window)
2454 {
2455     if (!_glfw.wl.activationManager)
2456         return;
2457 
2458     // We're about to overwrite this with a new request
2459     if (window->wl.activationToken)
2460         xdg_activation_token_v1_destroy(window->wl.activationToken);
2461 
2462     window->wl.activationToken =
2463         xdg_activation_v1_get_activation_token(_glfw.wl.activationManager);
2464     xdg_activation_token_v1_add_listener(window->wl.activationToken,
2465                                          &xdgActivationListener,
2466                                          window);
2467 
2468     xdg_activation_token_v1_commit(window->wl.activationToken);
2469 }
2470 
_glfwFocusWindowWayland(_GLFWwindow * window)2471 void _glfwFocusWindowWayland(_GLFWwindow* window)
2472 {
2473     if (!_glfw.wl.activationManager)
2474         return;
2475 
2476     if (window->wl.activationToken)
2477         xdg_activation_token_v1_destroy(window->wl.activationToken);
2478 
2479     window->wl.activationToken =
2480         xdg_activation_v1_get_activation_token(_glfw.wl.activationManager);
2481     xdg_activation_token_v1_add_listener(window->wl.activationToken,
2482                                          &xdgActivationListener,
2483                                          window);
2484 
2485     xdg_activation_token_v1_set_serial(window->wl.activationToken,
2486                                        _glfw.wl.serial,
2487                                        _glfw.wl.seat);
2488 
2489     _GLFWwindow* requester = _glfw.wl.keyboardFocus;
2490     if (requester)
2491     {
2492         xdg_activation_token_v1_set_surface(window->wl.activationToken,
2493                                             requester->wl.surface);
2494 
2495         if (requester->wl.appId)
2496         {
2497             xdg_activation_token_v1_set_app_id(window->wl.activationToken,
2498                                                requester->wl.appId);
2499         }
2500     }
2501 
2502     xdg_activation_token_v1_commit(window->wl.activationToken);
2503 }
2504 
_glfwSetWindowMonitorWayland(_GLFWwindow * window,_GLFWmonitor * monitor,int xpos,int ypos,int width,int height,int refreshRate)2505 void _glfwSetWindowMonitorWayland(_GLFWwindow* window,
2506                                   _GLFWmonitor* monitor,
2507                                   int xpos, int ypos,
2508                                   int width, int height,
2509                                   int refreshRate)
2510 {
2511     if (window->monitor == monitor)
2512     {
2513         if (!monitor)
2514             _glfwSetWindowSizeWayland(window, width, height);
2515 
2516         return;
2517     }
2518 
2519     if (window->monitor)
2520         releaseMonitor(window);
2521 
2522     _glfwInputWindowMonitor(window, monitor);
2523 
2524     if (window->monitor)
2525         acquireMonitor(window);
2526     else
2527         _glfwSetWindowSizeWayland(window, width, height);
2528 }
2529 
_glfwWindowFocusedWayland(_GLFWwindow * window)2530 GLFWbool _glfwWindowFocusedWayland(_GLFWwindow* window)
2531 {
2532     return _glfw.wl.keyboardFocus == window;
2533 }
2534 
_glfwWindowIconifiedWayland(_GLFWwindow * window)2535 GLFWbool _glfwWindowIconifiedWayland(_GLFWwindow* window)
2536 {
2537     // xdg-shell doesn’t give any way to request whether a surface is
2538     // iconified.
2539     return GLFW_FALSE;
2540 }
2541 
_glfwWindowVisibleWayland(_GLFWwindow * window)2542 GLFWbool _glfwWindowVisibleWayland(_GLFWwindow* window)
2543 {
2544     return window->wl.visible;
2545 }
2546 
_glfwWindowMaximizedWayland(_GLFWwindow * window)2547 GLFWbool _glfwWindowMaximizedWayland(_GLFWwindow* window)
2548 {
2549     return window->wl.maximized;
2550 }
2551 
_glfwWindowHoveredWayland(_GLFWwindow * window)2552 GLFWbool _glfwWindowHoveredWayland(_GLFWwindow* window)
2553 {
2554     return window->wl.hovered;
2555 }
2556 
_glfwFramebufferTransparentWayland(_GLFWwindow * window)2557 GLFWbool _glfwFramebufferTransparentWayland(_GLFWwindow* window)
2558 {
2559     return window->wl.transparent;
2560 }
2561 
_glfwSetWindowResizableWayland(_GLFWwindow * window,GLFWbool enabled)2562 void _glfwSetWindowResizableWayland(_GLFWwindow* window, GLFWbool enabled)
2563 {
2564     if (window->wl.libdecor.frame)
2565     {
2566         if (enabled)
2567         {
2568             libdecor_frame_set_capabilities(window->wl.libdecor.frame,
2569                                             LIBDECOR_ACTION_RESIZE);
2570         }
2571         else
2572         {
2573             libdecor_frame_unset_capabilities(window->wl.libdecor.frame,
2574                                               LIBDECOR_ACTION_RESIZE);
2575         }
2576     }
2577     else if (window->wl.xdg.toplevel)
2578         updateXdgSizeLimits(window);
2579 }
2580 
_glfwSetWindowDecoratedWayland(_GLFWwindow * window,GLFWbool enabled)2581 void _glfwSetWindowDecoratedWayland(_GLFWwindow* window, GLFWbool enabled)
2582 {
2583     if (window->wl.libdecor.frame)
2584     {
2585         libdecor_frame_set_visibility(window->wl.libdecor.frame, enabled);
2586     }
2587     else if (window->wl.xdg.decoration)
2588     {
2589         uint32_t mode;
2590 
2591         if (enabled)
2592             mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
2593         else
2594             mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
2595 
2596         zxdg_toplevel_decoration_v1_set_mode(window->wl.xdg.decoration, mode);
2597     }
2598     else if (window->wl.xdg.toplevel)
2599     {
2600         if (enabled)
2601             createFallbackDecorations(window);
2602         else
2603             destroyFallbackDecorations(window);
2604     }
2605 }
2606 
_glfwSetWindowFloatingWayland(_GLFWwindow * window,GLFWbool enabled)2607 void _glfwSetWindowFloatingWayland(_GLFWwindow* window, GLFWbool enabled)
2608 {
2609     _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2610                     "Wayland: Platform does not support making a window floating");
2611 }
2612 
_glfwSetWindowMousePassthroughWayland(_GLFWwindow * window,GLFWbool enabled)2613 void _glfwSetWindowMousePassthroughWayland(_GLFWwindow* window, GLFWbool enabled)
2614 {
2615     if (enabled)
2616     {
2617         struct wl_region* region = wl_compositor_create_region(_glfw.wl.compositor);
2618         wl_surface_set_input_region(window->wl.surface, region);
2619         wl_region_destroy(region);
2620     }
2621     else
2622         wl_surface_set_input_region(window->wl.surface, NULL);
2623 }
2624 
_glfwGetWindowOpacityWayland(_GLFWwindow * window)2625 float _glfwGetWindowOpacityWayland(_GLFWwindow* window)
2626 {
2627     return 1.f;
2628 }
2629 
_glfwSetWindowOpacityWayland(_GLFWwindow * window,float opacity)2630 void _glfwSetWindowOpacityWayland(_GLFWwindow* window, float opacity)
2631 {
2632     _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2633                     "Wayland: The platform does not support setting the window opacity");
2634 }
2635 
_glfwSetRawMouseMotionWayland(_GLFWwindow * window,GLFWbool enabled)2636 void _glfwSetRawMouseMotionWayland(_GLFWwindow* window, GLFWbool enabled)
2637 {
2638     // This is handled in relativePointerHandleRelativeMotion
2639 }
2640 
_glfwRawMouseMotionSupportedWayland(void)2641 GLFWbool _glfwRawMouseMotionSupportedWayland(void)
2642 {
2643     return GLFW_TRUE;
2644 }
2645 
_glfwPollEventsWayland(void)2646 void _glfwPollEventsWayland(void)
2647 {
2648     double timeout = 0.0;
2649     handleEvents(&timeout);
2650 }
2651 
_glfwWaitEventsWayland(void)2652 void _glfwWaitEventsWayland(void)
2653 {
2654     handleEvents(NULL);
2655 }
2656 
_glfwWaitEventsTimeoutWayland(double timeout)2657 void _glfwWaitEventsTimeoutWayland(double timeout)
2658 {
2659     handleEvents(&timeout);
2660 }
2661 
_glfwPostEmptyEventWayland(void)2662 void _glfwPostEmptyEventWayland(void)
2663 {
2664     wl_display_sync(_glfw.wl.display);
2665     flushDisplay();
2666 }
2667 
_glfwGetCursorPosWayland(_GLFWwindow * window,double * xpos,double * ypos)2668 void _glfwGetCursorPosWayland(_GLFWwindow* window, double* xpos, double* ypos)
2669 {
2670     if (xpos)
2671         *xpos = window->wl.cursorPosX;
2672     if (ypos)
2673         *ypos = window->wl.cursorPosY;
2674 }
2675 
_glfwSetCursorPosWayland(_GLFWwindow * window,double x,double y)2676 void _glfwSetCursorPosWayland(_GLFWwindow* window, double x, double y)
2677 {
2678     _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2679                     "Wayland: The platform does not support setting the cursor position");
2680 }
2681 
_glfwSetCursorModeWayland(_GLFWwindow * window,int mode)2682 void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode)
2683 {
2684     _glfwSetCursorWayland(window, window->wl.currentCursor);
2685 }
2686 
_glfwGetScancodeNameWayland(int scancode)2687 const char* _glfwGetScancodeNameWayland(int scancode)
2688 {
2689     if (scancode < 0 || scancode > 255)
2690     {
2691         _glfwInputError(GLFW_INVALID_VALUE,
2692                         "Wayland: Invalid scancode %i",
2693                         scancode);
2694         return NULL;
2695     }
2696 
2697     const int key = _glfw.wl.keycodes[scancode];
2698     if (key == GLFW_KEY_UNKNOWN)
2699         return NULL;
2700 
2701     const xkb_keycode_t keycode = scancode + 8;
2702     const xkb_layout_index_t layout =
2703         xkb_state_key_get_layout(_glfw.wl.xkb.state, keycode);
2704     if (layout == XKB_LAYOUT_INVALID)
2705     {
2706         _glfwInputError(GLFW_PLATFORM_ERROR,
2707                         "Wayland: Failed to retrieve layout for key name");
2708         return NULL;
2709     }
2710 
2711     const xkb_keysym_t* keysyms = NULL;
2712     xkb_keymap_key_get_syms_by_level(_glfw.wl.xkb.keymap,
2713                                      keycode,
2714                                      layout,
2715                                      0,
2716                                      &keysyms);
2717     if (keysyms == NULL)
2718     {
2719         _glfwInputError(GLFW_PLATFORM_ERROR,
2720                         "Wayland: Failed to retrieve keysym for key name");
2721         return NULL;
2722     }
2723 
2724     const uint32_t codepoint = _glfwKeySym2Unicode(keysyms[0]);
2725     if (codepoint == GLFW_INVALID_CODEPOINT)
2726     {
2727         _glfwInputError(GLFW_PLATFORM_ERROR,
2728                         "Wayland: Failed to retrieve codepoint for key name");
2729         return NULL;
2730     }
2731 
2732     const size_t count = _glfwEncodeUTF8(_glfw.wl.keynames[key],  codepoint);
2733     if (count == 0)
2734     {
2735         _glfwInputError(GLFW_PLATFORM_ERROR,
2736                         "Wayland: Failed to encode codepoint for key name");
2737         return NULL;
2738     }
2739 
2740     _glfw.wl.keynames[key][count] = '\0';
2741     return _glfw.wl.keynames[key];
2742 }
2743 
_glfwGetKeyScancodeWayland(int key)2744 int _glfwGetKeyScancodeWayland(int key)
2745 {
2746     return _glfw.wl.scancodes[key];
2747 }
2748 
_glfwCreateCursorWayland(_GLFWcursor * cursor,const GLFWimage * image,int xhot,int yhot)2749 GLFWbool _glfwCreateCursorWayland(_GLFWcursor* cursor,
2750                                   const GLFWimage* image,
2751                                   int xhot, int yhot)
2752 {
2753     cursor->wl.buffer = createShmBuffer(image);
2754     if (!cursor->wl.buffer)
2755         return GLFW_FALSE;
2756 
2757     cursor->wl.width = image->width;
2758     cursor->wl.height = image->height;
2759     cursor->wl.xhot = xhot;
2760     cursor->wl.yhot = yhot;
2761     return GLFW_TRUE;
2762 }
2763 
_glfwCreateStandardCursorWayland(_GLFWcursor * cursor,int shape)2764 GLFWbool _glfwCreateStandardCursorWayland(_GLFWcursor* cursor, int shape)
2765 {
2766     const char* name = NULL;
2767 
2768     // Try the XDG names first
2769     switch (shape)
2770     {
2771         case GLFW_ARROW_CURSOR:
2772             name = "default";
2773             break;
2774         case GLFW_IBEAM_CURSOR:
2775             name = "text";
2776             break;
2777         case GLFW_CROSSHAIR_CURSOR:
2778             name = "crosshair";
2779             break;
2780         case GLFW_POINTING_HAND_CURSOR:
2781             name = "pointer";
2782             break;
2783         case GLFW_RESIZE_EW_CURSOR:
2784             name = "ew-resize";
2785             break;
2786         case GLFW_RESIZE_NS_CURSOR:
2787             name = "ns-resize";
2788             break;
2789         case GLFW_RESIZE_NWSE_CURSOR:
2790             name = "nwse-resize";
2791             break;
2792         case GLFW_RESIZE_NESW_CURSOR:
2793             name = "nesw-resize";
2794             break;
2795         case GLFW_RESIZE_ALL_CURSOR:
2796             name = "all-scroll";
2797             break;
2798         case GLFW_NOT_ALLOWED_CURSOR:
2799             name = "not-allowed";
2800             break;
2801     }
2802 
2803     cursor->wl.cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name);
2804 
2805     if (_glfw.wl.cursorThemeHiDPI)
2806     {
2807         cursor->wl.cursorHiDPI =
2808             wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, name);
2809     }
2810 
2811     if (!cursor->wl.cursor)
2812     {
2813         // Fall back to the core X11 names
2814         switch (shape)
2815         {
2816             case GLFW_ARROW_CURSOR:
2817                 name = "left_ptr";
2818                 break;
2819             case GLFW_IBEAM_CURSOR:
2820                 name = "xterm";
2821                 break;
2822             case GLFW_CROSSHAIR_CURSOR:
2823                 name = "crosshair";
2824                 break;
2825             case GLFW_POINTING_HAND_CURSOR:
2826                 name = "hand2";
2827                 break;
2828             case GLFW_RESIZE_EW_CURSOR:
2829                 name = "sb_h_double_arrow";
2830                 break;
2831             case GLFW_RESIZE_NS_CURSOR:
2832                 name = "sb_v_double_arrow";
2833                 break;
2834             case GLFW_RESIZE_ALL_CURSOR:
2835                 name = "fleur";
2836                 break;
2837             default:
2838                 _glfwInputError(GLFW_CURSOR_UNAVAILABLE,
2839                                 "Wayland: Standard cursor shape unavailable");
2840                 return GLFW_FALSE;
2841         }
2842 
2843         cursor->wl.cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name);
2844         if (!cursor->wl.cursor)
2845         {
2846             _glfwInputError(GLFW_CURSOR_UNAVAILABLE,
2847                             "Wayland: Failed to create standard cursor \"%s\"",
2848                             name);
2849             return GLFW_FALSE;
2850         }
2851 
2852         if (_glfw.wl.cursorThemeHiDPI)
2853         {
2854             if (!cursor->wl.cursorHiDPI)
2855             {
2856                 cursor->wl.cursorHiDPI =
2857                     wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, name);
2858             }
2859         }
2860     }
2861 
2862     return GLFW_TRUE;
2863 }
2864 
_glfwDestroyCursorWayland(_GLFWcursor * cursor)2865 void _glfwDestroyCursorWayland(_GLFWcursor* cursor)
2866 {
2867     // If it's a standard cursor we don't need to do anything here
2868     if (cursor->wl.cursor)
2869         return;
2870 
2871     if (cursor->wl.buffer)
2872         wl_buffer_destroy(cursor->wl.buffer);
2873 }
2874 
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)2875 static void relativePointerHandleRelativeMotion(void* userData,
2876                                                 struct zwp_relative_pointer_v1* pointer,
2877                                                 uint32_t timeHi,
2878                                                 uint32_t timeLo,
2879                                                 wl_fixed_t dx,
2880                                                 wl_fixed_t dy,
2881                                                 wl_fixed_t dxUnaccel,
2882                                                 wl_fixed_t dyUnaccel)
2883 {
2884     _GLFWwindow* window = userData;
2885     double xpos = window->virtualCursorPosX;
2886     double ypos = window->virtualCursorPosY;
2887 
2888     if (window->cursorMode != GLFW_CURSOR_DISABLED)
2889         return;
2890 
2891     if (window->rawMouseMotion)
2892     {
2893         xpos += wl_fixed_to_double(dxUnaccel);
2894         ypos += wl_fixed_to_double(dyUnaccel);
2895     }
2896     else
2897     {
2898         xpos += wl_fixed_to_double(dx);
2899         ypos += wl_fixed_to_double(dy);
2900     }
2901 
2902     _glfwInputCursorPos(window, xpos, ypos);
2903 }
2904 
2905 static const struct zwp_relative_pointer_v1_listener relativePointerListener =
2906 {
2907     relativePointerHandleRelativeMotion
2908 };
2909 
lockedPointerHandleLocked(void * userData,struct zwp_locked_pointer_v1 * lockedPointer)2910 static void lockedPointerHandleLocked(void* userData,
2911                                       struct zwp_locked_pointer_v1* lockedPointer)
2912 {
2913 }
2914 
lockedPointerHandleUnlocked(void * userData,struct zwp_locked_pointer_v1 * lockedPointer)2915 static void lockedPointerHandleUnlocked(void* userData,
2916                                         struct zwp_locked_pointer_v1* lockedPointer)
2917 {
2918 }
2919 
2920 static const struct zwp_locked_pointer_v1_listener lockedPointerListener =
2921 {
2922     lockedPointerHandleLocked,
2923     lockedPointerHandleUnlocked
2924 };
2925 
lockPointer(_GLFWwindow * window)2926 static void lockPointer(_GLFWwindow* window)
2927 {
2928     if (!_glfw.wl.relativePointerManager)
2929     {
2930         _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2931                         "Wayland: The compositor does not support pointer locking");
2932         return;
2933     }
2934 
2935     window->wl.relativePointer =
2936         zwp_relative_pointer_manager_v1_get_relative_pointer(
2937             _glfw.wl.relativePointerManager,
2938             _glfw.wl.pointer);
2939     zwp_relative_pointer_v1_add_listener(window->wl.relativePointer,
2940                                          &relativePointerListener,
2941                                          window);
2942 
2943     window->wl.lockedPointer =
2944         zwp_pointer_constraints_v1_lock_pointer(
2945             _glfw.wl.pointerConstraints,
2946             window->wl.surface,
2947             _glfw.wl.pointer,
2948             NULL,
2949             ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
2950     zwp_locked_pointer_v1_add_listener(window->wl.lockedPointer,
2951                                        &lockedPointerListener,
2952                                        window);
2953 }
2954 
unlockPointer(_GLFWwindow * window)2955 static void unlockPointer(_GLFWwindow* window)
2956 {
2957     zwp_relative_pointer_v1_destroy(window->wl.relativePointer);
2958     window->wl.relativePointer = NULL;
2959 
2960     zwp_locked_pointer_v1_destroy(window->wl.lockedPointer);
2961     window->wl.lockedPointer = NULL;
2962 }
2963 
confinedPointerHandleConfined(void * userData,struct zwp_confined_pointer_v1 * confinedPointer)2964 static void confinedPointerHandleConfined(void* userData,
2965                                           struct zwp_confined_pointer_v1* confinedPointer)
2966 {
2967 }
2968 
confinedPointerHandleUnconfined(void * userData,struct zwp_confined_pointer_v1 * confinedPointer)2969 static void confinedPointerHandleUnconfined(void* userData,
2970                                             struct zwp_confined_pointer_v1* confinedPointer)
2971 {
2972 }
2973 
2974 static const struct zwp_confined_pointer_v1_listener confinedPointerListener =
2975 {
2976     confinedPointerHandleConfined,
2977     confinedPointerHandleUnconfined
2978 };
2979 
confinePointer(_GLFWwindow * window)2980 static void confinePointer(_GLFWwindow* window)
2981 {
2982     window->wl.confinedPointer =
2983         zwp_pointer_constraints_v1_confine_pointer(
2984             _glfw.wl.pointerConstraints,
2985             window->wl.surface,
2986             _glfw.wl.pointer,
2987             NULL,
2988             ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
2989 
2990     zwp_confined_pointer_v1_add_listener(window->wl.confinedPointer,
2991                                          &confinedPointerListener,
2992                                          window);
2993 }
2994 
unconfinePointer(_GLFWwindow * window)2995 static void unconfinePointer(_GLFWwindow* window)
2996 {
2997     zwp_confined_pointer_v1_destroy(window->wl.confinedPointer);
2998     window->wl.confinedPointer = NULL;
2999 }
3000 
_glfwSetCursorWayland(_GLFWwindow * window,_GLFWcursor * cursor)3001 void _glfwSetCursorWayland(_GLFWwindow* window, _GLFWcursor* cursor)
3002 {
3003     if (!_glfw.wl.pointer)
3004         return;
3005 
3006     window->wl.currentCursor = cursor;
3007 
3008     // If we're not in the correct window just save the cursor
3009     // the next time the pointer enters the window the cursor will change
3010     if (!window->wl.hovered)
3011         return;
3012 
3013     // Update pointer lock to match cursor mode
3014     if (window->cursorMode == GLFW_CURSOR_DISABLED)
3015     {
3016         if (window->wl.confinedPointer)
3017             unconfinePointer(window);
3018         if (!window->wl.lockedPointer)
3019             lockPointer(window);
3020     }
3021     else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
3022     {
3023         if (window->wl.lockedPointer)
3024             unlockPointer(window);
3025         if (!window->wl.confinedPointer)
3026             confinePointer(window);
3027     }
3028     else if (window->cursorMode == GLFW_CURSOR_NORMAL ||
3029              window->cursorMode == GLFW_CURSOR_HIDDEN)
3030     {
3031         if (window->wl.lockedPointer)
3032             unlockPointer(window);
3033         else if (window->wl.confinedPointer)
3034             unconfinePointer(window);
3035     }
3036 
3037     if (window->cursorMode == GLFW_CURSOR_NORMAL ||
3038         window->cursorMode == GLFW_CURSOR_CAPTURED)
3039     {
3040         if (cursor)
3041             setCursorImage(window, &cursor->wl);
3042         else
3043         {
3044             struct wl_cursor* defaultCursor =
3045                 wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, "left_ptr");
3046             if (!defaultCursor)
3047             {
3048                 _glfwInputError(GLFW_PLATFORM_ERROR,
3049                                 "Wayland: Standard cursor not found");
3050                 return;
3051             }
3052 
3053             struct wl_cursor* defaultCursorHiDPI = NULL;
3054             if (_glfw.wl.cursorThemeHiDPI)
3055             {
3056                 defaultCursorHiDPI =
3057                     wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, "left_ptr");
3058             }
3059 
3060             _GLFWcursorWayland cursorWayland =
3061             {
3062                 defaultCursor,
3063                 defaultCursorHiDPI,
3064                 NULL,
3065                 0, 0,
3066                 0, 0,
3067                 0
3068             };
3069 
3070             setCursorImage(window, &cursorWayland);
3071         }
3072     }
3073     else if (window->cursorMode == GLFW_CURSOR_HIDDEN ||
3074              window->cursorMode == GLFW_CURSOR_DISABLED)
3075     {
3076         wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial, NULL, 0, 0);
3077     }
3078 }
3079 
dataSourceHandleTarget(void * userData,struct wl_data_source * source,const char * mimeType)3080 static void dataSourceHandleTarget(void* userData,
3081                                    struct wl_data_source* source,
3082                                    const char* mimeType)
3083 {
3084     if (_glfw.wl.selectionSource != source)
3085     {
3086         _glfwInputError(GLFW_PLATFORM_ERROR,
3087                         "Wayland: Unknown clipboard data source");
3088         return;
3089     }
3090 }
3091 
dataSourceHandleSend(void * userData,struct wl_data_source * source,const char * mimeType,int fd)3092 static void dataSourceHandleSend(void* userData,
3093                                  struct wl_data_source* source,
3094                                  const char* mimeType,
3095                                  int fd)
3096 {
3097     // Ignore it if this is an outdated or invalid request
3098     if (_glfw.wl.selectionSource != source ||
3099         strcmp(mimeType, "text/plain;charset=utf-8") != 0)
3100     {
3101         close(fd);
3102         return;
3103     }
3104 
3105     char* string = _glfw.wl.clipboardString;
3106     size_t length = strlen(string);
3107 
3108     while (length > 0)
3109     {
3110         const ssize_t result = write(fd, string, length);
3111         if (result == -1)
3112         {
3113             if (errno == EINTR)
3114                 continue;
3115 
3116             _glfwInputError(GLFW_PLATFORM_ERROR,
3117                             "Wayland: Error while writing the clipboard: %s",
3118                             strerror(errno));
3119             break;
3120         }
3121 
3122         length -= result;
3123         string += result;
3124     }
3125 
3126     close(fd);
3127 }
3128 
dataSourceHandleCancelled(void * userData,struct wl_data_source * source)3129 static void dataSourceHandleCancelled(void* userData,
3130                                       struct wl_data_source* source)
3131 {
3132     wl_data_source_destroy(source);
3133 
3134     if (_glfw.wl.selectionSource != source)
3135         return;
3136 
3137     _glfw.wl.selectionSource = NULL;
3138 }
3139 
3140 static const struct wl_data_source_listener dataSourceListener =
3141 {
3142     dataSourceHandleTarget,
3143     dataSourceHandleSend,
3144     dataSourceHandleCancelled,
3145 };
3146 
_glfwSetClipboardStringWayland(const char * string)3147 void _glfwSetClipboardStringWayland(const char* string)
3148 {
3149     if (_glfw.wl.selectionSource)
3150     {
3151         wl_data_source_destroy(_glfw.wl.selectionSource);
3152         _glfw.wl.selectionSource = NULL;
3153     }
3154 
3155     char* copy = _glfw_strdup(string);
3156     if (!copy)
3157     {
3158         _glfwInputError(GLFW_OUT_OF_MEMORY, NULL);
3159         return;
3160     }
3161 
3162     _glfw_free(_glfw.wl.clipboardString);
3163     _glfw.wl.clipboardString = copy;
3164 
3165     _glfw.wl.selectionSource =
3166         wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager);
3167     if (!_glfw.wl.selectionSource)
3168     {
3169         _glfwInputError(GLFW_PLATFORM_ERROR,
3170                         "Wayland: Failed to create clipboard data source");
3171         return;
3172     }
3173     wl_data_source_add_listener(_glfw.wl.selectionSource,
3174                                 &dataSourceListener,
3175                                 NULL);
3176     wl_data_source_offer(_glfw.wl.selectionSource, "text/plain;charset=utf-8");
3177     wl_data_device_set_selection(_glfw.wl.dataDevice,
3178                                  _glfw.wl.selectionSource,
3179                                  _glfw.wl.serial);
3180 }
3181 
_glfwGetClipboardStringWayland(void)3182 const char* _glfwGetClipboardStringWayland(void)
3183 {
3184     if (!_glfw.wl.selectionOffer)
3185     {
3186         _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
3187                         "Wayland: No clipboard data available");
3188         return NULL;
3189     }
3190 
3191     if (_glfw.wl.selectionSource)
3192         return _glfw.wl.clipboardString;
3193 
3194     _glfw_free(_glfw.wl.clipboardString);
3195     _glfw.wl.clipboardString =
3196         readDataOfferAsString(_glfw.wl.selectionOffer, "text/plain;charset=utf-8");
3197     return _glfw.wl.clipboardString;
3198 }
3199 
_glfwGetEGLPlatformWayland(EGLint ** attribs)3200 EGLenum _glfwGetEGLPlatformWayland(EGLint** attribs)
3201 {
3202     if (_glfw.egl.EXT_platform_base && _glfw.egl.EXT_platform_wayland)
3203         return EGL_PLATFORM_WAYLAND_EXT;
3204     else
3205         return 0;
3206 }
3207 
_glfwGetEGLNativeDisplayWayland(void)3208 EGLNativeDisplayType _glfwGetEGLNativeDisplayWayland(void)
3209 {
3210     return _glfw.wl.display;
3211 }
3212 
_glfwGetEGLNativeWindowWayland(_GLFWwindow * window)3213 EGLNativeWindowType _glfwGetEGLNativeWindowWayland(_GLFWwindow* window)
3214 {
3215     return window->wl.egl.window;
3216 }
3217 
_glfwGetRequiredInstanceExtensionsWayland(char ** extensions)3218 void _glfwGetRequiredInstanceExtensionsWayland(char** extensions)
3219 {
3220     if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_wayland_surface)
3221         return;
3222 
3223     extensions[0] = "VK_KHR_surface";
3224     extensions[1] = "VK_KHR_wayland_surface";
3225 }
3226 
_glfwGetPhysicalDevicePresentationSupportWayland(VkInstance instance,VkPhysicalDevice device,uint32_t queuefamily)3227 GLFWbool _glfwGetPhysicalDevicePresentationSupportWayland(VkInstance instance,
3228                                                           VkPhysicalDevice device,
3229                                                           uint32_t queuefamily)
3230 {
3231     PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
3232         vkGetPhysicalDeviceWaylandPresentationSupportKHR =
3233         (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)
3234         vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR");
3235     if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR)
3236     {
3237         _glfwInputError(GLFW_API_UNAVAILABLE,
3238                         "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension");
3239         return VK_NULL_HANDLE;
3240     }
3241 
3242     return vkGetPhysicalDeviceWaylandPresentationSupportKHR(device,
3243                                                             queuefamily,
3244                                                             _glfw.wl.display);
3245 }
3246 
_glfwCreateWindowSurfaceWayland(VkInstance instance,_GLFWwindow * window,const VkAllocationCallbacks * allocator,VkSurfaceKHR * surface)3247 VkResult _glfwCreateWindowSurfaceWayland(VkInstance instance,
3248                                          _GLFWwindow* window,
3249                                          const VkAllocationCallbacks* allocator,
3250                                          VkSurfaceKHR* surface)
3251 {
3252     VkResult err;
3253     VkWaylandSurfaceCreateInfoKHR sci;
3254     PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR;
3255 
3256     vkCreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR)
3257         vkGetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR");
3258     if (!vkCreateWaylandSurfaceKHR)
3259     {
3260         _glfwInputError(GLFW_API_UNAVAILABLE,
3261                         "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension");
3262         return VK_ERROR_EXTENSION_NOT_PRESENT;
3263     }
3264 
3265     memset(&sci, 0, sizeof(sci));
3266     sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
3267     sci.display = _glfw.wl.display;
3268     sci.surface = window->wl.surface;
3269 
3270     err = vkCreateWaylandSurfaceKHR(instance, &sci, allocator, surface);
3271     if (err)
3272     {
3273         _glfwInputError(GLFW_PLATFORM_ERROR,
3274                         "Wayland: Failed to create Vulkan surface: %s",
3275                         _glfwGetVulkanResultString(err));
3276     }
3277 
3278     return err;
3279 }
3280 
3281 
3282 //////////////////////////////////////////////////////////////////////////
3283 //////                        GLFW native API                       //////
3284 //////////////////////////////////////////////////////////////////////////
3285 
glfwGetWaylandDisplay(void)3286 GLFWAPI struct wl_display* glfwGetWaylandDisplay(void)
3287 {
3288     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
3289 
3290     if (_glfw.platform.platformID != GLFW_PLATFORM_WAYLAND)
3291     {
3292         _glfwInputError(GLFW_PLATFORM_UNAVAILABLE,
3293                         "Wayland: Platform not initialized");
3294         return NULL;
3295     }
3296 
3297     return _glfw.wl.display;
3298 }
3299 
glfwGetWaylandWindow(GLFWwindow * handle)3300 GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle)
3301 {
3302     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
3303 
3304     if (_glfw.platform.platformID != GLFW_PLATFORM_WAYLAND)
3305     {
3306         _glfwInputError(GLFW_PLATFORM_UNAVAILABLE,
3307                         "Wayland: Platform not initialized");
3308         return NULL;
3309     }
3310 
3311     _GLFWwindow* window = (_GLFWwindow*) handle;
3312     assert(window != NULL);
3313 
3314     return window->wl.surface;
3315 }
3316 
3317 #endif // _GLFW_WAYLAND
3318 
3319