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