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