master
ブランチ master
cpputils / deps / glfw / src / wl_window.c
59 KB、1894行 生 Blame 履歴
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// It is fine to use C99 in this file because it will not be built with VS
27//========================================================================
28
29#define _GNU_SOURCE
30
31#include "internal.h"
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <errno.h>
36#include <unistd.h>
37#include <string.h>
38#include <fcntl.h>
39#include <sys/mman.h>
40#include <sys/timerfd.h>
41#include <poll.h>
42
43
44static void shellSurfaceHandlePing(void* data,
45 struct wl_shell_surface* shellSurface,
46 uint32_t serial)
47{
48 wl_shell_surface_pong(shellSurface, serial);
49}
50
51static void shellSurfaceHandleConfigure(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 (_glfw.wl.viewporter && window->decorated)
64 {
65 width -= _GLFW_DECORATION_HORIZONTAL;
66 height -= _GLFW_DECORATION_VERTICAL;
67 }
68 if (width < 1)
69 width = 1;
70 if (height < 1)
71 height = 1;
72
73 if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)
74 {
75 aspectRatio = (float)width / (float)height;
76 targetRatio = (float)window->numer / (float)window->denom;
77 if (aspectRatio < targetRatio)
78 height = width / targetRatio;
79 else if (aspectRatio > targetRatio)
80 width = height * targetRatio;
81 }
82
83 if (window->minwidth != GLFW_DONT_CARE && width < window->minwidth)
84 width = window->minwidth;
85 else if (window->maxwidth != GLFW_DONT_CARE && width > window->maxwidth)
86 width = window->maxwidth;
87
88 if (window->minheight != GLFW_DONT_CARE && height < window->minheight)
89 height = window->minheight;
90 else if (window->maxheight != GLFW_DONT_CARE && height > window->maxheight)
91 height = window->maxheight;
92 }
93
94 _glfwInputWindowSize(window, width, height);
95 _glfwPlatformSetWindowSize(window, width, height);
96 _glfwInputWindowDamage(window);
97}
98
99static void shellSurfaceHandlePopupDone(void* data,
100 struct wl_shell_surface* shellSurface)
101{
102}
103
104static const struct wl_shell_surface_listener shellSurfaceListener = {
105 shellSurfaceHandlePing,
106 shellSurfaceHandleConfigure,
107 shellSurfaceHandlePopupDone
108};
109
110static int createTmpfileCloexec(char* tmpname)
111{
112 int fd;
113
114 fd = mkostemp(tmpname, O_CLOEXEC);
115 if (fd >= 0)
116 unlink(tmpname);
117
118 return fd;
119}
120
121/*
122 * Create a new, unique, anonymous file of the given size, and
123 * return the file descriptor for it. The file descriptor is set
124 * CLOEXEC. The file is immediately suitable for mmap()'ing
125 * the given size at offset zero.
126 *
127 * The file should not have a permanent backing store like a disk,
128 * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
129 *
130 * The file name is deleted from the file system.
131 *
132 * The file is suitable for buffer sharing between processes by
133 * transmitting the file descriptor over Unix sockets using the
134 * SCM_RIGHTS methods.
135 *
136 * posix_fallocate() is used to guarantee that disk space is available
137 * for the file at the given size. If disk space is insufficient, errno
138 * is set to ENOSPC. If posix_fallocate() is not supported, program may
139 * receive SIGBUS on accessing mmap()'ed file contents instead.
140 */
141static int 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#ifdef HAVE_MEMFD_CREATE
150 fd = memfd_create("glfw-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING);
151 if (fd >= 0)
152 {
153 // We can add this seal before calling posix_fallocate(), as the file
154 // is currently zero-sized anyway.
155 //
156 // There is also no need to check for the return value, we couldn’t do
157 // anything with it anyway.
158 fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
159 }
160 else
161#elif defined(SHM_ANON)
162 fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600);
163 if (fd < 0)
164#endif
165 {
166 path = getenv("XDG_RUNTIME_DIR");
167 if (!path)
168 {
169 errno = ENOENT;
170 return -1;
171 }
172
173 name = calloc(strlen(path) + sizeof(template), 1);
174 strcpy(name, path);
175 strcat(name, template);
176
177 fd = createTmpfileCloexec(name);
178 free(name);
179 if (fd < 0)
180 return -1;
181 }
182
183#if defined(SHM_ANON)
184 // posix_fallocate does not work on SHM descriptors
185 ret = ftruncate(fd, size);
186#else
187 ret = posix_fallocate(fd, 0, size);
188#endif
189 if (ret != 0)
190 {
191 close(fd);
192 errno = ret;
193 return -1;
194 }
195 return fd;
196}
197
198static struct wl_buffer* createShmBuffer(const GLFWimage* image)
199{
200 struct wl_shm_pool* pool;
201 struct wl_buffer* buffer;
202 int stride = image->width * 4;
203 int length = image->width * image->height * 4;
204 void* data;
205 int fd, i;
206
207 fd = createAnonymousFile(length);
208 if (fd < 0)
209 {
210 _glfwInputError(GLFW_PLATFORM_ERROR,
211 "Wayland: Creating a buffer file for %d B failed: %m",
212 length);
213 return NULL;
214 }
215
216 data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
217 if (data == MAP_FAILED)
218 {
219 _glfwInputError(GLFW_PLATFORM_ERROR,
220 "Wayland: mmap failed: %m");
221 close(fd);
222 return NULL;
223 }
224
225 pool = wl_shm_create_pool(_glfw.wl.shm, fd, length);
226
227 close(fd);
228 unsigned char* source = (unsigned char*) image->pixels;
229 unsigned char* target = data;
230 for (i = 0; i < image->width * image->height; i++, source += 4)
231 {
232 unsigned int alpha = source[3];
233
234 *target++ = (unsigned char) ((source[2] * alpha) / 255);
235 *target++ = (unsigned char) ((source[1] * alpha) / 255);
236 *target++ = (unsigned char) ((source[0] * alpha) / 255);
237 *target++ = (unsigned char) alpha;
238 }
239
240 buffer =
241 wl_shm_pool_create_buffer(pool, 0,
242 image->width,
243 image->height,
244 stride, WL_SHM_FORMAT_ARGB8888);
245 munmap(data, length);
246 wl_shm_pool_destroy(pool);
247
248 return buffer;
249}
250
251static void createDecoration(_GLFWdecorationWayland* decoration,
252 struct wl_surface* parent,
253 struct wl_buffer* buffer, GLFWbool opaque,
254 int x, int y,
255 int width, int height)
256{
257 struct wl_region* region;
258
259 decoration->surface = wl_compositor_create_surface(_glfw.wl.compositor);
260 decoration->subsurface =
261 wl_subcompositor_get_subsurface(_glfw.wl.subcompositor,
262 decoration->surface, parent);
263 wl_subsurface_set_position(decoration->subsurface, x, y);
264 decoration->viewport = wp_viewporter_get_viewport(_glfw.wl.viewporter,
265 decoration->surface);
266 wp_viewport_set_destination(decoration->viewport, width, height);
267 wl_surface_attach(decoration->surface, buffer, 0, 0);
268
269 if (opaque)
270 {
271 region = wl_compositor_create_region(_glfw.wl.compositor);
272 wl_region_add(region, 0, 0, width, height);
273 wl_surface_set_opaque_region(decoration->surface, region);
274 wl_surface_commit(decoration->surface);
275 wl_region_destroy(region);
276 }
277 else
278 wl_surface_commit(decoration->surface);
279}
280
281static void createDecorations(_GLFWwindow* window)
282{
283 unsigned char data[] = { 224, 224, 224, 255 };
284 const GLFWimage image = { 1, 1, data };
285 GLFWbool opaque = (data[3] == 255);
286
287 if (!_glfw.wl.viewporter || !window->decorated || window->wl.decorations.serverSide)
288 return;
289
290 if (!window->wl.decorations.buffer)
291 window->wl.decorations.buffer = createShmBuffer(&image);
292 if (!window->wl.decorations.buffer)
293 return;
294
295 createDecoration(&window->wl.decorations.top, window->wl.surface,
296 window->wl.decorations.buffer, opaque,
297 0, -_GLFW_DECORATION_TOP,
298 window->wl.width, _GLFW_DECORATION_TOP);
299 createDecoration(&window->wl.decorations.left, window->wl.surface,
300 window->wl.decorations.buffer, opaque,
301 -_GLFW_DECORATION_WIDTH, -_GLFW_DECORATION_TOP,
302 _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
303 createDecoration(&window->wl.decorations.right, window->wl.surface,
304 window->wl.decorations.buffer, opaque,
305 window->wl.width, -_GLFW_DECORATION_TOP,
306 _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
307 createDecoration(&window->wl.decorations.bottom, window->wl.surface,
308 window->wl.decorations.buffer, opaque,
309 -_GLFW_DECORATION_WIDTH, window->wl.height,
310 window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH);
311}
312
313static void destroyDecoration(_GLFWdecorationWayland* decoration)
314{
315 if (decoration->surface)
316 wl_surface_destroy(decoration->surface);
317 if (decoration->subsurface)
318 wl_subsurface_destroy(decoration->subsurface);
319 if (decoration->viewport)
320 wp_viewport_destroy(decoration->viewport);
321 decoration->surface = NULL;
322 decoration->subsurface = NULL;
323 decoration->viewport = NULL;
324}
325
326static void destroyDecorations(_GLFWwindow* window)
327{
328 destroyDecoration(&window->wl.decorations.top);
329 destroyDecoration(&window->wl.decorations.left);
330 destroyDecoration(&window->wl.decorations.right);
331 destroyDecoration(&window->wl.decorations.bottom);
332}
333
334static void xdgDecorationHandleConfigure(void* data,
335 struct zxdg_toplevel_decoration_v1* decoration,
336 uint32_t mode)
337{
338 _GLFWwindow* window = data;
339
340 window->wl.decorations.serverSide = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
341
342 if (!window->wl.decorations.serverSide)
343 createDecorations(window);
344}
345
346static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener = {
347 xdgDecorationHandleConfigure,
348};
349
350// Makes the surface considered as XRGB instead of ARGB.
351static void setOpaqueRegion(_GLFWwindow* window)
352{
353 struct wl_region* region;
354
355 region = wl_compositor_create_region(_glfw.wl.compositor);
356 if (!region)
357 return;
358
359 wl_region_add(region, 0, 0, window->wl.width, window->wl.height);
360 wl_surface_set_opaque_region(window->wl.surface, region);
361 wl_surface_commit(window->wl.surface);
362 wl_region_destroy(region);
363}
364
365
366static void resizeWindow(_GLFWwindow* window)
367{
368 int scale = window->wl.scale;
369 int scaledWidth = window->wl.width * scale;
370 int scaledHeight = window->wl.height * scale;
371 wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
372 if (!window->wl.transparent)
373 setOpaqueRegion(window);
374 _glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
375 _glfwInputWindowContentScale(window, scale, scale);
376
377 if (!window->wl.decorations.top.surface)
378 return;
379
380 // Top decoration.
381 wp_viewport_set_destination(window->wl.decorations.top.viewport,
382 window->wl.width, _GLFW_DECORATION_TOP);
383 wl_surface_commit(window->wl.decorations.top.surface);
384
385 // Left decoration.
386 wp_viewport_set_destination(window->wl.decorations.left.viewport,
387 _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
388 wl_surface_commit(window->wl.decorations.left.surface);
389
390 // Right decoration.
391 wl_subsurface_set_position(window->wl.decorations.right.subsurface,
392 window->wl.width, -_GLFW_DECORATION_TOP);
393 wp_viewport_set_destination(window->wl.decorations.right.viewport,
394 _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
395 wl_surface_commit(window->wl.decorations.right.surface);
396
397 // Bottom decoration.
398 wl_subsurface_set_position(window->wl.decorations.bottom.subsurface,
399 -_GLFW_DECORATION_WIDTH, window->wl.height);
400 wp_viewport_set_destination(window->wl.decorations.bottom.viewport,
401 window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH);
402 wl_surface_commit(window->wl.decorations.bottom.surface);
403}
404
405static void checkScaleChange(_GLFWwindow* window)
406{
407 int scale = 1;
408 int i;
409 int monitorScale;
410
411 // Check if we will be able to set the buffer scale or not.
412 if (_glfw.wl.compositorVersion < 3)
413 return;
414
415 // Get the scale factor from the highest scale monitor.
416 for (i = 0; i < window->wl.monitorsCount; ++i)
417 {
418 monitorScale = window->wl.monitors[i]->wl.scale;
419 if (scale < monitorScale)
420 scale = monitorScale;
421 }
422
423 // Only change the framebuffer size if the scale changed.
424 if (scale != window->wl.scale)
425 {
426 window->wl.scale = scale;
427 wl_surface_set_buffer_scale(window->wl.surface, scale);
428 resizeWindow(window);
429 }
430}
431
432static void surfaceHandleEnter(void *data,
433 struct wl_surface *surface,
434 struct wl_output *output)
435{
436 _GLFWwindow* window = data;
437 _GLFWmonitor* monitor = wl_output_get_user_data(output);
438
439 if (window->wl.monitorsCount + 1 > window->wl.monitorsSize)
440 {
441 ++window->wl.monitorsSize;
442 window->wl.monitors =
443 realloc(window->wl.monitors,
444 window->wl.monitorsSize * sizeof(_GLFWmonitor*));
445 }
446
447 window->wl.monitors[window->wl.monitorsCount++] = monitor;
448
449 checkScaleChange(window);
450}
451
452static void surfaceHandleLeave(void *data,
453 struct wl_surface *surface,
454 struct wl_output *output)
455{
456 _GLFWwindow* window = data;
457 _GLFWmonitor* monitor = wl_output_get_user_data(output);
458 GLFWbool found;
459 int i;
460
461 for (i = 0, found = GLFW_FALSE; i < window->wl.monitorsCount - 1; ++i)
462 {
463 if (monitor == window->wl.monitors[i])
464 found = GLFW_TRUE;
465 if (found)
466 window->wl.monitors[i] = window->wl.monitors[i + 1];
467 }
468 window->wl.monitors[--window->wl.monitorsCount] = NULL;
469
470 checkScaleChange(window);
471}
472
473static const struct wl_surface_listener surfaceListener = {
474 surfaceHandleEnter,
475 surfaceHandleLeave
476};
477
478static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable)
479{
480 if (enable && !window->wl.idleInhibitor && _glfw.wl.idleInhibitManager)
481 {
482 window->wl.idleInhibitor =
483 zwp_idle_inhibit_manager_v1_create_inhibitor(
484 _glfw.wl.idleInhibitManager, window->wl.surface);
485 if (!window->wl.idleInhibitor)
486 _glfwInputError(GLFW_PLATFORM_ERROR,
487 "Wayland: Idle inhibitor creation failed");
488 }
489 else if (!enable && window->wl.idleInhibitor)
490 {
491 zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor);
492 window->wl.idleInhibitor = NULL;
493 }
494}
495
496static GLFWbool createSurface(_GLFWwindow* window,
497 const _GLFWwndconfig* wndconfig)
498{
499 window->wl.surface = wl_compositor_create_surface(_glfw.wl.compositor);
500 if (!window->wl.surface)
501 return GLFW_FALSE;
502
503 wl_surface_add_listener(window->wl.surface,
504 &surfaceListener,
505 window);
506
507 wl_surface_set_user_data(window->wl.surface, window);
508
509 window->wl.native = wl_egl_window_create(window->wl.surface,
510 wndconfig->width,
511 wndconfig->height);
512 if (!window->wl.native)
513 return GLFW_FALSE;
514
515 window->wl.width = wndconfig->width;
516 window->wl.height = wndconfig->height;
517 window->wl.scale = 1;
518
519 if (!window->wl.transparent)
520 setOpaqueRegion(window);
521
522 return GLFW_TRUE;
523}
524
525static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor,
526 int refreshRate)
527{
528 if (window->wl.xdg.toplevel)
529 {
530 xdg_toplevel_set_fullscreen(
531 window->wl.xdg.toplevel,
532 monitor->wl.output);
533 }
534 else if (window->wl.shellSurface)
535 {
536 wl_shell_surface_set_fullscreen(
537 window->wl.shellSurface,
538 WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
539 refreshRate * 1000, // Convert Hz to mHz.
540 monitor->wl.output);
541 }
542 setIdleInhibitor(window, GLFW_TRUE);
543 if (!window->wl.decorations.serverSide)
544 destroyDecorations(window);
545}
546
547static GLFWbool createShellSurface(_GLFWwindow* window)
548{
549 if (!_glfw.wl.shell)
550 {
551 _glfwInputError(GLFW_PLATFORM_ERROR,
552 "Wayland: wl_shell protocol not available");
553 return GLFW_FALSE;
554 }
555
556 window->wl.shellSurface = wl_shell_get_shell_surface(_glfw.wl.shell,
557 window->wl.surface);
558 if (!window->wl.shellSurface)
559 {
560 _glfwInputError(GLFW_PLATFORM_ERROR,
561 "Wayland: Shell surface creation failed");
562 return GLFW_FALSE;
563 }
564
565 wl_shell_surface_add_listener(window->wl.shellSurface,
566 &shellSurfaceListener,
567 window);
568
569 if (window->wl.title)
570 wl_shell_surface_set_title(window->wl.shellSurface, window->wl.title);
571
572 if (window->monitor)
573 {
574 setFullscreen(window, window->monitor, 0);
575 }
576 else if (window->wl.maximized)
577 {
578 wl_shell_surface_set_maximized(window->wl.shellSurface, NULL);
579 setIdleInhibitor(window, GLFW_FALSE);
580 createDecorations(window);
581 }
582 else
583 {
584 wl_shell_surface_set_toplevel(window->wl.shellSurface);
585 setIdleInhibitor(window, GLFW_FALSE);
586 createDecorations(window);
587 }
588
589 wl_surface_commit(window->wl.surface);
590
591 return GLFW_TRUE;
592}
593
594static void xdgToplevelHandleConfigure(void* data,
595 struct xdg_toplevel* toplevel,
596 int32_t width,
597 int32_t height,
598 struct wl_array* states)
599{
600 _GLFWwindow* window = data;
601 float aspectRatio;
602 float targetRatio;
603 uint32_t* state;
604 GLFWbool maximized = GLFW_FALSE;
605 GLFWbool fullscreen = GLFW_FALSE;
606 GLFWbool activated = GLFW_FALSE;
607
608 wl_array_for_each(state, states)
609 {
610 switch (*state)
611 {
612 case XDG_TOPLEVEL_STATE_MAXIMIZED:
613 maximized = GLFW_TRUE;
614 break;
615 case XDG_TOPLEVEL_STATE_FULLSCREEN:
616 fullscreen = GLFW_TRUE;
617 break;
618 case XDG_TOPLEVEL_STATE_RESIZING:
619 break;
620 case XDG_TOPLEVEL_STATE_ACTIVATED:
621 activated = GLFW_TRUE;
622 break;
623 }
624 }
625
626 if (width != 0 && height != 0)
627 {
628 if (!maximized && !fullscreen)
629 {
630 if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)
631 {
632 aspectRatio = (float)width / (float)height;
633 targetRatio = (float)window->numer / (float)window->denom;
634 if (aspectRatio < targetRatio)
635 height = width / targetRatio;
636 else if (aspectRatio > targetRatio)
637 width = height * targetRatio;
638 }
639 }
640
641 _glfwInputWindowSize(window, width, height);
642 _glfwPlatformSetWindowSize(window, width, height);
643 _glfwInputWindowDamage(window);
644 }
645
646 if (window->wl.wasFullscreen && window->autoIconify)
647 {
648 if (!activated || !fullscreen)
649 {
650 _glfwPlatformIconifyWindow(window);
651 window->wl.wasFullscreen = GLFW_FALSE;
652 }
653 }
654 if (fullscreen && activated)
655 window->wl.wasFullscreen = GLFW_TRUE;
656 _glfwInputWindowFocus(window, activated);
657}
658
659static void xdgToplevelHandleClose(void* data,
660 struct xdg_toplevel* toplevel)
661{
662 _GLFWwindow* window = data;
663 _glfwInputWindowCloseRequest(window);
664}
665
666static const struct xdg_toplevel_listener xdgToplevelListener = {
667 xdgToplevelHandleConfigure,
668 xdgToplevelHandleClose
669};
670
671static void xdgSurfaceHandleConfigure(void* data,
672 struct xdg_surface* surface,
673 uint32_t serial)
674{
675 xdg_surface_ack_configure(surface, serial);
676}
677
678static const struct xdg_surface_listener xdgSurfaceListener = {
679 xdgSurfaceHandleConfigure
680};
681
682static void setXdgDecorations(_GLFWwindow* window)
683{
684 if (_glfw.wl.decorationManager)
685 {
686 window->wl.xdg.decoration =
687 zxdg_decoration_manager_v1_get_toplevel_decoration(
688 _glfw.wl.decorationManager, window->wl.xdg.toplevel);
689 zxdg_toplevel_decoration_v1_add_listener(window->wl.xdg.decoration,
690 &xdgDecorationListener,
691 window);
692 zxdg_toplevel_decoration_v1_set_mode(
693 window->wl.xdg.decoration,
694 ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
695 }
696 else
697 {
698 window->wl.decorations.serverSide = GLFW_FALSE;
699 createDecorations(window);
700 }
701}
702
703static GLFWbool createXdgSurface(_GLFWwindow* window)
704{
705 window->wl.xdg.surface = xdg_wm_base_get_xdg_surface(_glfw.wl.wmBase,
706 window->wl.surface);
707 if (!window->wl.xdg.surface)
708 {
709 _glfwInputError(GLFW_PLATFORM_ERROR,
710 "Wayland: xdg-surface creation failed");
711 return GLFW_FALSE;
712 }
713
714 xdg_surface_add_listener(window->wl.xdg.surface,
715 &xdgSurfaceListener,
716 window);
717
718 window->wl.xdg.toplevel = xdg_surface_get_toplevel(window->wl.xdg.surface);
719 if (!window->wl.xdg.toplevel)
720 {
721 _glfwInputError(GLFW_PLATFORM_ERROR,
722 "Wayland: xdg-toplevel creation failed");
723 return GLFW_FALSE;
724 }
725
726 xdg_toplevel_add_listener(window->wl.xdg.toplevel,
727 &xdgToplevelListener,
728 window);
729
730 if (window->wl.title)
731 xdg_toplevel_set_title(window->wl.xdg.toplevel, window->wl.title);
732
733 if (window->minwidth != GLFW_DONT_CARE && window->minheight != GLFW_DONT_CARE)
734 xdg_toplevel_set_min_size(window->wl.xdg.toplevel,
735 window->minwidth, window->minheight);
736 if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE)
737 xdg_toplevel_set_max_size(window->wl.xdg.toplevel,
738 window->maxwidth, window->maxheight);
739
740 if (window->monitor)
741 {
742 xdg_toplevel_set_fullscreen(window->wl.xdg.toplevel,
743 window->monitor->wl.output);
744 setIdleInhibitor(window, GLFW_TRUE);
745 }
746 else if (window->wl.maximized)
747 {
748 xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
749 setIdleInhibitor(window, GLFW_FALSE);
750 setXdgDecorations(window);
751 }
752 else
753 {
754 setIdleInhibitor(window, GLFW_FALSE);
755 setXdgDecorations(window);
756 }
757
758 wl_surface_commit(window->wl.surface);
759 wl_display_roundtrip(_glfw.wl.display);
760
761 return GLFW_TRUE;
762}
763
764static void setCursorImage(_GLFWwindow* window,
765 _GLFWcursorWayland* cursorWayland)
766{
767 struct itimerspec timer = {};
768 struct wl_cursor* wlCursor = cursorWayland->cursor;
769 struct wl_cursor_image* image;
770 struct wl_buffer* buffer;
771 struct wl_surface* surface = _glfw.wl.cursorSurface;
772 int scale = 1;
773
774 if (!wlCursor)
775 buffer = cursorWayland->buffer;
776 else
777 {
778 if (window->wl.scale > 1 && cursorWayland->cursorHiDPI)
779 {
780 wlCursor = cursorWayland->cursorHiDPI;
781 scale = 2;
782 }
783
784 image = wlCursor->images[cursorWayland->currentImage];
785 buffer = wl_cursor_image_get_buffer(image);
786 if (!buffer)
787 return;
788
789 timer.it_value.tv_sec = image->delay / 1000;
790 timer.it_value.tv_nsec = (image->delay % 1000) * 1000000;
791 timerfd_settime(_glfw.wl.cursorTimerfd, 0, &timer, NULL);
792
793 cursorWayland->width = image->width;
794 cursorWayland->height = image->height;
795 cursorWayland->xhot = image->hotspot_x;
796 cursorWayland->yhot = image->hotspot_y;
797 }
798
799 wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
800 surface,
801 cursorWayland->xhot / scale,
802 cursorWayland->yhot / scale);
803 wl_surface_set_buffer_scale(surface, scale);
804 wl_surface_attach(surface, buffer, 0, 0);
805 wl_surface_damage(surface, 0, 0,
806 cursorWayland->width, cursorWayland->height);
807 wl_surface_commit(surface);
808}
809
810static void incrementCursorImage(_GLFWwindow* window)
811{
812 _GLFWcursor* cursor;
813
814 if (!window || window->wl.decorations.focus != mainWindow)
815 return;
816
817 cursor = window->wl.currentCursor;
818 if (cursor && cursor->wl.cursor)
819 {
820 cursor->wl.currentImage += 1;
821 cursor->wl.currentImage %= cursor->wl.cursor->image_count;
822 setCursorImage(window, &cursor->wl);
823 }
824}
825
826static void handleEvents(int timeout)
827{
828 struct wl_display* display = _glfw.wl.display;
829 struct pollfd fds[] = {
830 { wl_display_get_fd(display), POLLIN },
831 { _glfw.wl.timerfd, POLLIN },
832 { _glfw.wl.cursorTimerfd, POLLIN },
833 };
834 ssize_t read_ret;
835 uint64_t repeats, i;
836
837 while (wl_display_prepare_read(display) != 0)
838 wl_display_dispatch_pending(display);
839
840 // If an error different from EAGAIN happens, we have likely been
841 // disconnected from the Wayland session, try to handle that the best we
842 // can.
843 if (wl_display_flush(display) < 0 && errno != EAGAIN)
844 {
845 _GLFWwindow* window = _glfw.windowListHead;
846 while (window)
847 {
848 _glfwInputWindowCloseRequest(window);
849 window = window->next;
850 }
851 wl_display_cancel_read(display);
852 return;
853 }
854
855 if (poll(fds, 3, timeout) > 0)
856 {
857 if (fds[0].revents & POLLIN)
858 {
859 wl_display_read_events(display);
860 wl_display_dispatch_pending(display);
861 }
862 else
863 {
864 wl_display_cancel_read(display);
865 }
866
867 if (fds[1].revents & POLLIN)
868 {
869 read_ret = read(_glfw.wl.timerfd, &repeats, sizeof(repeats));
870 if (read_ret != 8)
871 return;
872
873 for (i = 0; i < repeats; ++i)
874 _glfwInputKey(_glfw.wl.keyboardFocus, _glfw.wl.keyboardLastKey,
875 _glfw.wl.keyboardLastScancode, GLFW_REPEAT,
876 _glfw.wl.xkb.modifiers);
877 }
878
879 if (fds[2].revents & POLLIN)
880 {
881 read_ret = read(_glfw.wl.cursorTimerfd, &repeats, sizeof(repeats));
882 if (read_ret != 8)
883 return;
884
885 incrementCursorImage(_glfw.wl.pointerFocus);
886 }
887 }
888 else
889 {
890 wl_display_cancel_read(display);
891 }
892}
893
894// Translates a GLFW standard cursor to a theme cursor name
895//
896static char *translateCursorShape(int shape)
897{
898 switch (shape)
899 {
900 case GLFW_ARROW_CURSOR:
901 return "left_ptr";
902 case GLFW_IBEAM_CURSOR:
903 return "xterm";
904 case GLFW_CROSSHAIR_CURSOR:
905 return "crosshair";
906 case GLFW_HAND_CURSOR:
907 return "hand2";
908 case GLFW_HRESIZE_CURSOR:
909 return "sb_h_double_arrow";
910 case GLFW_VRESIZE_CURSOR:
911 return "sb_v_double_arrow";
912 }
913 return NULL;
914}
915
916//////////////////////////////////////////////////////////////////////////
917////// GLFW platform API //////
918//////////////////////////////////////////////////////////////////////////
919
920int _glfwPlatformCreateWindow(_GLFWwindow* window,
921 const _GLFWwndconfig* wndconfig,
922 const _GLFWctxconfig* ctxconfig,
923 const _GLFWfbconfig* fbconfig)
924{
925 window->wl.transparent = fbconfig->transparent;
926
927 if (!createSurface(window, wndconfig))
928 return GLFW_FALSE;
929
930 if (ctxconfig->client != GLFW_NO_API)
931 {
932 if (ctxconfig->source == GLFW_EGL_CONTEXT_API ||
933 ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
934 {
935 if (!_glfwInitEGL())
936 return GLFW_FALSE;
937 if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
938 return GLFW_FALSE;
939 }
940 else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
941 {
942 if (!_glfwInitOSMesa())
943 return GLFW_FALSE;
944 if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
945 return GLFW_FALSE;
946 }
947 }
948
949 if (wndconfig->title)
950 window->wl.title = _glfw_strdup(wndconfig->title);
951
952 if (wndconfig->visible)
953 {
954 if (_glfw.wl.wmBase)
955 {
956 if (!createXdgSurface(window))
957 return GLFW_FALSE;
958 }
959 else
960 {
961 if (!createShellSurface(window))
962 return GLFW_FALSE;
963 }
964
965 window->wl.visible = GLFW_TRUE;
966 }
967 else
968 {
969 window->wl.xdg.surface = NULL;
970 window->wl.xdg.toplevel = NULL;
971 window->wl.shellSurface = NULL;
972 window->wl.visible = GLFW_FALSE;
973 }
974
975 window->wl.currentCursor = NULL;
976
977 window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*));
978 window->wl.monitorsCount = 0;
979 window->wl.monitorsSize = 1;
980
981 return GLFW_TRUE;
982}
983
984void _glfwPlatformDestroyWindow(_GLFWwindow* window)
985{
986 if (window == _glfw.wl.pointerFocus)
987 {
988 _glfw.wl.pointerFocus = NULL;
989 _glfwInputCursorEnter(window, GLFW_FALSE);
990 }
991 if (window == _glfw.wl.keyboardFocus)
992 {
993 _glfw.wl.keyboardFocus = NULL;
994 _glfwInputWindowFocus(window, GLFW_FALSE);
995 }
996
997 if (window->wl.idleInhibitor)
998 zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor);
999
1000 if (window->context.destroy)
1001 window->context.destroy(window);
1002
1003 destroyDecorations(window);
1004 if (window->wl.xdg.decoration)
1005 zxdg_toplevel_decoration_v1_destroy(window->wl.xdg.decoration);
1006
1007 if (window->wl.decorations.buffer)
1008 wl_buffer_destroy(window->wl.decorations.buffer);
1009
1010 if (window->wl.native)
1011 wl_egl_window_destroy(window->wl.native);
1012
1013 if (window->wl.shellSurface)
1014 wl_shell_surface_destroy(window->wl.shellSurface);
1015
1016 if (window->wl.xdg.toplevel)
1017 xdg_toplevel_destroy(window->wl.xdg.toplevel);
1018
1019 if (window->wl.xdg.surface)
1020 xdg_surface_destroy(window->wl.xdg.surface);
1021
1022 if (window->wl.surface)
1023 wl_surface_destroy(window->wl.surface);
1024
1025 free(window->wl.title);
1026 free(window->wl.monitors);
1027}
1028
1029void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
1030{
1031 if (window->wl.title)
1032 free(window->wl.title);
1033 window->wl.title = _glfw_strdup(title);
1034 if (window->wl.xdg.toplevel)
1035 xdg_toplevel_set_title(window->wl.xdg.toplevel, title);
1036 else if (window->wl.shellSurface)
1037 wl_shell_surface_set_title(window->wl.shellSurface, title);
1038}
1039
1040void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
1041 int count, const GLFWimage* images)
1042{
1043 _glfwInputError(GLFW_PLATFORM_ERROR,
1044 "Wayland: Setting window icon not supported");
1045}
1046
1047void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
1048{
1049 // A Wayland client is not aware of its position, so just warn and leave it
1050 // as (0, 0)
1051
1052 _glfwInputError(GLFW_PLATFORM_ERROR,
1053 "Wayland: Window position retrieval not supported");
1054}
1055
1056void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
1057{
1058 // A Wayland client can not set its position, so just warn
1059
1060 _glfwInputError(GLFW_PLATFORM_ERROR,
1061 "Wayland: Window position setting not supported");
1062}
1063
1064void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
1065{
1066 if (width)
1067 *width = window->wl.width;
1068 if (height)
1069 *height = window->wl.height;
1070}
1071
1072void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
1073{
1074 window->wl.width = width;
1075 window->wl.height = height;
1076 resizeWindow(window);
1077}
1078
1079void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
1080 int minwidth, int minheight,
1081 int maxwidth, int maxheight)
1082{
1083 if (_glfw.wl.wmBase)
1084 {
1085 if (window->wl.xdg.toplevel)
1086 {
1087 if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE)
1088 minwidth = minheight = 0;
1089 if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)
1090 maxwidth = maxheight = 0;
1091 xdg_toplevel_set_min_size(window->wl.xdg.toplevel, minwidth, minheight);
1092 xdg_toplevel_set_max_size(window->wl.xdg.toplevel, maxwidth, maxheight);
1093 wl_surface_commit(window->wl.surface);
1094 }
1095 }
1096 else
1097 {
1098 // TODO: find out how to trigger a resize.
1099 // The actual limits are checked in the wl_shell_surface::configure handler.
1100 }
1101}
1102
1103void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window,
1104 int numer, int denom)
1105{
1106 // TODO: find out how to trigger a resize.
1107 // The actual limits are checked in the wl_shell_surface::configure handler.
1108}
1109
1110void _glfwPlatformGetFramebufferSize(_GLFWwindow* window,
1111 int* width, int* height)
1112{
1113 _glfwPlatformGetWindowSize(window, width, height);
1114 *width *= window->wl.scale;
1115 *height *= window->wl.scale;
1116}
1117
1118void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
1119 int* left, int* top,
1120 int* right, int* bottom)
1121{
1122 if (window->decorated && !window->monitor && !window->wl.decorations.serverSide)
1123 {
1124 if (top)
1125 *top = _GLFW_DECORATION_TOP;
1126 if (left)
1127 *left = _GLFW_DECORATION_WIDTH;
1128 if (right)
1129 *right = _GLFW_DECORATION_WIDTH;
1130 if (bottom)
1131 *bottom = _GLFW_DECORATION_WIDTH;
1132 }
1133}
1134
1135void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
1136 float* xscale, float* yscale)
1137{
1138 if (xscale)
1139 *xscale = (float) window->wl.scale;
1140 if (yscale)
1141 *yscale = (float) window->wl.scale;
1142}
1143
1144void _glfwPlatformIconifyWindow(_GLFWwindow* window)
1145{
1146 if (_glfw.wl.wmBase)
1147 {
1148 if (window->wl.xdg.toplevel)
1149 xdg_toplevel_set_minimized(window->wl.xdg.toplevel);
1150 }
1151 else
1152 {
1153 _glfwInputError(GLFW_PLATFORM_ERROR,
1154 "Wayland: Iconify window not supported on wl_shell");
1155 }
1156}
1157
1158void _glfwPlatformRestoreWindow(_GLFWwindow* window)
1159{
1160 if (window->wl.xdg.toplevel)
1161 {
1162 if (window->monitor)
1163 xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
1164 if (window->wl.maximized)
1165 xdg_toplevel_unset_maximized(window->wl.xdg.toplevel);
1166 // There is no way to unset minimized, or even to know if we are
1167 // minimized, so there is nothing to do here.
1168 }
1169 else if (window->wl.shellSurface)
1170 {
1171 if (window->monitor || window->wl.maximized)
1172 wl_shell_surface_set_toplevel(window->wl.shellSurface);
1173 }
1174 _glfwInputWindowMonitor(window, NULL);
1175 window->wl.maximized = GLFW_FALSE;
1176}
1177
1178void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
1179{
1180 if (window->wl.xdg.toplevel)
1181 {
1182 xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
1183 }
1184 else if (window->wl.shellSurface)
1185 {
1186 // Let the compositor select the best output.
1187 wl_shell_surface_set_maximized(window->wl.shellSurface, NULL);
1188 }
1189 window->wl.maximized = GLFW_TRUE;
1190}
1191
1192void _glfwPlatformShowWindow(_GLFWwindow* window)
1193{
1194 if (!window->wl.visible)
1195 {
1196 if (_glfw.wl.wmBase)
1197 createXdgSurface(window);
1198 else if (!window->wl.shellSurface)
1199 createShellSurface(window);
1200 window->wl.visible = GLFW_TRUE;
1201 }
1202}
1203
1204void _glfwPlatformHideWindow(_GLFWwindow* window)
1205{
1206 if (window->wl.xdg.toplevel)
1207 {
1208 xdg_toplevel_destroy(window->wl.xdg.toplevel);
1209 xdg_surface_destroy(window->wl.xdg.surface);
1210 window->wl.xdg.toplevel = NULL;
1211 window->wl.xdg.surface = NULL;
1212 }
1213 else if (window->wl.shellSurface)
1214 {
1215 wl_shell_surface_destroy(window->wl.shellSurface);
1216 window->wl.shellSurface = NULL;
1217 }
1218 window->wl.visible = GLFW_FALSE;
1219}
1220
1221void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
1222{
1223 // TODO
1224 _glfwInputError(GLFW_PLATFORM_ERROR,
1225 "Wayland: Window attention request not implemented yet");
1226}
1227
1228void _glfwPlatformFocusWindow(_GLFWwindow* window)
1229{
1230 _glfwInputError(GLFW_PLATFORM_ERROR,
1231 "Wayland: Focusing a window requires user interaction");
1232}
1233
1234void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
1235 _GLFWmonitor* monitor,
1236 int xpos, int ypos,
1237 int width, int height,
1238 int refreshRate)
1239{
1240 if (monitor)
1241 {
1242 setFullscreen(window, monitor, refreshRate);
1243 }
1244 else
1245 {
1246 if (window->wl.xdg.toplevel)
1247 xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
1248 else if (window->wl.shellSurface)
1249 wl_shell_surface_set_toplevel(window->wl.shellSurface);
1250 setIdleInhibitor(window, GLFW_FALSE);
1251 if (!_glfw.wl.decorationManager)
1252 createDecorations(window);
1253 }
1254 _glfwInputWindowMonitor(window, monitor);
1255}
1256
1257int _glfwPlatformWindowFocused(_GLFWwindow* window)
1258{
1259 return _glfw.wl.keyboardFocus == window;
1260}
1261
1262int _glfwPlatformWindowIconified(_GLFWwindow* window)
1263{
1264 // wl_shell doesn't have any iconified concept, and xdg-shell doesn’t give
1265 // any way to request whether a surface is iconified.
1266 return GLFW_FALSE;
1267}
1268
1269int _glfwPlatformWindowVisible(_GLFWwindow* window)
1270{
1271 return window->wl.visible;
1272}
1273
1274int _glfwPlatformWindowMaximized(_GLFWwindow* window)
1275{
1276 return window->wl.maximized;
1277}
1278
1279int _glfwPlatformWindowHovered(_GLFWwindow* window)
1280{
1281 return window->wl.hovered;
1282}
1283
1284int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
1285{
1286 return window->wl.transparent;
1287}
1288
1289void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
1290{
1291 // TODO
1292 _glfwInputError(GLFW_PLATFORM_ERROR,
1293 "Wayland: Window attribute setting not implemented yet");
1294}
1295
1296void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
1297{
1298 if (!window->monitor)
1299 {
1300 if (enabled)
1301 createDecorations(window);
1302 else
1303 destroyDecorations(window);
1304 }
1305}
1306
1307void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
1308{
1309 // TODO
1310 _glfwInputError(GLFW_PLATFORM_ERROR,
1311 "Wayland: Window attribute setting not implemented yet");
1312}
1313
1314float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
1315{
1316 return 1.f;
1317}
1318
1319void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
1320{
1321}
1322
1323void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled)
1324{
1325 // This is handled in relativePointerHandleRelativeMotion
1326}
1327
1328GLFWbool _glfwPlatformRawMouseMotionSupported(void)
1329{
1330 return GLFW_TRUE;
1331}
1332
1333void _glfwPlatformPollEvents(void)
1334{
1335 handleEvents(0);
1336}
1337
1338void _glfwPlatformWaitEvents(void)
1339{
1340 handleEvents(-1);
1341}
1342
1343void _glfwPlatformWaitEventsTimeout(double timeout)
1344{
1345 handleEvents((int) (timeout * 1e3));
1346}
1347
1348void _glfwPlatformPostEmptyEvent(void)
1349{
1350 wl_display_sync(_glfw.wl.display);
1351}
1352
1353void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
1354{
1355 if (xpos)
1356 *xpos = window->wl.cursorPosX;
1357 if (ypos)
1358 *ypos = window->wl.cursorPosY;
1359}
1360
1361static GLFWbool isPointerLocked(_GLFWwindow* window);
1362
1363void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
1364{
1365 if (isPointerLocked(window))
1366 {
1367 zwp_locked_pointer_v1_set_cursor_position_hint(
1368 window->wl.pointerLock.lockedPointer,
1369 wl_fixed_from_double(x), wl_fixed_from_double(y));
1370 wl_surface_commit(window->wl.surface);
1371 }
1372}
1373
1374void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
1375{
1376 _glfwPlatformSetCursor(window, window->wl.currentCursor);
1377}
1378
1379const char* _glfwPlatformGetScancodeName(int scancode)
1380{
1381 // TODO
1382 return NULL;
1383}
1384
1385int _glfwPlatformGetKeyScancode(int key)
1386{
1387 return _glfw.wl.scancodes[key];
1388}
1389
1390int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
1391 const GLFWimage* image,
1392 int xhot, int yhot)
1393{
1394 cursor->wl.buffer = createShmBuffer(image);
1395 if (!cursor->wl.buffer)
1396 return GLFW_FALSE;
1397
1398 cursor->wl.width = image->width;
1399 cursor->wl.height = image->height;
1400 cursor->wl.xhot = xhot;
1401 cursor->wl.yhot = yhot;
1402 return GLFW_TRUE;
1403}
1404
1405int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
1406{
1407 struct wl_cursor* standardCursor;
1408
1409 standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme,
1410 translateCursorShape(shape));
1411 if (!standardCursor)
1412 {
1413 _glfwInputError(GLFW_PLATFORM_ERROR,
1414 "Wayland: Standard cursor \"%s\" not found",
1415 translateCursorShape(shape));
1416 return GLFW_FALSE;
1417 }
1418
1419 cursor->wl.cursor = standardCursor;
1420 cursor->wl.currentImage = 0;
1421
1422 if (_glfw.wl.cursorThemeHiDPI)
1423 {
1424 standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI,
1425 translateCursorShape(shape));
1426 cursor->wl.cursorHiDPI = standardCursor;
1427 }
1428
1429 return GLFW_TRUE;
1430}
1431
1432void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
1433{
1434 // If it's a standard cursor we don't need to do anything here
1435 if (cursor->wl.cursor)
1436 return;
1437
1438 if (cursor->wl.buffer)
1439 wl_buffer_destroy(cursor->wl.buffer);
1440}
1441
1442static void relativePointerHandleRelativeMotion(void* data,
1443 struct zwp_relative_pointer_v1* pointer,
1444 uint32_t timeHi,
1445 uint32_t timeLo,
1446 wl_fixed_t dx,
1447 wl_fixed_t dy,
1448 wl_fixed_t dxUnaccel,
1449 wl_fixed_t dyUnaccel)
1450{
1451 _GLFWwindow* window = data;
1452 double xpos = window->virtualCursorPosX;
1453 double ypos = window->virtualCursorPosY;
1454
1455 if (window->cursorMode != GLFW_CURSOR_DISABLED)
1456 return;
1457
1458 if (window->rawMouseMotion)
1459 {
1460 xpos += wl_fixed_to_double(dxUnaccel);
1461 ypos += wl_fixed_to_double(dyUnaccel);
1462 }
1463 else
1464 {
1465 xpos += wl_fixed_to_double(dx);
1466 ypos += wl_fixed_to_double(dy);
1467 }
1468
1469 _glfwInputCursorPos(window, xpos, ypos);
1470}
1471
1472static const struct zwp_relative_pointer_v1_listener relativePointerListener = {
1473 relativePointerHandleRelativeMotion
1474};
1475
1476static void lockedPointerHandleLocked(void* data,
1477 struct zwp_locked_pointer_v1* lockedPointer)
1478{
1479}
1480
1481static void unlockPointer(_GLFWwindow* window)
1482{
1483 struct zwp_relative_pointer_v1* relativePointer =
1484 window->wl.pointerLock.relativePointer;
1485 struct zwp_locked_pointer_v1* lockedPointer =
1486 window->wl.pointerLock.lockedPointer;
1487
1488 zwp_relative_pointer_v1_destroy(relativePointer);
1489 zwp_locked_pointer_v1_destroy(lockedPointer);
1490
1491 window->wl.pointerLock.relativePointer = NULL;
1492 window->wl.pointerLock.lockedPointer = NULL;
1493}
1494
1495static void lockPointer(_GLFWwindow* window);
1496
1497static void lockedPointerHandleUnlocked(void* data,
1498 struct zwp_locked_pointer_v1* lockedPointer)
1499{
1500}
1501
1502static const struct zwp_locked_pointer_v1_listener lockedPointerListener = {
1503 lockedPointerHandleLocked,
1504 lockedPointerHandleUnlocked
1505};
1506
1507static void lockPointer(_GLFWwindow* window)
1508{
1509 struct zwp_relative_pointer_v1* relativePointer;
1510 struct zwp_locked_pointer_v1* lockedPointer;
1511
1512 if (!_glfw.wl.relativePointerManager)
1513 {
1514 _glfwInputError(GLFW_PLATFORM_ERROR,
1515 "Wayland: no relative pointer manager");
1516 return;
1517 }
1518
1519 relativePointer =
1520 zwp_relative_pointer_manager_v1_get_relative_pointer(
1521 _glfw.wl.relativePointerManager,
1522 _glfw.wl.pointer);
1523 zwp_relative_pointer_v1_add_listener(relativePointer,
1524 &relativePointerListener,
1525 window);
1526
1527 lockedPointer =
1528 zwp_pointer_constraints_v1_lock_pointer(
1529 _glfw.wl.pointerConstraints,
1530 window->wl.surface,
1531 _glfw.wl.pointer,
1532 NULL,
1533 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
1534 zwp_locked_pointer_v1_add_listener(lockedPointer,
1535 &lockedPointerListener,
1536 window);
1537
1538 window->wl.pointerLock.relativePointer = relativePointer;
1539 window->wl.pointerLock.lockedPointer = lockedPointer;
1540
1541 wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
1542 NULL, 0, 0);
1543}
1544
1545static GLFWbool isPointerLocked(_GLFWwindow* window)
1546{
1547 return window->wl.pointerLock.lockedPointer != NULL;
1548}
1549
1550void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
1551{
1552 struct wl_cursor* defaultCursor;
1553 struct wl_cursor* defaultCursorHiDPI = NULL;
1554
1555 if (!_glfw.wl.pointer)
1556 return;
1557
1558 window->wl.currentCursor = cursor;
1559
1560 // If we're not in the correct window just save the cursor
1561 // the next time the pointer enters the window the cursor will change
1562 if (window != _glfw.wl.pointerFocus || window->wl.decorations.focus != mainWindow)
1563 return;
1564
1565 // Unlock possible pointer lock if no longer disabled.
1566 if (window->cursorMode != GLFW_CURSOR_DISABLED && isPointerLocked(window))
1567 unlockPointer(window);
1568
1569 if (window->cursorMode == GLFW_CURSOR_NORMAL)
1570 {
1571 if (cursor)
1572 setCursorImage(window, &cursor->wl);
1573 else
1574 {
1575 defaultCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme,
1576 "left_ptr");
1577 if (!defaultCursor)
1578 {
1579 _glfwInputError(GLFW_PLATFORM_ERROR,
1580 "Wayland: Standard cursor not found");
1581 return;
1582 }
1583 if (_glfw.wl.cursorThemeHiDPI)
1584 defaultCursorHiDPI =
1585 wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI,
1586 "left_ptr");
1587 _GLFWcursorWayland cursorWayland = {
1588 defaultCursor,
1589 defaultCursorHiDPI,
1590 NULL,
1591 0, 0,
1592 0, 0,
1593 0
1594 };
1595 setCursorImage(window, &cursorWayland);
1596 }
1597 }
1598 else if (window->cursorMode == GLFW_CURSOR_DISABLED)
1599 {
1600 if (!isPointerLocked(window))
1601 lockPointer(window);
1602 }
1603 else if (window->cursorMode == GLFW_CURSOR_HIDDEN)
1604 {
1605 wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, NULL, 0, 0);
1606 }
1607}
1608
1609static void dataSourceHandleTarget(void* data,
1610 struct wl_data_source* dataSource,
1611 const char* mimeType)
1612{
1613 if (_glfw.wl.dataSource != dataSource)
1614 {
1615 _glfwInputError(GLFW_PLATFORM_ERROR,
1616 "Wayland: Unknown clipboard data source");
1617 return;
1618 }
1619}
1620
1621static void dataSourceHandleSend(void* data,
1622 struct wl_data_source* dataSource,
1623 const char* mimeType,
1624 int fd)
1625{
1626 const char* string = _glfw.wl.clipboardSendString;
1627 size_t len = _glfw.wl.clipboardSendSize;
1628 int ret;
1629
1630 if (_glfw.wl.dataSource != dataSource)
1631 {
1632 _glfwInputError(GLFW_PLATFORM_ERROR,
1633 "Wayland: Unknown clipboard data source");
1634 return;
1635 }
1636
1637 if (!string)
1638 {
1639 _glfwInputError(GLFW_PLATFORM_ERROR,
1640 "Wayland: Copy requested from an invalid string");
1641 return;
1642 }
1643
1644 if (strcmp(mimeType, "text/plain;charset=utf-8") != 0)
1645 {
1646 _glfwInputError(GLFW_PLATFORM_ERROR,
1647 "Wayland: Wrong MIME type asked from clipboard");
1648 close(fd);
1649 return;
1650 }
1651
1652 while (len > 0)
1653 {
1654 ret = write(fd, string, len);
1655 if (ret == -1 && errno == EINTR)
1656 continue;
1657 if (ret == -1)
1658 {
1659 // TODO: also report errno maybe.
1660 _glfwInputError(GLFW_PLATFORM_ERROR,
1661 "Wayland: Error while writing the clipboard");
1662 close(fd);
1663 return;
1664 }
1665 len -= ret;
1666 }
1667 close(fd);
1668}
1669
1670static void dataSourceHandleCancelled(void* data,
1671 struct wl_data_source* dataSource)
1672{
1673 wl_data_source_destroy(dataSource);
1674
1675 if (_glfw.wl.dataSource != dataSource)
1676 {
1677 _glfwInputError(GLFW_PLATFORM_ERROR,
1678 "Wayland: Unknown clipboard data source");
1679 return;
1680 }
1681
1682 _glfw.wl.dataSource = NULL;
1683}
1684
1685static const struct wl_data_source_listener dataSourceListener = {
1686 dataSourceHandleTarget,
1687 dataSourceHandleSend,
1688 dataSourceHandleCancelled,
1689};
1690
1691void _glfwPlatformSetClipboardString(const char* string)
1692{
1693 if (_glfw.wl.dataSource)
1694 {
1695 wl_data_source_destroy(_glfw.wl.dataSource);
1696 _glfw.wl.dataSource = NULL;
1697 }
1698
1699 if (_glfw.wl.clipboardSendString)
1700 {
1701 free(_glfw.wl.clipboardSendString);
1702 _glfw.wl.clipboardSendString = NULL;
1703 }
1704
1705 _glfw.wl.clipboardSendString = strdup(string);
1706 if (!_glfw.wl.clipboardSendString)
1707 {
1708 _glfwInputError(GLFW_PLATFORM_ERROR,
1709 "Wayland: Impossible to allocate clipboard string");
1710 return;
1711 }
1712 _glfw.wl.clipboardSendSize = strlen(string);
1713 _glfw.wl.dataSource =
1714 wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager);
1715 if (!_glfw.wl.dataSource)
1716 {
1717 _glfwInputError(GLFW_PLATFORM_ERROR,
1718 "Wayland: Impossible to create clipboard source");
1719 free(_glfw.wl.clipboardSendString);
1720 return;
1721 }
1722 wl_data_source_add_listener(_glfw.wl.dataSource,
1723 &dataSourceListener,
1724 NULL);
1725 wl_data_source_offer(_glfw.wl.dataSource, "text/plain;charset=utf-8");
1726 wl_data_device_set_selection(_glfw.wl.dataDevice,
1727 _glfw.wl.dataSource,
1728 _glfw.wl.serial);
1729}
1730
1731static GLFWbool growClipboardString(void)
1732{
1733 char* clipboard = _glfw.wl.clipboardString;
1734
1735 clipboard = realloc(clipboard, _glfw.wl.clipboardSize * 2);
1736 if (!clipboard)
1737 {
1738 _glfwInputError(GLFW_PLATFORM_ERROR,
1739 "Wayland: Impossible to grow clipboard string");
1740 return GLFW_FALSE;
1741 }
1742 _glfw.wl.clipboardString = clipboard;
1743 _glfw.wl.clipboardSize = _glfw.wl.clipboardSize * 2;
1744 return GLFW_TRUE;
1745}
1746
1747const char* _glfwPlatformGetClipboardString(void)
1748{
1749 int fds[2];
1750 int ret;
1751 size_t len = 0;
1752
1753 if (!_glfw.wl.dataOffer)
1754 {
1755 _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
1756 "No clipboard data has been sent yet");
1757 return NULL;
1758 }
1759
1760 ret = pipe2(fds, O_CLOEXEC);
1761 if (ret < 0)
1762 {
1763 // TODO: also report errno maybe?
1764 _glfwInputError(GLFW_PLATFORM_ERROR,
1765 "Wayland: Impossible to create clipboard pipe fds");
1766 return NULL;
1767 }
1768
1769 wl_data_offer_receive(_glfw.wl.dataOffer, "text/plain;charset=utf-8", fds[1]);
1770 close(fds[1]);
1771
1772 // XXX: this is a huge hack, this function shouldn’t be synchronous!
1773 handleEvents(-1);
1774
1775 while (1)
1776 {
1777 // Grow the clipboard if we need to paste something bigger, there is no
1778 // shrink operation yet.
1779 if (len + 4096 > _glfw.wl.clipboardSize)
1780 {
1781 if (!growClipboardString())
1782 {
1783 close(fds[0]);
1784 return NULL;
1785 }
1786 }
1787
1788 // Then read from the fd to the clipboard, handling all known errors.
1789 ret = read(fds[0], _glfw.wl.clipboardString + len, 4096);
1790 if (ret == 0)
1791 break;
1792 if (ret == -1 && errno == EINTR)
1793 continue;
1794 if (ret == -1)
1795 {
1796 // TODO: also report errno maybe.
1797 _glfwInputError(GLFW_PLATFORM_ERROR,
1798 "Wayland: Impossible to read from clipboard fd");
1799 close(fds[0]);
1800 return NULL;
1801 }
1802 len += ret;
1803 }
1804 close(fds[0]);
1805 if (len + 1 > _glfw.wl.clipboardSize)
1806 {
1807 if (!growClipboardString())
1808 return NULL;
1809 }
1810 _glfw.wl.clipboardString[len] = '\0';
1811 return _glfw.wl.clipboardString;
1812}
1813
1814void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
1815{
1816 if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_wayland_surface)
1817 return;
1818
1819 extensions[0] = "VK_KHR_surface";
1820 extensions[1] = "VK_KHR_wayland_surface";
1821}
1822
1823int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
1824 VkPhysicalDevice device,
1825 uint32_t queuefamily)
1826{
1827 PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
1828 vkGetPhysicalDeviceWaylandPresentationSupportKHR =
1829 (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)
1830 vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR");
1831 if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR)
1832 {
1833 _glfwInputError(GLFW_API_UNAVAILABLE,
1834 "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension");
1835 return VK_NULL_HANDLE;
1836 }
1837
1838 return vkGetPhysicalDeviceWaylandPresentationSupportKHR(device,
1839 queuefamily,
1840 _glfw.wl.display);
1841}
1842
1843VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
1844 _GLFWwindow* window,
1845 const VkAllocationCallbacks* allocator,
1846 VkSurfaceKHR* surface)
1847{
1848 VkResult err;
1849 VkWaylandSurfaceCreateInfoKHR sci;
1850 PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR;
1851
1852 vkCreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR)
1853 vkGetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR");
1854 if (!vkCreateWaylandSurfaceKHR)
1855 {
1856 _glfwInputError(GLFW_API_UNAVAILABLE,
1857 "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension");
1858 return VK_ERROR_EXTENSION_NOT_PRESENT;
1859 }
1860
1861 memset(&sci, 0, sizeof(sci));
1862 sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
1863 sci.display = _glfw.wl.display;
1864 sci.surface = window->wl.surface;
1865
1866 err = vkCreateWaylandSurfaceKHR(instance, &sci, allocator, surface);
1867 if (err)
1868 {
1869 _glfwInputError(GLFW_PLATFORM_ERROR,
1870 "Wayland: Failed to create Vulkan surface: %s",
1871 _glfwGetVulkanResultString(err));
1872 }
1873
1874 return err;
1875}
1876
1877
1878//////////////////////////////////////////////////////////////////////////
1879////// GLFW native API //////
1880//////////////////////////////////////////////////////////////////////////
1881
1882GLFWAPI struct wl_display* glfwGetWaylandDisplay(void)
1883{
1884 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
1885 return _glfw.wl.display;
1886}
1887
1888GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle)
1889{
1890 _GLFWwindow* window = (_GLFWwindow*) handle;
1891 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
1892 return window->wl.surface;
1893}
1894