• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //========================================================================
2 // GLFW 3.2 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 <poll.h>
39 
40 #include <wayland-egl.h>
41 #include <wayland-cursor.h>
42 
43 
handlePing(void * data,struct wl_shell_surface * shellSurface,uint32_t serial)44 static void handlePing(void* data,
45                        struct wl_shell_surface* shellSurface,
46                        uint32_t serial)
47 {
48     wl_shell_surface_pong(shellSurface, serial);
49 }
50 
handleConfigure(void * data,struct wl_shell_surface * shellSurface,uint32_t edges,int32_t width,int32_t height)51 static void handleConfigure(void* data,
52                             struct wl_shell_surface* shellSurface,
53                             uint32_t edges,
54                             int32_t width,
55                             int32_t height)
56 {
57     _GLFWwindow* window = data;
58     float aspectRatio;
59     float targetRatio;
60 
61     if (!window->monitor)
62     {
63         if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)
64         {
65             aspectRatio = (float)width / (float)height;
66             targetRatio = (float)window->numer / (float)window->denom;
67             if (aspectRatio < targetRatio)
68                 height = width / targetRatio;
69             else if (aspectRatio > targetRatio)
70                 width = height * targetRatio;
71         }
72 
73         if (window->minwidth != GLFW_DONT_CARE && width < window->minwidth)
74             width = window->minwidth;
75         else if (window->maxwidth != GLFW_DONT_CARE && width > window->maxwidth)
76             width = window->maxwidth;
77 
78         if (window->minheight != GLFW_DONT_CARE && height < window->minheight)
79             height = window->minheight;
80         else if (window->maxheight != GLFW_DONT_CARE && height > window->maxheight)
81             height = window->maxheight;
82     }
83 
84     _glfwInputWindowSize(window, width, height);
85     _glfwPlatformSetWindowSize(window, width, height);
86     _glfwInputWindowDamage(window);
87 }
88 
handlePopupDone(void * data,struct wl_shell_surface * shellSurface)89 static void handlePopupDone(void* data,
90                             struct wl_shell_surface* shellSurface)
91 {
92 }
93 
94 static const struct wl_shell_surface_listener shellSurfaceListener = {
95     handlePing,
96     handleConfigure,
97     handlePopupDone
98 };
99 
checkScaleChange(_GLFWwindow * window)100 static void checkScaleChange(_GLFWwindow* window)
101 {
102     int scaledWidth, scaledHeight;
103     int scale = 1;
104     int i;
105     int monitorScale;
106 
107     // Check if we will be able to set the buffer scale or not.
108     if (_glfw.wl.wl_compositor_version < 3)
109         return;
110 
111     // Get the scale factor from the highest scale monitor.
112     for (i = 0; i < window->wl.monitorsCount; ++i)
113     {
114         monitorScale = window->wl.monitors[i]->wl.scale;
115         if (scale < monitorScale)
116             scale = monitorScale;
117     }
118 
119     // Only change the framebuffer size if the scale changed.
120     if (scale != window->wl.scale)
121     {
122         window->wl.scale = scale;
123         scaledWidth = window->wl.width * scale;
124         scaledHeight = window->wl.height * scale;
125         wl_surface_set_buffer_scale(window->wl.surface, scale);
126         wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
127         _glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
128     }
129 }
130 
handleEnter(void * data,struct wl_surface * surface,struct wl_output * output)131 static void handleEnter(void *data,
132                         struct wl_surface *surface,
133                         struct wl_output *output)
134 {
135     _GLFWwindow* window = data;
136     _GLFWmonitor* monitor = wl_output_get_user_data(output);
137 
138     if (window->wl.monitorsCount + 1 > window->wl.monitorsSize)
139     {
140         ++window->wl.monitorsSize;
141         window->wl.monitors =
142             realloc(window->wl.monitors,
143                     window->wl.monitorsSize * sizeof(_GLFWmonitor*));
144     }
145 
146     window->wl.monitors[window->wl.monitorsCount++] = monitor;
147 
148     checkScaleChange(window);
149 }
150 
handleLeave(void * data,struct wl_surface * surface,struct wl_output * output)151 static void handleLeave(void *data,
152                         struct wl_surface *surface,
153                         struct wl_output *output)
154 {
155     _GLFWwindow* window = data;
156     _GLFWmonitor* monitor = wl_output_get_user_data(output);
157     GLFWbool found;
158     int i;
159 
160     for (i = 0, found = GLFW_FALSE; i < window->wl.monitorsCount - 1; ++i)
161     {
162         if (monitor == window->wl.monitors[i])
163             found = GLFW_TRUE;
164         if (found)
165             window->wl.monitors[i] = window->wl.monitors[i + 1];
166     }
167     window->wl.monitors[--window->wl.monitorsCount] = NULL;
168 
169     checkScaleChange(window);
170 }
171 
172 static const struct wl_surface_listener surfaceListener = {
173     handleEnter,
174     handleLeave
175 };
176 
177 // Makes the surface considered as XRGB instead of ARGB.
setOpaqueRegion(_GLFWwindow * window)178 static void setOpaqueRegion(_GLFWwindow* window)
179 {
180     struct wl_region* region;
181 
182     region = wl_compositor_create_region(_glfw.wl.compositor);
183     if (!region)
184         return;
185 
186     wl_region_add(region, 0, 0, window->wl.width, window->wl.height);
187     wl_surface_set_opaque_region(window->wl.surface, region);
188     wl_surface_commit(window->wl.surface);
189     wl_region_destroy(region);
190 }
191 
createSurface(_GLFWwindow * window,const _GLFWwndconfig * wndconfig)192 static GLFWbool createSurface(_GLFWwindow* window,
193                               const _GLFWwndconfig* wndconfig)
194 {
195     window->wl.surface = wl_compositor_create_surface(_glfw.wl.compositor);
196     if (!window->wl.surface)
197         return GLFW_FALSE;
198 
199     wl_surface_add_listener(window->wl.surface,
200                             &surfaceListener,
201                             window);
202 
203     wl_surface_set_user_data(window->wl.surface, window);
204 
205     window->wl.native = wl_egl_window_create(window->wl.surface,
206                                              wndconfig->width,
207                                              wndconfig->height);
208     if (!window->wl.native)
209         return GLFW_FALSE;
210 
211     window->wl.width = wndconfig->width;
212     window->wl.height = wndconfig->height;
213     window->wl.scale = 1;
214 
215     // TODO: make this optional once issue #197 is fixed.
216     setOpaqueRegion(window);
217 
218     return GLFW_TRUE;
219 }
220 
createShellSurface(_GLFWwindow * window)221 static GLFWbool createShellSurface(_GLFWwindow* window)
222 {
223     window->wl.shell_surface = wl_shell_get_shell_surface(_glfw.wl.shell,
224                                                           window->wl.surface);
225     if (!window->wl.shell_surface)
226         return GLFW_FALSE;
227 
228     wl_shell_surface_add_listener(window->wl.shell_surface,
229                                   &shellSurfaceListener,
230                                   window);
231 
232     if (window->wl.title)
233         wl_shell_surface_set_title(window->wl.shell_surface, window->wl.title);
234 
235     if (window->monitor)
236     {
237         wl_shell_surface_set_fullscreen(
238             window->wl.shell_surface,
239             WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
240             0,
241             window->monitor->wl.output);
242     }
243     else if (window->wl.maximized)
244     {
245         wl_shell_surface_set_maximized(window->wl.shell_surface, NULL);
246     }
247     else
248     {
249         wl_shell_surface_set_toplevel(window->wl.shell_surface);
250     }
251 
252     return GLFW_TRUE;
253 }
254 
255 static int
createTmpfileCloexec(char * tmpname)256 createTmpfileCloexec(char* tmpname)
257 {
258     int fd;
259 
260     fd = mkostemp(tmpname, O_CLOEXEC);
261     if (fd >= 0)
262         unlink(tmpname);
263 
264     return fd;
265 }
266 
267 static void
handleEvents(int timeout)268 handleEvents(int timeout)
269 {
270     struct wl_display* display = _glfw.wl.display;
271     struct pollfd fds[] = {
272         { wl_display_get_fd(display), POLLIN },
273     };
274 
275     while (wl_display_prepare_read(display) != 0)
276         wl_display_dispatch_pending(display);
277 
278     // If an error different from EAGAIN happens, we have likely been
279     // disconnected from the Wayland session, try to handle that the best we
280     // can.
281     if (wl_display_flush(display) < 0 && errno != EAGAIN)
282     {
283         _GLFWwindow* window = _glfw.windowListHead;
284         while (window)
285         {
286             _glfwInputWindowCloseRequest(window);
287             window = window->next;
288         }
289         wl_display_cancel_read(display);
290         return;
291     }
292 
293     if (poll(fds, 1, timeout) > 0)
294     {
295         wl_display_read_events(display);
296         wl_display_dispatch_pending(display);
297     }
298     else
299     {
300         wl_display_cancel_read(display);
301     }
302 }
303 
304 /*
305  * Create a new, unique, anonymous file of the given size, and
306  * return the file descriptor for it. The file descriptor is set
307  * CLOEXEC. The file is immediately suitable for mmap()'ing
308  * the given size at offset zero.
309  *
310  * The file should not have a permanent backing store like a disk,
311  * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
312  *
313  * The file name is deleted from the file system.
314  *
315  * The file is suitable for buffer sharing between processes by
316  * transmitting the file descriptor over Unix sockets using the
317  * SCM_RIGHTS methods.
318  *
319  * posix_fallocate() is used to guarantee that disk space is available
320  * for the file at the given size. If disk space is insufficent, errno
321  * is set to ENOSPC. If posix_fallocate() is not supported, program may
322  * receive SIGBUS on accessing mmap()'ed file contents instead.
323  */
324 int
createAnonymousFile(off_t size)325 createAnonymousFile(off_t size)
326 {
327     static const char template[] = "/glfw-shared-XXXXXX";
328     const char* path;
329     char* name;
330     int fd;
331     int ret;
332 
333     path = getenv("XDG_RUNTIME_DIR");
334     if (!path)
335     {
336         errno = ENOENT;
337         return -1;
338     }
339 
340     name = calloc(strlen(path) + sizeof(template), 1);
341     strcpy(name, path);
342     strcat(name, template);
343 
344     fd = createTmpfileCloexec(name);
345 
346     free(name);
347 
348     if (fd < 0)
349         return -1;
350     ret = posix_fallocate(fd, 0, size);
351     if (ret != 0)
352     {
353         close(fd);
354         errno = ret;
355         return -1;
356     }
357     return fd;
358 }
359 
360 // Translates a GLFW standard cursor to a theme cursor name
361 //
translateCursorShape(int shape)362 static char *translateCursorShape(int shape)
363 {
364     switch (shape)
365     {
366         case GLFW_ARROW_CURSOR:
367             return "left_ptr";
368         case GLFW_IBEAM_CURSOR:
369             return "xterm";
370         case GLFW_CROSSHAIR_CURSOR:
371             return "crosshair";
372         case GLFW_HAND_CURSOR:
373             return "grabbing";
374         case GLFW_HRESIZE_CURSOR:
375             return "sb_h_double_arrow";
376         case GLFW_VRESIZE_CURSOR:
377             return "sb_v_double_arrow";
378     }
379     return NULL;
380 }
381 
382 //////////////////////////////////////////////////////////////////////////
383 //////                       GLFW platform API                      //////
384 //////////////////////////////////////////////////////////////////////////
385 
_glfwPlatformCreateWindow(_GLFWwindow * window,const _GLFWwndconfig * wndconfig,const _GLFWctxconfig * ctxconfig,const _GLFWfbconfig * fbconfig)386 int _glfwPlatformCreateWindow(_GLFWwindow* window,
387                               const _GLFWwndconfig* wndconfig,
388                               const _GLFWctxconfig* ctxconfig,
389                               const _GLFWfbconfig* fbconfig)
390 {
391     if (!createSurface(window, wndconfig))
392         return GLFW_FALSE;
393 
394     if (ctxconfig->client != GLFW_NO_API)
395     {
396         if (!_glfwInitEGL())
397             return GLFW_FALSE;
398         if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
399             return GLFW_FALSE;
400     }
401 
402     if (wndconfig->title)
403         window->wl.title = strdup(wndconfig->title);
404 
405     if (wndconfig->visible)
406     {
407         if (!createShellSurface(window))
408             return GLFW_FALSE;
409 
410         window->wl.visible = GLFW_TRUE;
411     }
412     else
413     {
414         window->wl.shell_surface = NULL;
415         window->wl.visible = GLFW_FALSE;
416     }
417 
418     window->wl.currentCursor = NULL;
419 
420     window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*));
421     window->wl.monitorsCount = 0;
422     window->wl.monitorsSize = 1;
423 
424     return GLFW_TRUE;
425 }
426 
_glfwPlatformDestroyWindow(_GLFWwindow * window)427 void _glfwPlatformDestroyWindow(_GLFWwindow* window)
428 {
429     if (window == _glfw.wl.pointerFocus)
430     {
431         _glfw.wl.pointerFocus = NULL;
432         _glfwInputCursorEnter(window, GLFW_FALSE);
433     }
434     if (window == _glfw.wl.keyboardFocus)
435     {
436         _glfw.wl.keyboardFocus = NULL;
437         _glfwInputWindowFocus(window, GLFW_FALSE);
438     }
439 
440     if (window->context.destroy)
441         window->context.destroy(window);
442 
443     if (window->wl.native)
444         wl_egl_window_destroy(window->wl.native);
445 
446     if (window->wl.shell_surface)
447         wl_shell_surface_destroy(window->wl.shell_surface);
448 
449     if (window->wl.surface)
450         wl_surface_destroy(window->wl.surface);
451 
452     free(window->wl.title);
453     free(window->wl.monitors);
454 }
455 
_glfwPlatformSetWindowTitle(_GLFWwindow * window,const char * title)456 void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
457 {
458     if (window->wl.title)
459         free(window->wl.title);
460     window->wl.title = strdup(title);
461     if (window->wl.shell_surface)
462         wl_shell_surface_set_title(window->wl.shell_surface, title);
463 }
464 
_glfwPlatformSetWindowIcon(_GLFWwindow * window,int count,const GLFWimage * images)465 void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
466                                 int count, const GLFWimage* images)
467 {
468     _glfwInputError(GLFW_PLATFORM_ERROR,
469                     "Wayland: Setting window icon not supported");
470 }
471 
_glfwPlatformGetWindowPos(_GLFWwindow * window,int * xpos,int * ypos)472 void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
473 {
474     // A Wayland client is not aware of its position, so just warn and leave it
475     // as (0, 0)
476 
477     _glfwInputError(GLFW_PLATFORM_ERROR,
478                     "Wayland: Window position retrieval not supported");
479 }
480 
_glfwPlatformSetWindowPos(_GLFWwindow * window,int xpos,int ypos)481 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
482 {
483     // A Wayland client can not set its position, so just warn
484 
485     _glfwInputError(GLFW_PLATFORM_ERROR,
486                     "Wayland: Window position setting not supported");
487 }
488 
_glfwPlatformGetWindowSize(_GLFWwindow * window,int * width,int * height)489 void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
490 {
491     if (width)
492         *width = window->wl.width;
493     if (height)
494         *height = window->wl.height;
495 }
496 
_glfwPlatformSetWindowSize(_GLFWwindow * window,int width,int height)497 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
498 {
499     int scaledWidth = width * window->wl.scale;
500     int scaledHeight = height * window->wl.scale;
501     window->wl.width = width;
502     window->wl.height = height;
503     wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
504     setOpaqueRegion(window);
505     _glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
506 }
507 
_glfwPlatformSetWindowSizeLimits(_GLFWwindow * window,int minwidth,int minheight,int maxwidth,int maxheight)508 void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
509                                       int minwidth, int minheight,
510                                       int maxwidth, int maxheight)
511 {
512     // TODO: find out how to trigger a resize.
513     // The actual limits are checked in the wl_shell_surface::configure handler.
514 }
515 
_glfwPlatformSetWindowAspectRatio(_GLFWwindow * window,int numer,int denom)516 void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)
517 {
518     // TODO: find out how to trigger a resize.
519     // The actual limits are checked in the wl_shell_surface::configure handler.
520 }
521 
_glfwPlatformGetFramebufferSize(_GLFWwindow * window,int * width,int * height)522 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
523 {
524     _glfwPlatformGetWindowSize(window, width, height);
525     *width *= window->wl.scale;
526     *height *= window->wl.scale;
527 }
528 
_glfwPlatformGetWindowFrameSize(_GLFWwindow * window,int * left,int * top,int * right,int * bottom)529 void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
530                                      int* left, int* top,
531                                      int* right, int* bottom)
532 {
533     // TODO: will need a proper implementation once decorations are
534     // implemented, but for now just leave everything as 0.
535 }
536 
_glfwPlatformIconifyWindow(_GLFWwindow * window)537 void _glfwPlatformIconifyWindow(_GLFWwindow* window)
538 {
539     // TODO: move to xdg_shell instead of wl_shell.
540     _glfwInputError(GLFW_PLATFORM_ERROR,
541                     "Wayland: Iconify window not supported");
542 }
543 
_glfwPlatformRestoreWindow(_GLFWwindow * window)544 void _glfwPlatformRestoreWindow(_GLFWwindow* window)
545 {
546     // TODO: also do the same for iconified.
547     if (window->monitor || window->wl.maximized)
548     {
549         if (window->wl.shell_surface)
550             wl_shell_surface_set_toplevel(window->wl.shell_surface);
551 
552         window->wl.maximized = GLFW_FALSE;
553     }
554 }
555 
_glfwPlatformMaximizeWindow(_GLFWwindow * window)556 void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
557 {
558     if (!window->monitor && !window->wl.maximized)
559     {
560         if (window->wl.shell_surface)
561         {
562             // Let the compositor select the best output.
563             wl_shell_surface_set_maximized(window->wl.shell_surface, NULL);
564         }
565         window->wl.maximized = GLFW_TRUE;
566     }
567 }
568 
_glfwPlatformShowWindow(_GLFWwindow * window)569 void _glfwPlatformShowWindow(_GLFWwindow* window)
570 {
571     if (!window->monitor)
572     {
573         if (!window->wl.shell_surface)
574             createShellSurface(window);
575         window->wl.visible = GLFW_TRUE;
576     }
577 }
578 
_glfwPlatformHideWindow(_GLFWwindow * window)579 void _glfwPlatformHideWindow(_GLFWwindow* window)
580 {
581     if (!window->monitor)
582     {
583         if (window->wl.shell_surface)
584             wl_shell_surface_destroy(window->wl.shell_surface);
585         window->wl.visible = GLFW_FALSE;
586     }
587 }
588 
_glfwPlatformFocusWindow(_GLFWwindow * window)589 void _glfwPlatformFocusWindow(_GLFWwindow* window)
590 {
591     _glfwInputError(GLFW_PLATFORM_ERROR,
592                     "Wayland: Focusing a window requires user interaction");
593 }
594 
_glfwPlatformSetWindowMonitor(_GLFWwindow * window,_GLFWmonitor * monitor,int xpos,int ypos,int width,int height,int refreshRate)595 void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
596                                    _GLFWmonitor* monitor,
597                                    int xpos, int ypos,
598                                    int width, int height,
599                                    int refreshRate)
600 {
601     if (monitor)
602     {
603         wl_shell_surface_set_fullscreen(
604             window->wl.shell_surface,
605             WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
606             refreshRate * 1000, // Convert Hz to mHz.
607             monitor->wl.output);
608     }
609     else
610     {
611         wl_shell_surface_set_toplevel(window->wl.shell_surface);
612     }
613     _glfwInputWindowMonitorChange(window, monitor);
614 }
615 
_glfwPlatformWindowFocused(_GLFWwindow * window)616 int _glfwPlatformWindowFocused(_GLFWwindow* window)
617 {
618     return _glfw.wl.keyboardFocus == window;
619 }
620 
_glfwPlatformWindowIconified(_GLFWwindow * window)621 int _glfwPlatformWindowIconified(_GLFWwindow* window)
622 {
623     // TODO: move to xdg_shell, wl_shell doesn't have any iconified concept.
624     return GLFW_FALSE;
625 }
626 
_glfwPlatformWindowVisible(_GLFWwindow * window)627 int _glfwPlatformWindowVisible(_GLFWwindow* window)
628 {
629     return window->wl.visible;
630 }
631 
_glfwPlatformWindowMaximized(_GLFWwindow * window)632 int _glfwPlatformWindowMaximized(_GLFWwindow* window)
633 {
634     return window->wl.maximized;
635 }
636 
_glfwPlatformPollEvents(void)637 void _glfwPlatformPollEvents(void)
638 {
639     handleEvents(0);
640 }
641 
_glfwPlatformWaitEvents(void)642 void _glfwPlatformWaitEvents(void)
643 {
644     handleEvents(-1);
645 }
646 
_glfwPlatformWaitEventsTimeout(double timeout)647 void _glfwPlatformWaitEventsTimeout(double timeout)
648 {
649     handleEvents((int) (timeout * 1e3));
650 }
651 
_glfwPlatformPostEmptyEvent(void)652 void _glfwPlatformPostEmptyEvent(void)
653 {
654     wl_display_sync(_glfw.wl.display);
655 }
656 
_glfwPlatformGetCursorPos(_GLFWwindow * window,double * xpos,double * ypos)657 void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
658 {
659     if (xpos)
660         *xpos = window->wl.cursorPosX;
661     if (ypos)
662         *ypos = window->wl.cursorPosY;
663 }
664 
665 static GLFWbool isPointerLocked(_GLFWwindow* window);
666 
_glfwPlatformSetCursorPos(_GLFWwindow * window,double x,double y)667 void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
668 {
669     if (isPointerLocked(window))
670     {
671         zwp_locked_pointer_v1_set_cursor_position_hint(
672             window->wl.pointerLock.lockedPointer,
673             wl_fixed_from_double(x), wl_fixed_from_double(y));
674         wl_surface_commit(window->wl.surface);
675     }
676 }
677 
_glfwPlatformSetCursorMode(_GLFWwindow * window,int mode)678 void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
679 {
680     _glfwPlatformSetCursor(window, window->wl.currentCursor);
681 }
682 
_glfwPlatformGetKeyName(int key,int scancode)683 const char* _glfwPlatformGetKeyName(int key, int scancode)
684 {
685     // TODO
686     return NULL;
687 }
688 
_glfwPlatformCreateCursor(_GLFWcursor * cursor,const GLFWimage * image,int xhot,int yhot)689 int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
690                               const GLFWimage* image,
691                               int xhot, int yhot)
692 {
693     struct wl_shm_pool* pool;
694     int stride = image->width * 4;
695     int length = image->width * image->height * 4;
696     void* data;
697     int fd, i;
698 
699     fd = createAnonymousFile(length);
700     if (fd < 0)
701     {
702         _glfwInputError(GLFW_PLATFORM_ERROR,
703                         "Wayland: Creating a buffer file for %d B failed: %m",
704                         length);
705         return GLFW_FALSE;
706     }
707 
708     data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
709     if (data == MAP_FAILED)
710     {
711         _glfwInputError(GLFW_PLATFORM_ERROR,
712                         "Wayland: Cursor mmap failed: %m");
713         close(fd);
714         return GLFW_FALSE;
715     }
716 
717     pool = wl_shm_create_pool(_glfw.wl.shm, fd, length);
718 
719     close(fd);
720     unsigned char* source = (unsigned char*) image->pixels;
721     unsigned char* target = data;
722     for (i = 0;  i < image->width * image->height;  i++, source += 4)
723     {
724         unsigned int alpha = source[3];
725 
726         *target++ = (unsigned char) ((source[2] * alpha) / 255);
727         *target++ = (unsigned char) ((source[1] * alpha) / 255);
728         *target++ = (unsigned char) ((source[0] * alpha) / 255);
729         *target++ = (unsigned char) alpha;
730     }
731 
732     cursor->wl.buffer =
733         wl_shm_pool_create_buffer(pool, 0,
734                                   image->width,
735                                   image->height,
736                                   stride, WL_SHM_FORMAT_ARGB8888);
737     munmap(data, length);
738     wl_shm_pool_destroy(pool);
739 
740     cursor->wl.width = image->width;
741     cursor->wl.height = image->height;
742     cursor->wl.xhot = xhot;
743     cursor->wl.yhot = yhot;
744     return GLFW_TRUE;
745 }
746 
_glfwPlatformCreateStandardCursor(_GLFWcursor * cursor,int shape)747 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
748 {
749     struct wl_cursor* standardCursor;
750 
751     standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme,
752                                                 translateCursorShape(shape));
753     if (!standardCursor)
754     {
755         _glfwInputError(GLFW_PLATFORM_ERROR,
756                         "Wayland: Standard cursor \"%s\" not found",
757                         translateCursorShape(shape));
758         return GLFW_FALSE;
759     }
760 
761     cursor->wl.image = standardCursor->images[0];
762     return GLFW_TRUE;
763 }
764 
_glfwPlatformDestroyCursor(_GLFWcursor * cursor)765 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
766 {
767     // If it's a standard cursor we don't need to do anything here
768     if (cursor->wl.image)
769         return;
770 
771     if (cursor->wl.buffer)
772         wl_buffer_destroy(cursor->wl.buffer);
773 }
774 
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)775 static void handleRelativeMotion(void* data,
776                                  struct zwp_relative_pointer_v1* pointer,
777                                  uint32_t timeHi,
778                                  uint32_t timeLo,
779                                  wl_fixed_t dx,
780                                  wl_fixed_t dy,
781                                  wl_fixed_t dxUnaccel,
782                                  wl_fixed_t dyUnaccel)
783 {
784     _GLFWwindow* window = data;
785 
786     if (window->cursorMode != GLFW_CURSOR_DISABLED)
787         return;
788 
789     _glfwInputCursorPos(window,
790                         window->virtualCursorPosX + wl_fixed_to_double(dxUnaccel),
791                         window->virtualCursorPosY + wl_fixed_to_double(dyUnaccel));
792 }
793 
794 static const struct zwp_relative_pointer_v1_listener relativePointerListener = {
795     handleRelativeMotion
796 };
797 
handleLocked(void * data,struct zwp_locked_pointer_v1 * lockedPointer)798 static void handleLocked(void* data,
799                          struct zwp_locked_pointer_v1* lockedPointer)
800 {
801 }
802 
unlockPointer(_GLFWwindow * window)803 static void unlockPointer(_GLFWwindow* window)
804 {
805     struct zwp_relative_pointer_v1* relativePointer =
806         window->wl.pointerLock.relativePointer;
807     struct zwp_locked_pointer_v1* lockedPointer =
808         window->wl.pointerLock.lockedPointer;
809 
810     zwp_relative_pointer_v1_destroy(relativePointer);
811     zwp_locked_pointer_v1_destroy(lockedPointer);
812 
813     window->wl.pointerLock.relativePointer = NULL;
814     window->wl.pointerLock.lockedPointer = NULL;
815 }
816 
817 static void lockPointer(_GLFWwindow* window);
818 
handleUnlocked(void * data,struct zwp_locked_pointer_v1 * lockedPointer)819 static void handleUnlocked(void* data,
820                            struct zwp_locked_pointer_v1* lockedPointer)
821 {
822 }
823 
824 static const struct zwp_locked_pointer_v1_listener lockedPointerListener = {
825     handleLocked,
826     handleUnlocked
827 };
828 
lockPointer(_GLFWwindow * window)829 static void lockPointer(_GLFWwindow* window)
830 {
831     struct zwp_relative_pointer_v1* relativePointer;
832     struct zwp_locked_pointer_v1* lockedPointer;
833 
834     if (!_glfw.wl.relativePointerManager)
835     {
836         _glfwInputError(GLFW_PLATFORM_ERROR,
837                         "Wayland: no relative pointer manager");
838         return;
839     }
840 
841     relativePointer =
842         zwp_relative_pointer_manager_v1_get_relative_pointer(
843             _glfw.wl.relativePointerManager,
844             _glfw.wl.pointer);
845     zwp_relative_pointer_v1_add_listener(relativePointer,
846                                          &relativePointerListener,
847                                          window);
848 
849     lockedPointer =
850         zwp_pointer_constraints_v1_lock_pointer(
851             _glfw.wl.pointerConstraints,
852             window->wl.surface,
853             _glfw.wl.pointer,
854             NULL,
855             ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
856     zwp_locked_pointer_v1_add_listener(lockedPointer,
857                                        &lockedPointerListener,
858                                        window);
859 
860     window->wl.pointerLock.relativePointer = relativePointer;
861     window->wl.pointerLock.lockedPointer = lockedPointer;
862 
863     wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
864                           NULL, 0, 0);
865 }
866 
isPointerLocked(_GLFWwindow * window)867 static GLFWbool isPointerLocked(_GLFWwindow* window)
868 {
869     return window->wl.pointerLock.lockedPointer != NULL;
870 }
871 
_glfwPlatformSetCursor(_GLFWwindow * window,_GLFWcursor * cursor)872 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
873 {
874     struct wl_buffer* buffer;
875     struct wl_cursor* defaultCursor;
876     struct wl_cursor_image* image;
877     struct wl_surface* surface = _glfw.wl.cursorSurface;
878 
879     if (!_glfw.wl.pointer)
880         return;
881 
882     window->wl.currentCursor = cursor;
883 
884     // If we're not in the correct window just save the cursor
885     // the next time the pointer enters the window the cursor will change
886     if (window != _glfw.wl.pointerFocus)
887         return;
888 
889     // Unlock possible pointer lock if no longer disabled.
890     if (window->cursorMode != GLFW_CURSOR_DISABLED && isPointerLocked(window))
891         unlockPointer(window);
892 
893     if (window->cursorMode == GLFW_CURSOR_NORMAL)
894     {
895         if (cursor)
896             image = cursor->wl.image;
897         else
898         {
899             defaultCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme,
900                                                        "left_ptr");
901             if (!defaultCursor)
902             {
903                 _glfwInputError(GLFW_PLATFORM_ERROR,
904                                 "Wayland: Standard cursor not found");
905                 return;
906             }
907             image = defaultCursor->images[0];
908         }
909 
910         if (image)
911         {
912             buffer = wl_cursor_image_get_buffer(image);
913             if (!buffer)
914                 return;
915             wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
916                                   surface,
917                                   image->hotspot_x,
918                                   image->hotspot_y);
919             wl_surface_attach(surface, buffer, 0, 0);
920             wl_surface_damage(surface, 0, 0,
921                               image->width, image->height);
922             wl_surface_commit(surface);
923         }
924         else
925         {
926             wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
927                                   surface,
928                                   cursor->wl.xhot,
929                                   cursor->wl.yhot);
930             wl_surface_attach(surface, cursor->wl.buffer, 0, 0);
931             wl_surface_damage(surface, 0, 0,
932                               cursor->wl.width, cursor->wl.height);
933             wl_surface_commit(surface);
934         }
935     }
936     else if (window->cursorMode == GLFW_CURSOR_DISABLED)
937     {
938         if (!isPointerLocked(window))
939             lockPointer(window);
940     }
941     else if (window->cursorMode == GLFW_CURSOR_HIDDEN)
942     {
943         wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
944                               NULL, 0, 0);
945     }
946 }
947 
_glfwPlatformSetClipboardString(_GLFWwindow * window,const char * string)948 void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string)
949 {
950     // TODO
951     _glfwInputError(GLFW_PLATFORM_ERROR,
952                     "Wayland: Clipboard setting not implemented yet");
953 }
954 
_glfwPlatformGetClipboardString(_GLFWwindow * window)955 const char* _glfwPlatformGetClipboardString(_GLFWwindow* window)
956 {
957     // TODO
958     _glfwInputError(GLFW_PLATFORM_ERROR,
959                     "Wayland: Clipboard getting not implemented yet");
960     return NULL;
961 }
962 
_glfwPlatformGetRequiredInstanceExtensions(uint32_t * count)963 char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count)
964 {
965     char** extensions;
966 
967     *count = 0;
968 
969     if (!_glfw.vk.KHR_wayland_surface)
970         return NULL;
971 
972     extensions = calloc(2, sizeof(char*));
973     extensions[0] = strdup("VK_KHR_surface");
974     extensions[1] = strdup("VK_KHR_wayland_surface");
975 
976     *count = 2;
977     return extensions;
978 }
979 
_glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,VkPhysicalDevice device,uint32_t queuefamily)980 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
981                                                       VkPhysicalDevice device,
982                                                       uint32_t queuefamily)
983 {
984     PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR =
985         (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)
986         vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR");
987     if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR)
988     {
989         _glfwInputError(GLFW_API_UNAVAILABLE,
990                         "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension");
991         return VK_NULL_HANDLE;
992     }
993 
994     return vkGetPhysicalDeviceWaylandPresentationSupportKHR(device,
995                                                             queuefamily,
996                                                             _glfw.wl.display);
997 }
998 
_glfwPlatformCreateWindowSurface(VkInstance instance,_GLFWwindow * window,const VkAllocationCallbacks * allocator,VkSurfaceKHR * surface)999 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
1000                                           _GLFWwindow* window,
1001                                           const VkAllocationCallbacks* allocator,
1002                                           VkSurfaceKHR* surface)
1003 {
1004     VkResult err;
1005     VkWaylandSurfaceCreateInfoKHR sci;
1006     PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR;
1007 
1008     vkCreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR)
1009         vkGetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR");
1010     if (!vkCreateWaylandSurfaceKHR)
1011     {
1012         _glfwInputError(GLFW_API_UNAVAILABLE,
1013                         "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension");
1014         return VK_ERROR_EXTENSION_NOT_PRESENT;
1015     }
1016 
1017     memset(&sci, 0, sizeof(sci));
1018     sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
1019     sci.display = _glfw.wl.display;
1020     sci.surface = window->wl.surface;
1021 
1022     err = vkCreateWaylandSurfaceKHR(instance, &sci, allocator, surface);
1023     if (err)
1024     {
1025         _glfwInputError(GLFW_PLATFORM_ERROR,
1026                         "Wayland: Failed to create Vulkan surface: %s",
1027                         _glfwGetVulkanResultString(err));
1028     }
1029 
1030     return err;
1031 }
1032 
1033 
1034 //////////////////////////////////////////////////////////////////////////
1035 //////                        GLFW native API                       //////
1036 //////////////////////////////////////////////////////////////////////////
1037 
glfwGetWaylandDisplay(void)1038 GLFWAPI struct wl_display* glfwGetWaylandDisplay(void)
1039 {
1040     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
1041     return _glfw.wl.display;
1042 }
1043 
glfwGetWaylandWindow(GLFWwindow * handle)1044 GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle)
1045 {
1046     _GLFWwindow* window = (_GLFWwindow*) handle;
1047     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
1048     return window->wl.surface;
1049 }
1050 
1051