1 //========================================================================
2 // GLFW 3.5 Wayland - www.glfw.org
3 //------------------------------------------------------------------------
4 // Copyright (c) 2014 Jonas Ådahl <jadahl@gmail.com>
5 //
6 // This software is provided 'as-is', without any express or implied
7 // warranty. In no event will the authors be held liable for any damages
8 // arising from the use of this software.
9 //
10 // Permission is granted to anyone to use this software for any purpose,
11 // including commercial applications, and to alter it and redistribute it
12 // freely, subject to the following restrictions:
13 //
14 // 1. The origin of this software must not be misrepresented; you must not
15 // claim that you wrote the original software. If you use this software
16 // in a product, an acknowledgment in the product documentation would
17 // be appreciated but is not required.
18 //
19 // 2. Altered source versions must be plainly marked as such, and must not
20 // be misrepresented as being the original software.
21 //
22 // 3. This notice may not be removed or altered from any source
23 // distribution.
24 //
25 //========================================================================
26
27 #define _GNU_SOURCE
28
29 #include "internal.h"
30
31 #if defined(_GLFW_WAYLAND)
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <errno.h>
36 #include <assert.h>
37 #include <unistd.h>
38 #include <string.h>
39 #include <fcntl.h>
40 #include <sys/mman.h>
41 #include <sys/timerfd.h>
42 #include <poll.h>
43 #include <linux/input-event-codes.h>
44
45 #include "wayland-client-protocol.h"
46 #include "xdg-shell-client-protocol.h"
47 #include "xdg-decoration-unstable-v1-client-protocol.h"
48 #include "viewporter-client-protocol.h"
49 #include "relative-pointer-unstable-v1-client-protocol.h"
50 #include "pointer-constraints-unstable-v1-client-protocol.h"
51 #include "xdg-activation-v1-client-protocol.h"
52 #include "idle-inhibit-unstable-v1-client-protocol.h"
53 #include "fractional-scale-v1-client-protocol.h"
54
55 #define GLFW_BORDER_SIZE 4
56 #define GLFW_CAPTION_HEIGHT 24
57
createTmpfileCloexec(char * tmpname)58 static int createTmpfileCloexec(char* tmpname)
59 {
60 int fd;
61
62 fd = mkostemp(tmpname, O_CLOEXEC);
63 if (fd >= 0)
64 unlink(tmpname);
65
66 return fd;
67 }
68
69 /*
70 * Create a new, unique, anonymous file of the given size, and
71 * return the file descriptor for it. The file descriptor is set
72 * CLOEXEC. The file is immediately suitable for mmap()'ing
73 * the given size at offset zero.
74 *
75 * The file should not have a permanent backing store like a disk,
76 * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
77 *
78 * The file name is deleted from the file system.
79 *
80 * The file is suitable for buffer sharing between processes by
81 * transmitting the file descriptor over Unix sockets using the
82 * SCM_RIGHTS methods.
83 *
84 * posix_fallocate() is used to guarantee that disk space is available
85 * for the file at the given size. If disk space is insufficient, errno
86 * is set to ENOSPC. If posix_fallocate() is not supported, program may
87 * receive SIGBUS on accessing mmap()'ed file contents instead.
88 */
createAnonymousFile(off_t size)89 static int createAnonymousFile(off_t size)
90 {
91 static const char template[] = "/glfw-shared-XXXXXX";
92 const char* path;
93 char* name;
94 int fd;
95 int ret;
96
97 #ifdef HAVE_MEMFD_CREATE
98 fd = memfd_create("glfw-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING);
99 if (fd >= 0)
100 {
101 // We can add this seal before calling posix_fallocate(), as the file
102 // is currently zero-sized anyway.
103 //
104 // There is also no need to check for the return value, we couldn’t do
105 // anything with it anyway.
106 fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
107 }
108 else
109 #elif defined(SHM_ANON)
110 fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600);
111 if (fd < 0)
112 #endif
113 {
114 path = getenv("XDG_RUNTIME_DIR");
115 if (!path)
116 {
117 errno = ENOENT;
118 return -1;
119 }
120
121 name = _glfw_calloc(strlen(path) + sizeof(template), 1);
122 strcpy(name, path);
123 strcat(name, template);
124
125 fd = createTmpfileCloexec(name);
126 _glfw_free(name);
127 if (fd < 0)
128 return -1;
129 }
130
131 #if defined(SHM_ANON)
132 // posix_fallocate does not work on SHM descriptors
133 ret = ftruncate(fd, size);
134 #else
135 ret = posix_fallocate(fd, 0, size);
136 #endif
137 if (ret != 0)
138 {
139 close(fd);
140 errno = ret;
141 return -1;
142 }
143 return fd;
144 }
145
createShmBuffer(const GLFWimage * image)146 static struct wl_buffer* createShmBuffer(const GLFWimage* image)
147 {
148 const int stride = image->width * 4;
149 const int length = image->width * image->height * 4;
150
151 const int fd = createAnonymousFile(length);
152 if (fd < 0)
153 {
154 _glfwInputError(GLFW_PLATFORM_ERROR,
155 "Wayland: Failed to create buffer file of size %d: %s",
156 length, strerror(errno));
157 return NULL;
158 }
159
160 void* data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
161 if (data == MAP_FAILED)
162 {
163 _glfwInputError(GLFW_PLATFORM_ERROR,
164 "Wayland: Failed to map file: %s", strerror(errno));
165 close(fd);
166 return NULL;
167 }
168
169 struct wl_shm_pool* pool = wl_shm_create_pool(_glfw.wl.shm, fd, length);
170
171 close(fd);
172
173 unsigned char* source = (unsigned char*) image->pixels;
174 unsigned char* target = data;
175 for (int i = 0; i < image->width * image->height; i++, source += 4)
176 {
177 unsigned int alpha = source[3];
178
179 *target++ = (unsigned char) ((source[2] * alpha) / 255);
180 *target++ = (unsigned char) ((source[1] * alpha) / 255);
181 *target++ = (unsigned char) ((source[0] * alpha) / 255);
182 *target++ = (unsigned char) alpha;
183 }
184
185 struct wl_buffer* buffer =
186 wl_shm_pool_create_buffer(pool, 0,
187 image->width,
188 image->height,
189 stride, WL_SHM_FORMAT_ARGB8888);
190 munmap(data, length);
191 wl_shm_pool_destroy(pool);
192
193 return buffer;
194 }
195
createFallbackEdge(_GLFWwindow * window,_GLFWfallbackEdgeWayland * edge,struct wl_surface * parent,struct wl_buffer * buffer,int x,int y,int width,int height)196 static void createFallbackEdge(_GLFWwindow* window,
197 _GLFWfallbackEdgeWayland* edge,
198 struct wl_surface* parent,
199 struct wl_buffer* buffer,
200 int x, int y,
201 int width, int height)
202 {
203 edge->surface = wl_compositor_create_surface(_glfw.wl.compositor);
204 wl_surface_set_user_data(edge->surface, window);
205 wl_proxy_set_tag((struct wl_proxy*) edge->surface, &_glfw.wl.tag);
206 edge->subsurface = wl_subcompositor_get_subsurface(_glfw.wl.subcompositor,
207 edge->surface, parent);
208 wl_subsurface_set_position(edge->subsurface, x, y);
209 edge->viewport = wp_viewporter_get_viewport(_glfw.wl.viewporter,
210 edge->surface);
211 wp_viewport_set_destination(edge->viewport, width, height);
212 wl_surface_attach(edge->surface, buffer, 0, 0);
213
214 struct wl_region* region = wl_compositor_create_region(_glfw.wl.compositor);
215 wl_region_add(region, 0, 0, width, height);
216 wl_surface_set_opaque_region(edge->surface, region);
217 wl_surface_commit(edge->surface);
218 wl_region_destroy(region);
219 }
220
createFallbackDecorations(_GLFWwindow * window)221 static void createFallbackDecorations(_GLFWwindow* window)
222 {
223 unsigned char data[] = { 224, 224, 224, 255 };
224 const GLFWimage image = { 1, 1, data };
225
226 if (!_glfw.wl.viewporter)
227 return;
228
229 if (!window->wl.fallback.buffer)
230 window->wl.fallback.buffer = createShmBuffer(&image);
231 if (!window->wl.fallback.buffer)
232 return;
233
234 createFallbackEdge(window, &window->wl.fallback.top, window->wl.surface,
235 window->wl.fallback.buffer,
236 0, -GLFW_CAPTION_HEIGHT,
237 window->wl.width, GLFW_CAPTION_HEIGHT);
238 createFallbackEdge(window, &window->wl.fallback.left, window->wl.surface,
239 window->wl.fallback.buffer,
240 -GLFW_BORDER_SIZE, -GLFW_CAPTION_HEIGHT,
241 GLFW_BORDER_SIZE, window->wl.height + GLFW_CAPTION_HEIGHT);
242 createFallbackEdge(window, &window->wl.fallback.right, window->wl.surface,
243 window->wl.fallback.buffer,
244 window->wl.width, -GLFW_CAPTION_HEIGHT,
245 GLFW_BORDER_SIZE, window->wl.height + GLFW_CAPTION_HEIGHT);
246 createFallbackEdge(window, &window->wl.fallback.bottom, window->wl.surface,
247 window->wl.fallback.buffer,
248 -GLFW_BORDER_SIZE, window->wl.height,
249 window->wl.width + GLFW_BORDER_SIZE * 2, GLFW_BORDER_SIZE);
250
251 window->wl.fallback.decorations = GLFW_TRUE;
252 }
253
destroyFallbackEdge(_GLFWfallbackEdgeWayland * edge)254 static void destroyFallbackEdge(_GLFWfallbackEdgeWayland* edge)
255 {
256 if (edge->subsurface)
257 wl_subsurface_destroy(edge->subsurface);
258 if (edge->surface)
259 wl_surface_destroy(edge->surface);
260 if (edge->viewport)
261 wp_viewport_destroy(edge->viewport);
262
263 edge->surface = NULL;
264 edge->subsurface = NULL;
265 edge->viewport = NULL;
266 }
267
destroyFallbackDecorations(_GLFWwindow * window)268 static void destroyFallbackDecorations(_GLFWwindow* window)
269 {
270 window->wl.fallback.decorations = GLFW_FALSE;
271
272 destroyFallbackEdge(&window->wl.fallback.top);
273 destroyFallbackEdge(&window->wl.fallback.left);
274 destroyFallbackEdge(&window->wl.fallback.right);
275 destroyFallbackEdge(&window->wl.fallback.bottom);
276 }
277
xdgDecorationHandleConfigure(void * userData,struct zxdg_toplevel_decoration_v1 * decoration,uint32_t mode)278 static void xdgDecorationHandleConfigure(void* userData,
279 struct zxdg_toplevel_decoration_v1* decoration,
280 uint32_t mode)
281 {
282 _GLFWwindow* window = userData;
283
284 window->wl.xdg.decorationMode = mode;
285
286 if (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE)
287 {
288 if (window->decorated && !window->monitor)
289 createFallbackDecorations(window);
290 }
291 else
292 destroyFallbackDecorations(window);
293 }
294
295 static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener =
296 {
297 xdgDecorationHandleConfigure,
298 };
299
300 // Makes the surface considered as XRGB instead of ARGB.
setContentAreaOpaque(_GLFWwindow * window)301 static void setContentAreaOpaque(_GLFWwindow* window)
302 {
303 struct wl_region* region;
304
305 region = wl_compositor_create_region(_glfw.wl.compositor);
306 if (!region)
307 return;
308
309 wl_region_add(region, 0, 0, window->wl.width, window->wl.height);
310 wl_surface_set_opaque_region(window->wl.surface, region);
311 wl_region_destroy(region);
312 }
313
resizeFramebuffer(_GLFWwindow * window)314 static void resizeFramebuffer(_GLFWwindow* window)
315 {
316 if (window->wl.fractionalScale)
317 {
318 window->wl.fbWidth = (window->wl.width * window->wl.scalingNumerator) / 120;
319 window->wl.fbHeight = (window->wl.height * window->wl.scalingNumerator) / 120;
320 }
321 else
322 {
323 window->wl.fbWidth = window->wl.width * window->wl.bufferScale;
324 window->wl.fbHeight = window->wl.height * window->wl.bufferScale;
325 }
326
327 if (window->wl.egl.window)
328 {
329 wl_egl_window_resize(window->wl.egl.window,
330 window->wl.fbWidth,
331 window->wl.fbHeight,
332 0, 0);
333 }
334
335 if (!window->wl.transparent)
336 setContentAreaOpaque(window);
337
338 _glfwInputFramebufferSize(window, window->wl.fbWidth, window->wl.fbHeight);
339 }
340
resizeWindow(_GLFWwindow * window,int width,int height)341 static GLFWbool resizeWindow(_GLFWwindow* window, int width, int height)
342 {
343 width = _glfw_max(width, 1);
344 height = _glfw_max(height, 1);
345
346 if (width == window->wl.width && height == window->wl.height)
347 return GLFW_FALSE;
348
349 window->wl.width = width;
350 window->wl.height = height;
351
352 resizeFramebuffer(window);
353
354 if (window->wl.scalingViewport)
355 {
356 wp_viewport_set_destination(window->wl.scalingViewport,
357 window->wl.width,
358 window->wl.height);
359 }
360
361 if (window->wl.fallback.decorations)
362 {
363 wp_viewport_set_destination(window->wl.fallback.top.viewport,
364 window->wl.width,
365 GLFW_CAPTION_HEIGHT);
366 wl_surface_commit(window->wl.fallback.top.surface);
367
368 wp_viewport_set_destination(window->wl.fallback.left.viewport,
369 GLFW_BORDER_SIZE,
370 window->wl.height + GLFW_CAPTION_HEIGHT);
371 wl_surface_commit(window->wl.fallback.left.surface);
372
373 wl_subsurface_set_position(window->wl.fallback.right.subsurface,
374 window->wl.width, -GLFW_CAPTION_HEIGHT);
375 wp_viewport_set_destination(window->wl.fallback.right.viewport,
376 GLFW_BORDER_SIZE,
377 window->wl.height + GLFW_CAPTION_HEIGHT);
378 wl_surface_commit(window->wl.fallback.right.surface);
379
380 wl_subsurface_set_position(window->wl.fallback.bottom.subsurface,
381 -GLFW_BORDER_SIZE, window->wl.height);
382 wp_viewport_set_destination(window->wl.fallback.bottom.viewport,
383 window->wl.width + GLFW_BORDER_SIZE * 2,
384 GLFW_BORDER_SIZE);
385 wl_surface_commit(window->wl.fallback.bottom.surface);
386 }
387
388 return GLFW_TRUE;
389 }
390
_glfwUpdateBufferScaleFromOutputsWayland(_GLFWwindow * window)391 void _glfwUpdateBufferScaleFromOutputsWayland(_GLFWwindow* window)
392 {
393 if (wl_compositor_get_version(_glfw.wl.compositor) <
394 WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION)
395 {
396 return;
397 }
398
399 if (!window->wl.scaleFramebuffer)
400 return;
401
402 // When using fractional scaling, the buffer scale should remain at 1
403 if (window->wl.fractionalScale)
404 return;
405
406 // Get the scale factor from the highest scale monitor.
407 int32_t maxScale = 1;
408
409 for (size_t i = 0; i < window->wl.outputScaleCount; i++)
410 maxScale = _glfw_max(window->wl.outputScales[i].factor, maxScale);
411
412 // Only change the framebuffer size if the scale changed.
413 if (window->wl.bufferScale != maxScale)
414 {
415 window->wl.bufferScale = maxScale;
416 wl_surface_set_buffer_scale(window->wl.surface, maxScale);
417 _glfwInputWindowContentScale(window, maxScale, maxScale);
418 resizeFramebuffer(window);
419
420 if (window->wl.visible)
421 _glfwInputWindowDamage(window);
422 }
423 }
424
surfaceHandleEnter(void * userData,struct wl_surface * surface,struct wl_output * output)425 static void surfaceHandleEnter(void* userData,
426 struct wl_surface* surface,
427 struct wl_output* output)
428 {
429 if (wl_proxy_get_tag((struct wl_proxy*) output) != &_glfw.wl.tag)
430 return;
431
432 _GLFWwindow* window = userData;
433 _GLFWmonitor* monitor = wl_output_get_user_data(output);
434 if (!window || !monitor)
435 return;
436
437 if (window->wl.outputScaleCount + 1 > window->wl.outputScaleSize)
438 {
439 window->wl.outputScaleSize++;
440 window->wl.outputScales =
441 _glfw_realloc(window->wl.outputScales,
442 window->wl.outputScaleSize * sizeof(_GLFWscaleWayland));
443 }
444
445 window->wl.outputScaleCount++;
446 window->wl.outputScales[window->wl.outputScaleCount - 1] =
447 (_GLFWscaleWayland) { output, monitor->wl.scale };
448
449 _glfwUpdateBufferScaleFromOutputsWayland(window);
450 }
451
surfaceHandleLeave(void * userData,struct wl_surface * surface,struct wl_output * output)452 static void surfaceHandleLeave(void* userData,
453 struct wl_surface* surface,
454 struct wl_output* output)
455 {
456 if (wl_proxy_get_tag((struct wl_proxy*) output) != &_glfw.wl.tag)
457 return;
458
459 _GLFWwindow* window = userData;
460
461 for (size_t i = 0; i < window->wl.outputScaleCount; i++)
462 {
463 if (window->wl.outputScales[i].output == output)
464 {
465 window->wl.outputScales[i] =
466 window->wl.outputScales[window->wl.outputScaleCount - 1];
467 window->wl.outputScaleCount--;
468 break;
469 }
470 }
471
472 _glfwUpdateBufferScaleFromOutputsWayland(window);
473 }
474
475 static const struct wl_surface_listener surfaceListener =
476 {
477 surfaceHandleEnter,
478 surfaceHandleLeave
479 };
480
setIdleInhibitor(_GLFWwindow * window,GLFWbool enable)481 static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable)
482 {
483 if (enable && !window->wl.idleInhibitor && _glfw.wl.idleInhibitManager)
484 {
485 window->wl.idleInhibitor =
486 zwp_idle_inhibit_manager_v1_create_inhibitor(
487 _glfw.wl.idleInhibitManager, window->wl.surface);
488 if (!window->wl.idleInhibitor)
489 _glfwInputError(GLFW_PLATFORM_ERROR,
490 "Wayland: Failed to create idle inhibitor");
491 }
492 else if (!enable && window->wl.idleInhibitor)
493 {
494 zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor);
495 window->wl.idleInhibitor = NULL;
496 }
497 }
498
499 // Make the specified window and its video mode active on its monitor
500 //
acquireMonitor(_GLFWwindow * window)501 static void acquireMonitor(_GLFWwindow* window)
502 {
503 if (window->wl.libdecor.frame)
504 {
505 libdecor_frame_set_fullscreen(window->wl.libdecor.frame,
506 window->monitor->wl.output);
507 }
508 else if (window->wl.xdg.toplevel)
509 {
510 xdg_toplevel_set_fullscreen(window->wl.xdg.toplevel,
511 window->monitor->wl.output);
512 }
513
514 setIdleInhibitor(window, GLFW_TRUE);
515
516 if (window->wl.fallback.decorations)
517 destroyFallbackDecorations(window);
518 }
519
520 // Remove the window and restore the original video mode
521 //
releaseMonitor(_GLFWwindow * window)522 static void releaseMonitor(_GLFWwindow* window)
523 {
524 if (window->wl.libdecor.frame)
525 libdecor_frame_unset_fullscreen(window->wl.libdecor.frame);
526 else if (window->wl.xdg.toplevel)
527 xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
528
529 setIdleInhibitor(window, GLFW_FALSE);
530
531 if (!window->wl.libdecor.frame &&
532 window->wl.xdg.decorationMode != ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE)
533 {
534 if (window->decorated)
535 createFallbackDecorations(window);
536 }
537 }
538
fractionalScaleHandlePreferredScale(void * userData,struct wp_fractional_scale_v1 * fractionalScale,uint32_t numerator)539 void fractionalScaleHandlePreferredScale(void* userData,
540 struct wp_fractional_scale_v1* fractionalScale,
541 uint32_t numerator)
542 {
543 _GLFWwindow* window = userData;
544
545 window->wl.scalingNumerator = numerator;
546 _glfwInputWindowContentScale(window, numerator / 120.f, numerator / 120.f);
547 resizeFramebuffer(window);
548
549 if (window->wl.visible)
550 _glfwInputWindowDamage(window);
551 }
552
553 const struct wp_fractional_scale_v1_listener fractionalScaleListener =
554 {
555 fractionalScaleHandlePreferredScale,
556 };
557
xdgToplevelHandleConfigure(void * userData,struct xdg_toplevel * toplevel,int32_t width,int32_t height,struct wl_array * states)558 static void xdgToplevelHandleConfigure(void* userData,
559 struct xdg_toplevel* toplevel,
560 int32_t width,
561 int32_t height,
562 struct wl_array* states)
563 {
564 _GLFWwindow* window = userData;
565 uint32_t* state;
566
567 window->wl.pending.activated = GLFW_FALSE;
568 window->wl.pending.maximized = GLFW_FALSE;
569 window->wl.pending.fullscreen = GLFW_FALSE;
570
571 wl_array_for_each(state, states)
572 {
573 switch (*state)
574 {
575 case XDG_TOPLEVEL_STATE_MAXIMIZED:
576 window->wl.pending.maximized = GLFW_TRUE;
577 break;
578 case XDG_TOPLEVEL_STATE_FULLSCREEN:
579 window->wl.pending.fullscreen = GLFW_TRUE;
580 break;
581 case XDG_TOPLEVEL_STATE_RESIZING:
582 break;
583 case XDG_TOPLEVEL_STATE_ACTIVATED:
584 window->wl.pending.activated = GLFW_TRUE;
585 break;
586 }
587 }
588
589 if (width && height)
590 {
591 if (window->wl.fallback.decorations)
592 {
593 window->wl.pending.width = _glfw_max(0, width - GLFW_BORDER_SIZE * 2);
594 window->wl.pending.height =
595 _glfw_max(0, height - GLFW_BORDER_SIZE - GLFW_CAPTION_HEIGHT);
596 }
597 else
598 {
599 window->wl.pending.width = width;
600 window->wl.pending.height = height;
601 }
602 }
603 else
604 {
605 window->wl.pending.width = window->wl.width;
606 window->wl.pending.height = window->wl.height;
607 }
608 }
609
xdgToplevelHandleClose(void * userData,struct xdg_toplevel * toplevel)610 static void xdgToplevelHandleClose(void* userData,
611 struct xdg_toplevel* toplevel)
612 {
613 _GLFWwindow* window = userData;
614 _glfwInputWindowCloseRequest(window);
615 }
616
617 static const struct xdg_toplevel_listener xdgToplevelListener =
618 {
619 xdgToplevelHandleConfigure,
620 xdgToplevelHandleClose
621 };
622
xdgSurfaceHandleConfigure(void * userData,struct xdg_surface * surface,uint32_t serial)623 static void xdgSurfaceHandleConfigure(void* userData,
624 struct xdg_surface* surface,
625 uint32_t serial)
626 {
627 _GLFWwindow* window = userData;
628
629 xdg_surface_ack_configure(surface, serial);
630
631 if (window->wl.activated != window->wl.pending.activated)
632 {
633 window->wl.activated = window->wl.pending.activated;
634 if (!window->wl.activated)
635 {
636 if (window->monitor && window->autoIconify)
637 xdg_toplevel_set_minimized(window->wl.xdg.toplevel);
638 }
639 }
640
641 if (window->wl.maximized != window->wl.pending.maximized)
642 {
643 window->wl.maximized = window->wl.pending.maximized;
644 _glfwInputWindowMaximize(window, window->wl.maximized);
645 }
646
647 window->wl.fullscreen = window->wl.pending.fullscreen;
648
649 int width = window->wl.pending.width;
650 int height = window->wl.pending.height;
651
652 if (!window->wl.maximized && !window->wl.fullscreen)
653 {
654 if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)
655 {
656 const float aspectRatio = (float) width / (float) height;
657 const float targetRatio = (float) window->numer / (float) window->denom;
658 if (aspectRatio < targetRatio)
659 height = width / targetRatio;
660 else if (aspectRatio > targetRatio)
661 width = height * targetRatio;
662 }
663 }
664
665 if (resizeWindow(window, width, height))
666 {
667 _glfwInputWindowSize(window, window->wl.width, window->wl.height);
668
669 if (window->wl.visible)
670 _glfwInputWindowDamage(window);
671 }
672
673 if (!window->wl.visible)
674 {
675 // Allow the window to be mapped only if it either has no XDG
676 // decorations or they have already received a configure event
677 if (!window->wl.xdg.decoration || window->wl.xdg.decorationMode)
678 {
679 window->wl.visible = GLFW_TRUE;
680 _glfwInputWindowDamage(window);
681 }
682 }
683 }
684
685 static const struct xdg_surface_listener xdgSurfaceListener =
686 {
687 xdgSurfaceHandleConfigure
688 };
689
libdecorFrameHandleConfigure(struct libdecor_frame * frame,struct libdecor_configuration * config,void * userData)690 void libdecorFrameHandleConfigure(struct libdecor_frame* frame,
691 struct libdecor_configuration* config,
692 void* userData)
693 {
694 _GLFWwindow* window = userData;
695 int width, height;
696
697 enum libdecor_window_state windowState;
698 GLFWbool fullscreen, activated, maximized;
699
700 if (libdecor_configuration_get_window_state(config, &windowState))
701 {
702 fullscreen = (windowState & LIBDECOR_WINDOW_STATE_FULLSCREEN) != 0;
703 activated = (windowState & LIBDECOR_WINDOW_STATE_ACTIVE) != 0;
704 maximized = (windowState & LIBDECOR_WINDOW_STATE_MAXIMIZED) != 0;
705 }
706 else
707 {
708 fullscreen = window->wl.fullscreen;
709 activated = window->wl.activated;
710 maximized = window->wl.maximized;
711 }
712
713 if (!libdecor_configuration_get_content_size(config, frame, &width, &height))
714 {
715 width = window->wl.width;
716 height = window->wl.height;
717 }
718
719 if (!maximized && !fullscreen)
720 {
721 if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)
722 {
723 const float aspectRatio = (float) width / (float) height;
724 const float targetRatio = (float) window->numer / (float) window->denom;
725 if (aspectRatio < targetRatio)
726 height = width / targetRatio;
727 else if (aspectRatio > targetRatio)
728 width = height * targetRatio;
729 }
730 }
731
732 struct libdecor_state* frameState = libdecor_state_new(width, height);
733 libdecor_frame_commit(frame, frameState, config);
734 libdecor_state_free(frameState);
735
736 if (window->wl.activated != activated)
737 {
738 window->wl.activated = activated;
739 if (!window->wl.activated)
740 {
741 if (window->monitor && window->autoIconify)
742 libdecor_frame_set_minimized(window->wl.libdecor.frame);
743 }
744 }
745
746 if (window->wl.maximized != maximized)
747 {
748 window->wl.maximized = maximized;
749 _glfwInputWindowMaximize(window, window->wl.maximized);
750 }
751
752 window->wl.fullscreen = fullscreen;
753
754 GLFWbool damaged = GLFW_FALSE;
755
756 if (!window->wl.visible)
757 {
758 window->wl.visible = GLFW_TRUE;
759 damaged = GLFW_TRUE;
760 }
761
762 if (resizeWindow(window, width, height))
763 {
764 _glfwInputWindowSize(window, window->wl.width, window->wl.height);
765 damaged = GLFW_TRUE;
766 }
767
768 if (damaged)
769 _glfwInputWindowDamage(window);
770 else
771 wl_surface_commit(window->wl.surface);
772 }
773
libdecorFrameHandleClose(struct libdecor_frame * frame,void * userData)774 void libdecorFrameHandleClose(struct libdecor_frame* frame, void* userData)
775 {
776 _GLFWwindow* window = userData;
777 _glfwInputWindowCloseRequest(window);
778 }
779
libdecorFrameHandleCommit(struct libdecor_frame * frame,void * userData)780 void libdecorFrameHandleCommit(struct libdecor_frame* frame, void* userData)
781 {
782 _GLFWwindow* window = userData;
783 wl_surface_commit(window->wl.surface);
784 }
785
libdecorFrameHandleDismissPopup(struct libdecor_frame * frame,const char * seatName,void * userData)786 void libdecorFrameHandleDismissPopup(struct libdecor_frame* frame,
787 const char* seatName,
788 void* userData)
789 {
790 }
791
792 static const struct libdecor_frame_interface libdecorFrameInterface =
793 {
794 libdecorFrameHandleConfigure,
795 libdecorFrameHandleClose,
796 libdecorFrameHandleCommit,
797 libdecorFrameHandleDismissPopup
798 };
799
createLibdecorFrame(_GLFWwindow * window)800 static GLFWbool createLibdecorFrame(_GLFWwindow* window)
801 {
802 // Allow libdecor to finish initialization of itself and its plugin
803 while (!_glfw.wl.libdecor.ready)
804 _glfwWaitEventsWayland();
805
806 window->wl.libdecor.frame = libdecor_decorate(_glfw.wl.libdecor.context,
807 window->wl.surface,
808 &libdecorFrameInterface,
809 window);
810 if (!window->wl.libdecor.frame)
811 {
812 _glfwInputError(GLFW_PLATFORM_ERROR,
813 "Wayland: Failed to create libdecor frame");
814 return GLFW_FALSE;
815 }
816
817 struct libdecor_state* frameState =
818 libdecor_state_new(window->wl.width, window->wl.height);
819 libdecor_frame_commit(window->wl.libdecor.frame, frameState, NULL);
820 libdecor_state_free(frameState);
821
822 if (strlen(window->wl.appId))
823 libdecor_frame_set_app_id(window->wl.libdecor.frame, window->wl.appId);
824
825 libdecor_frame_set_title(window->wl.libdecor.frame, window->title);
826
827 if (window->minwidth != GLFW_DONT_CARE &&
828 window->minheight != GLFW_DONT_CARE)
829 {
830 libdecor_frame_set_min_content_size(window->wl.libdecor.frame,
831 window->minwidth,
832 window->minheight);
833 }
834
835 if (window->maxwidth != GLFW_DONT_CARE &&
836 window->maxheight != GLFW_DONT_CARE)
837 {
838 libdecor_frame_set_max_content_size(window->wl.libdecor.frame,
839 window->maxwidth,
840 window->maxheight);
841 }
842
843 if (!window->resizable)
844 {
845 libdecor_frame_unset_capabilities(window->wl.libdecor.frame,
846 LIBDECOR_ACTION_RESIZE);
847 }
848
849 if (window->monitor)
850 {
851 libdecor_frame_set_fullscreen(window->wl.libdecor.frame,
852 window->monitor->wl.output);
853 setIdleInhibitor(window, GLFW_TRUE);
854 }
855 else
856 {
857 if (window->wl.maximized)
858 libdecor_frame_set_maximized(window->wl.libdecor.frame);
859
860 if (!window->decorated)
861 libdecor_frame_set_visibility(window->wl.libdecor.frame, false);
862
863 setIdleInhibitor(window, GLFW_FALSE);
864 }
865
866 libdecor_frame_map(window->wl.libdecor.frame);
867 wl_display_roundtrip(_glfw.wl.display);
868 return GLFW_TRUE;
869 }
870
updateXdgSizeLimits(_GLFWwindow * window)871 static void updateXdgSizeLimits(_GLFWwindow* window)
872 {
873 int minwidth, minheight, maxwidth, maxheight;
874
875 if (window->resizable)
876 {
877 if (window->minwidth == GLFW_DONT_CARE || window->minheight == GLFW_DONT_CARE)
878 minwidth = minheight = 0;
879 else
880 {
881 minwidth = window->minwidth;
882 minheight = window->minheight;
883
884 if (window->wl.fallback.decorations)
885 {
886 minwidth += GLFW_BORDER_SIZE * 2;
887 minheight += GLFW_CAPTION_HEIGHT + GLFW_BORDER_SIZE;
888 }
889 }
890
891 if (window->maxwidth == GLFW_DONT_CARE || window->maxheight == GLFW_DONT_CARE)
892 maxwidth = maxheight = 0;
893 else
894 {
895 maxwidth = window->maxwidth;
896 maxheight = window->maxheight;
897
898 if (window->wl.fallback.decorations)
899 {
900 maxwidth += GLFW_BORDER_SIZE * 2;
901 maxheight += GLFW_CAPTION_HEIGHT + GLFW_BORDER_SIZE;
902 }
903 }
904 }
905 else
906 {
907 minwidth = maxwidth = window->wl.width;
908 minheight = maxheight = window->wl.height;
909 }
910
911 xdg_toplevel_set_min_size(window->wl.xdg.toplevel, minwidth, minheight);
912 xdg_toplevel_set_max_size(window->wl.xdg.toplevel, maxwidth, maxheight);
913 }
914
createXdgShellObjects(_GLFWwindow * window)915 static GLFWbool createXdgShellObjects(_GLFWwindow* window)
916 {
917 window->wl.xdg.surface = xdg_wm_base_get_xdg_surface(_glfw.wl.wmBase,
918 window->wl.surface);
919 if (!window->wl.xdg.surface)
920 {
921 _glfwInputError(GLFW_PLATFORM_ERROR,
922 "Wayland: Failed to create xdg-surface for window");
923 return GLFW_FALSE;
924 }
925
926 xdg_surface_add_listener(window->wl.xdg.surface, &xdgSurfaceListener, window);
927
928 window->wl.xdg.toplevel = xdg_surface_get_toplevel(window->wl.xdg.surface);
929 if (!window->wl.xdg.toplevel)
930 {
931 _glfwInputError(GLFW_PLATFORM_ERROR,
932 "Wayland: Failed to create xdg-toplevel for window");
933 return GLFW_FALSE;
934 }
935
936 xdg_toplevel_add_listener(window->wl.xdg.toplevel, &xdgToplevelListener, window);
937
938 if (window->wl.appId)
939 xdg_toplevel_set_app_id(window->wl.xdg.toplevel, window->wl.appId);
940
941 xdg_toplevel_set_title(window->wl.xdg.toplevel, window->title);
942
943 if (window->monitor)
944 {
945 xdg_toplevel_set_fullscreen(window->wl.xdg.toplevel, window->monitor->wl.output);
946 setIdleInhibitor(window, GLFW_TRUE);
947 }
948 else
949 {
950 if (window->wl.maximized)
951 xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
952
953 setIdleInhibitor(window, GLFW_FALSE);
954 }
955
956 if (_glfw.wl.decorationManager)
957 {
958 window->wl.xdg.decoration =
959 zxdg_decoration_manager_v1_get_toplevel_decoration(
960 _glfw.wl.decorationManager, window->wl.xdg.toplevel);
961 zxdg_toplevel_decoration_v1_add_listener(window->wl.xdg.decoration,
962 &xdgDecorationListener,
963 window);
964
965 uint32_t mode;
966
967 if (window->decorated)
968 mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
969 else
970 mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
971
972 zxdg_toplevel_decoration_v1_set_mode(window->wl.xdg.decoration, mode);
973 }
974 else
975 {
976 if (window->decorated && !window->monitor)
977 createFallbackDecorations(window);
978 }
979
980 updateXdgSizeLimits(window);
981
982 wl_surface_commit(window->wl.surface);
983 wl_display_roundtrip(_glfw.wl.display);
984 return GLFW_TRUE;
985 }
986
createShellObjects(_GLFWwindow * window)987 static GLFWbool createShellObjects(_GLFWwindow* window)
988 {
989 if (_glfw.wl.libdecor.context)
990 {
991 if (createLibdecorFrame(window))
992 return GLFW_TRUE;
993 }
994
995 return createXdgShellObjects(window);
996 }
997
destroyShellObjects(_GLFWwindow * window)998 static void destroyShellObjects(_GLFWwindow* window)
999 {
1000 destroyFallbackDecorations(window);
1001
1002 if (window->wl.libdecor.frame)
1003 libdecor_frame_unref(window->wl.libdecor.frame);
1004
1005 if (window->wl.xdg.decoration)
1006 zxdg_toplevel_decoration_v1_destroy(window->wl.xdg.decoration);
1007
1008 if (window->wl.xdg.toplevel)
1009 xdg_toplevel_destroy(window->wl.xdg.toplevel);
1010
1011 if (window->wl.xdg.surface)
1012 xdg_surface_destroy(window->wl.xdg.surface);
1013
1014 window->wl.libdecor.frame = NULL;
1015 window->wl.xdg.decoration = NULL;
1016 window->wl.xdg.decorationMode = 0;
1017 window->wl.xdg.toplevel = NULL;
1018 window->wl.xdg.surface = NULL;
1019 }
1020
createNativeSurface(_GLFWwindow * window,const _GLFWwndconfig * wndconfig,const _GLFWfbconfig * fbconfig)1021 static GLFWbool createNativeSurface(_GLFWwindow* window,
1022 const _GLFWwndconfig* wndconfig,
1023 const _GLFWfbconfig* fbconfig)
1024 {
1025 window->wl.surface = wl_compositor_create_surface(_glfw.wl.compositor);
1026 if (!window->wl.surface)
1027 {
1028 _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to create window surface");
1029 return GLFW_FALSE;
1030 }
1031
1032 wl_proxy_set_tag((struct wl_proxy*) window->wl.surface, &_glfw.wl.tag);
1033 wl_surface_add_listener(window->wl.surface,
1034 &surfaceListener,
1035 window);
1036
1037 window->wl.width = wndconfig->width;
1038 window->wl.height = wndconfig->height;
1039 window->wl.fbWidth = wndconfig->width;
1040 window->wl.fbHeight = wndconfig->height;
1041 window->wl.appId = _glfw_strdup(wndconfig->wl.appId);
1042
1043 window->wl.bufferScale = 1;
1044 window->wl.scalingNumerator = 120;
1045 window->wl.scaleFramebuffer = wndconfig->scaleFramebuffer;
1046
1047 window->wl.maximized = wndconfig->maximized;
1048
1049 window->wl.transparent = fbconfig->transparent;
1050 if (!window->wl.transparent)
1051 setContentAreaOpaque(window);
1052
1053 if (_glfw.wl.fractionalScaleManager)
1054 {
1055 if (window->wl.scaleFramebuffer)
1056 {
1057 window->wl.scalingViewport =
1058 wp_viewporter_get_viewport(_glfw.wl.viewporter, window->wl.surface);
1059
1060 wp_viewport_set_destination(window->wl.scalingViewport,
1061 window->wl.width,
1062 window->wl.height);
1063
1064 window->wl.fractionalScale =
1065 wp_fractional_scale_manager_v1_get_fractional_scale(
1066 _glfw.wl.fractionalScaleManager,
1067 window->wl.surface);
1068
1069 wp_fractional_scale_v1_add_listener(window->wl.fractionalScale,
1070 &fractionalScaleListener,
1071 window);
1072 }
1073 }
1074
1075 return GLFW_TRUE;
1076 }
1077
setCursorImage(_GLFWwindow * window,_GLFWcursorWayland * cursorWayland)1078 static void setCursorImage(_GLFWwindow* window,
1079 _GLFWcursorWayland* cursorWayland)
1080 {
1081 struct itimerspec timer = {0};
1082 struct wl_cursor* wlCursor = cursorWayland->cursor;
1083 struct wl_cursor_image* image;
1084 struct wl_buffer* buffer;
1085 struct wl_surface* surface = _glfw.wl.cursorSurface;
1086 int scale = 1;
1087
1088 if (!wlCursor)
1089 buffer = cursorWayland->buffer;
1090 else
1091 {
1092 if (window->wl.bufferScale > 1 && cursorWayland->cursorHiDPI)
1093 {
1094 wlCursor = cursorWayland->cursorHiDPI;
1095 scale = 2;
1096 }
1097
1098 image = wlCursor->images[cursorWayland->currentImage];
1099 buffer = wl_cursor_image_get_buffer(image);
1100 if (!buffer)
1101 return;
1102
1103 timer.it_value.tv_sec = image->delay / 1000;
1104 timer.it_value.tv_nsec = (image->delay % 1000) * 1000000;
1105 timerfd_settime(_glfw.wl.cursorTimerfd, 0, &timer, NULL);
1106
1107 cursorWayland->width = image->width;
1108 cursorWayland->height = image->height;
1109 cursorWayland->xhot = image->hotspot_x;
1110 cursorWayland->yhot = image->hotspot_y;
1111 }
1112
1113 wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial,
1114 surface,
1115 cursorWayland->xhot / scale,
1116 cursorWayland->yhot / scale);
1117 wl_surface_set_buffer_scale(surface, scale);
1118 wl_surface_attach(surface, buffer, 0, 0);
1119 wl_surface_damage(surface, 0, 0,
1120 cursorWayland->width, cursorWayland->height);
1121 wl_surface_commit(surface);
1122 }
1123
incrementCursorImage(_GLFWwindow * window)1124 static void incrementCursorImage(_GLFWwindow* window)
1125 {
1126 _GLFWcursor* cursor;
1127
1128 if (!window || !window->wl.hovered)
1129 return;
1130
1131 cursor = window->wl.currentCursor;
1132 if (cursor && cursor->wl.cursor)
1133 {
1134 cursor->wl.currentImage += 1;
1135 cursor->wl.currentImage %= cursor->wl.cursor->image_count;
1136 setCursorImage(window, &cursor->wl);
1137 }
1138 }
1139
flushDisplay(void)1140 static GLFWbool flushDisplay(void)
1141 {
1142 while (wl_display_flush(_glfw.wl.display) == -1)
1143 {
1144 if (errno != EAGAIN)
1145 return GLFW_FALSE;
1146
1147 struct pollfd fd = { wl_display_get_fd(_glfw.wl.display), POLLOUT };
1148
1149 while (poll(&fd, 1, -1) == -1)
1150 {
1151 if (errno != EINTR && errno != EAGAIN)
1152 return GLFW_FALSE;
1153 }
1154 }
1155
1156 return GLFW_TRUE;
1157 }
1158
translateKey(uint32_t scancode)1159 static int translateKey(uint32_t scancode)
1160 {
1161 if (scancode < sizeof(_glfw.wl.keycodes) / sizeof(_glfw.wl.keycodes[0]))
1162 return _glfw.wl.keycodes[scancode];
1163
1164 return GLFW_KEY_UNKNOWN;
1165 }
1166
composeSymbol(xkb_keysym_t sym)1167 static xkb_keysym_t composeSymbol(xkb_keysym_t sym)
1168 {
1169 if (sym == XKB_KEY_NoSymbol || !_glfw.wl.xkb.composeState)
1170 return sym;
1171 if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym)
1172 != XKB_COMPOSE_FEED_ACCEPTED)
1173 return sym;
1174 switch (xkb_compose_state_get_status(_glfw.wl.xkb.composeState))
1175 {
1176 case XKB_COMPOSE_COMPOSED:
1177 return xkb_compose_state_get_one_sym(_glfw.wl.xkb.composeState);
1178 case XKB_COMPOSE_COMPOSING:
1179 case XKB_COMPOSE_CANCELLED:
1180 return XKB_KEY_NoSymbol;
1181 case XKB_COMPOSE_NOTHING:
1182 default:
1183 return sym;
1184 }
1185 }
1186
inputText(_GLFWwindow * window,uint32_t scancode)1187 static void inputText(_GLFWwindow* window, uint32_t scancode)
1188 {
1189 const xkb_keysym_t* keysyms;
1190 const xkb_keycode_t keycode = scancode + 8;
1191
1192 if (xkb_state_key_get_syms(_glfw.wl.xkb.state, keycode, &keysyms) == 1)
1193 {
1194 const xkb_keysym_t keysym = composeSymbol(keysyms[0]);
1195 const uint32_t codepoint = _glfwKeySym2Unicode(keysym);
1196 if (codepoint != GLFW_INVALID_CODEPOINT)
1197 {
1198 const int mods = _glfw.wl.xkb.modifiers;
1199 const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
1200 _glfwInputChar(window, codepoint, mods, plain);
1201 }
1202 }
1203 }
1204
handleEvents(double * timeout)1205 static void handleEvents(double* timeout)
1206 {
1207 #if defined(GLFW_BUILD_LINUX_JOYSTICK)
1208 if (_glfw.joysticksInitialized)
1209 _glfwDetectJoystickConnectionLinux();
1210 #endif
1211
1212 GLFWbool event = GLFW_FALSE;
1213 enum { DISPLAY_FD, KEYREPEAT_FD, CURSOR_FD, LIBDECOR_FD };
1214 struct pollfd fds[] =
1215 {
1216 [DISPLAY_FD] = { wl_display_get_fd(_glfw.wl.display), POLLIN },
1217 [KEYREPEAT_FD] = { _glfw.wl.keyRepeatTimerfd, POLLIN },
1218 [CURSOR_FD] = { _glfw.wl.cursorTimerfd, POLLIN },
1219 [LIBDECOR_FD] = { -1, POLLIN }
1220 };
1221
1222 if (_glfw.wl.libdecor.context)
1223 fds[LIBDECOR_FD].fd = libdecor_get_fd(_glfw.wl.libdecor.context);
1224
1225 while (!event)
1226 {
1227 while (wl_display_prepare_read(_glfw.wl.display) != 0)
1228 {
1229 if (wl_display_dispatch_pending(_glfw.wl.display) > 0)
1230 return;
1231 }
1232
1233 // If an error other than EAGAIN happens, we have likely been disconnected
1234 // from the Wayland session; try to handle that the best we can.
1235 if (!flushDisplay())
1236 {
1237 wl_display_cancel_read(_glfw.wl.display);
1238
1239 _GLFWwindow* window = _glfw.windowListHead;
1240 while (window)
1241 {
1242 _glfwInputWindowCloseRequest(window);
1243 window = window->next;
1244 }
1245
1246 return;
1247 }
1248
1249 if (!_glfwPollPOSIX(fds, sizeof(fds) / sizeof(fds[0]), timeout))
1250 {
1251 wl_display_cancel_read(_glfw.wl.display);
1252 return;
1253 }
1254
1255 if (fds[DISPLAY_FD].revents & POLLIN)
1256 {
1257 wl_display_read_events(_glfw.wl.display);
1258 if (wl_display_dispatch_pending(_glfw.wl.display) > 0)
1259 event = GLFW_TRUE;
1260 }
1261 else
1262 wl_display_cancel_read(_glfw.wl.display);
1263
1264 if (fds[KEYREPEAT_FD].revents & POLLIN)
1265 {
1266 uint64_t repeats;
1267
1268 if (read(_glfw.wl.keyRepeatTimerfd, &repeats, sizeof(repeats)) == 8)
1269 {
1270 for (uint64_t i = 0; i < repeats; i++)
1271 {
1272 _glfwInputKey(_glfw.wl.keyboardFocus,
1273 translateKey(_glfw.wl.keyRepeatScancode),
1274 _glfw.wl.keyRepeatScancode,
1275 GLFW_PRESS,
1276 _glfw.wl.xkb.modifiers);
1277 inputText(_glfw.wl.keyboardFocus, _glfw.wl.keyRepeatScancode);
1278 }
1279
1280 event = GLFW_TRUE;
1281 }
1282 }
1283
1284 if (fds[CURSOR_FD].revents & POLLIN)
1285 {
1286 uint64_t repeats;
1287
1288 if (read(_glfw.wl.cursorTimerfd, &repeats, sizeof(repeats)) == 8)
1289 incrementCursorImage(_glfw.wl.pointerFocus);
1290 }
1291
1292 if (fds[LIBDECOR_FD].revents & POLLIN)
1293 {
1294 if (libdecor_dispatch(_glfw.wl.libdecor.context, 0) > 0)
1295 event = GLFW_TRUE;
1296 }
1297 }
1298 }
1299
1300 // Reads the specified data offer as the specified MIME type
1301 //
readDataOfferAsString(struct wl_data_offer * offer,const char * mimeType)1302 static char* readDataOfferAsString(struct wl_data_offer* offer, const char* mimeType)
1303 {
1304 int fds[2];
1305
1306 if (pipe2(fds, O_CLOEXEC) == -1)
1307 {
1308 _glfwInputError(GLFW_PLATFORM_ERROR,
1309 "Wayland: Failed to create pipe for data offer: %s",
1310 strerror(errno));
1311 return NULL;
1312 }
1313
1314 wl_data_offer_receive(offer, mimeType, fds[1]);
1315 flushDisplay();
1316 close(fds[1]);
1317
1318 char* string = NULL;
1319 size_t size = 0;
1320 size_t length = 0;
1321
1322 for (;;)
1323 {
1324 const size_t readSize = 4096;
1325 const size_t requiredSize = length + readSize + 1;
1326 if (requiredSize > size)
1327 {
1328 char* longer = _glfw_realloc(string, requiredSize);
1329 if (!longer)
1330 {
1331 _glfwInputError(GLFW_OUT_OF_MEMORY, NULL);
1332 close(fds[0]);
1333 return NULL;
1334 }
1335
1336 string = longer;
1337 size = requiredSize;
1338 }
1339
1340 const ssize_t result = read(fds[0], string + length, readSize);
1341 if (result == 0)
1342 break;
1343 else if (result == -1)
1344 {
1345 if (errno == EINTR)
1346 continue;
1347
1348 _glfwInputError(GLFW_PLATFORM_ERROR,
1349 "Wayland: Failed to read from data offer pipe: %s",
1350 strerror(errno));
1351 close(fds[0]);
1352 return NULL;
1353 }
1354
1355 length += result;
1356 }
1357
1358 close(fds[0]);
1359
1360 string[length] = '\0';
1361 return string;
1362 }
1363
pointerHandleEnter(void * userData,struct wl_pointer * pointer,uint32_t serial,struct wl_surface * surface,wl_fixed_t sx,wl_fixed_t sy)1364 static void pointerHandleEnter(void* userData,
1365 struct wl_pointer* pointer,
1366 uint32_t serial,
1367 struct wl_surface* surface,
1368 wl_fixed_t sx,
1369 wl_fixed_t sy)
1370 {
1371 // Happens in the case we just destroyed the surface.
1372 if (!surface)
1373 return;
1374
1375 if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag)
1376 return;
1377
1378 _GLFWwindow* window = wl_surface_get_user_data(surface);
1379
1380 _glfw.wl.serial = serial;
1381 _glfw.wl.pointerEnterSerial = serial;
1382 _glfw.wl.pointerFocus = window;
1383
1384 if (surface == window->wl.surface)
1385 {
1386 window->wl.hovered = GLFW_TRUE;
1387 _glfwSetCursorWayland(window, window->wl.currentCursor);
1388 _glfwInputCursorEnter(window, GLFW_TRUE);
1389 }
1390 else
1391 {
1392 if (window->wl.fallback.decorations)
1393 window->wl.fallback.focus = surface;
1394 }
1395 }
1396
pointerHandleLeave(void * userData,struct wl_pointer * pointer,uint32_t serial,struct wl_surface * surface)1397 static void pointerHandleLeave(void* userData,
1398 struct wl_pointer* pointer,
1399 uint32_t serial,
1400 struct wl_surface* surface)
1401 {
1402 if (!surface)
1403 return;
1404
1405 if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag)
1406 return;
1407
1408 _GLFWwindow* window = _glfw.wl.pointerFocus;
1409 if (!window)
1410 return;
1411
1412 _glfw.wl.serial = serial;
1413 _glfw.wl.pointerFocus = NULL;
1414 _glfw.wl.cursorPreviousName = NULL;
1415
1416 if (window->wl.hovered)
1417 {
1418 window->wl.hovered = GLFW_FALSE;
1419 _glfwInputCursorEnter(window, GLFW_FALSE);
1420 }
1421 else
1422 {
1423 if (window->wl.fallback.decorations)
1424 window->wl.fallback.focus = NULL;
1425 }
1426 }
1427
pointerHandleMotion(void * userData,struct wl_pointer * pointer,uint32_t time,wl_fixed_t sx,wl_fixed_t sy)1428 static void pointerHandleMotion(void* userData,
1429 struct wl_pointer* pointer,
1430 uint32_t time,
1431 wl_fixed_t sx,
1432 wl_fixed_t sy)
1433 {
1434 _GLFWwindow* window = _glfw.wl.pointerFocus;
1435 if (!window)
1436 return;
1437
1438 if (window->cursorMode == GLFW_CURSOR_DISABLED)
1439 return;
1440
1441 const double xpos = wl_fixed_to_double(sx);
1442 const double ypos = wl_fixed_to_double(sy);
1443 window->wl.cursorPosX = xpos;
1444 window->wl.cursorPosY = ypos;
1445
1446 if (window->wl.hovered)
1447 {
1448 _glfw.wl.cursorPreviousName = NULL;
1449 _glfwInputCursorPos(window, xpos, ypos);
1450 return;
1451 }
1452
1453 if (window->wl.fallback.decorations)
1454 {
1455 const char* cursorName = "left_ptr";
1456
1457 if (window->resizable)
1458 {
1459 if (window->wl.fallback.focus == window->wl.fallback.top.surface)
1460 {
1461 if (ypos < GLFW_BORDER_SIZE)
1462 cursorName = "n-resize";
1463 }
1464 else if (window->wl.fallback.focus == window->wl.fallback.left.surface)
1465 {
1466 if (ypos < GLFW_BORDER_SIZE)
1467 cursorName = "nw-resize";
1468 else
1469 cursorName = "w-resize";
1470 }
1471 else if (window->wl.fallback.focus == window->wl.fallback.right.surface)
1472 {
1473 if (ypos < GLFW_BORDER_SIZE)
1474 cursorName = "ne-resize";
1475 else
1476 cursorName = "e-resize";
1477 }
1478 else if (window->wl.fallback.focus == window->wl.fallback.bottom.surface)
1479 {
1480 if (xpos < GLFW_BORDER_SIZE)
1481 cursorName = "sw-resize";
1482 else if (xpos > window->wl.width + GLFW_BORDER_SIZE)
1483 cursorName = "se-resize";
1484 else
1485 cursorName = "s-resize";
1486 }
1487 }
1488
1489 if (_glfw.wl.cursorPreviousName != cursorName)
1490 {
1491 struct wl_surface* surface = _glfw.wl.cursorSurface;
1492 struct wl_cursor_theme* theme = _glfw.wl.cursorTheme;
1493 int scale = 1;
1494
1495 if (window->wl.bufferScale > 1 && _glfw.wl.cursorThemeHiDPI)
1496 {
1497 // We only support up to scale=2 for now, since libwayland-cursor
1498 // requires us to load a different theme for each size.
1499 scale = 2;
1500 theme = _glfw.wl.cursorThemeHiDPI;
1501 }
1502
1503 struct wl_cursor* cursor = wl_cursor_theme_get_cursor(theme, cursorName);
1504 if (!cursor)
1505 return;
1506
1507 // TODO: handle animated cursors too.
1508 struct wl_cursor_image* image = cursor->images[0];
1509 if (!image)
1510 return;
1511
1512 struct wl_buffer* buffer = wl_cursor_image_get_buffer(image);
1513 if (!buffer)
1514 return;
1515
1516 wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial,
1517 surface,
1518 image->hotspot_x / scale,
1519 image->hotspot_y / scale);
1520 wl_surface_set_buffer_scale(surface, scale);
1521 wl_surface_attach(surface, buffer, 0, 0);
1522 wl_surface_damage(surface, 0, 0, image->width, image->height);
1523 wl_surface_commit(surface);
1524
1525 _glfw.wl.cursorPreviousName = cursorName;
1526 }
1527 }
1528 }
1529
pointerHandleButton(void * userData,struct wl_pointer * pointer,uint32_t serial,uint32_t time,uint32_t button,uint32_t state)1530 static void pointerHandleButton(void* userData,
1531 struct wl_pointer* pointer,
1532 uint32_t serial,
1533 uint32_t time,
1534 uint32_t button,
1535 uint32_t state)
1536 {
1537 _GLFWwindow* window = _glfw.wl.pointerFocus;
1538 if (!window)
1539 return;
1540
1541 if (window->wl.hovered)
1542 {
1543 _glfw.wl.serial = serial;
1544
1545 _glfwInputMouseClick(window,
1546 button - BTN_LEFT,
1547 state == WL_POINTER_BUTTON_STATE_PRESSED,
1548 _glfw.wl.xkb.modifiers);
1549 return;
1550 }
1551
1552 if (window->wl.fallback.decorations)
1553 {
1554 if (button == BTN_LEFT)
1555 {
1556 uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE;
1557
1558 if (window->wl.fallback.focus == window->wl.fallback.top.surface)
1559 {
1560 if (window->wl.cursorPosY < GLFW_BORDER_SIZE)
1561 edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP;
1562 else
1563 xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial);
1564 }
1565 else if (window->wl.fallback.focus == window->wl.fallback.left.surface)
1566 {
1567 if (window->wl.cursorPosY < GLFW_BORDER_SIZE)
1568 edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
1569 else
1570 edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
1571 }
1572 else if (window->wl.fallback.focus == window->wl.fallback.right.surface)
1573 {
1574 if (window->wl.cursorPosY < GLFW_BORDER_SIZE)
1575 edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
1576 else
1577 edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
1578 }
1579 else if (window->wl.fallback.focus == window->wl.fallback.bottom.surface)
1580 {
1581 if (window->wl.cursorPosX < GLFW_BORDER_SIZE)
1582 edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
1583 else if (window->wl.cursorPosX > window->wl.width + GLFW_BORDER_SIZE)
1584 edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
1585 else
1586 edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
1587 }
1588
1589 if (edges != XDG_TOPLEVEL_RESIZE_EDGE_NONE)
1590 {
1591 xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat,
1592 serial, edges);
1593 }
1594 }
1595 else if (button == BTN_RIGHT)
1596 {
1597 if (window->wl.xdg.toplevel)
1598 {
1599 xdg_toplevel_show_window_menu(window->wl.xdg.toplevel,
1600 _glfw.wl.seat, serial,
1601 window->wl.cursorPosX,
1602 window->wl.cursorPosY);
1603 }
1604 }
1605 }
1606 }
1607
pointerHandleAxis(void * userData,struct wl_pointer * pointer,uint32_t time,uint32_t axis,wl_fixed_t value)1608 static void pointerHandleAxis(void* userData,
1609 struct wl_pointer* pointer,
1610 uint32_t time,
1611 uint32_t axis,
1612 wl_fixed_t value)
1613 {
1614 _GLFWwindow* window = _glfw.wl.pointerFocus;
1615 if (!window)
1616 return;
1617
1618 // NOTE: 10 units of motion per mouse wheel step seems to be a common ratio
1619 if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
1620 _glfwInputScroll(window, -wl_fixed_to_double(value) / 10.0, 0.0);
1621 else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
1622 _glfwInputScroll(window, 0.0, -wl_fixed_to_double(value) / 10.0);
1623 }
1624
1625 static const struct wl_pointer_listener pointerListener =
1626 {
1627 pointerHandleEnter,
1628 pointerHandleLeave,
1629 pointerHandleMotion,
1630 pointerHandleButton,
1631 pointerHandleAxis,
1632 };
1633
keyboardHandleKeymap(void * userData,struct wl_keyboard * keyboard,uint32_t format,int fd,uint32_t size)1634 static void keyboardHandleKeymap(void* userData,
1635 struct wl_keyboard* keyboard,
1636 uint32_t format,
1637 int fd,
1638 uint32_t size)
1639 {
1640 struct xkb_keymap* keymap;
1641 struct xkb_state* state;
1642 struct xkb_compose_table* composeTable;
1643 struct xkb_compose_state* composeState;
1644
1645 char* mapStr;
1646 const char* locale;
1647
1648 if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
1649 {
1650 close(fd);
1651 return;
1652 }
1653
1654 mapStr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
1655 if (mapStr == MAP_FAILED) {
1656 close(fd);
1657 return;
1658 }
1659
1660 keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context,
1661 mapStr,
1662 XKB_KEYMAP_FORMAT_TEXT_V1,
1663 0);
1664 munmap(mapStr, size);
1665 close(fd);
1666
1667 if (!keymap)
1668 {
1669 _glfwInputError(GLFW_PLATFORM_ERROR,
1670 "Wayland: Failed to compile keymap");
1671 return;
1672 }
1673
1674 state = xkb_state_new(keymap);
1675 if (!state)
1676 {
1677 _glfwInputError(GLFW_PLATFORM_ERROR,
1678 "Wayland: Failed to create XKB state");
1679 xkb_keymap_unref(keymap);
1680 return;
1681 }
1682
1683 // Look up the preferred locale, falling back to "C" as default.
1684 locale = getenv("LC_ALL");
1685 if (!locale)
1686 locale = getenv("LC_CTYPE");
1687 if (!locale)
1688 locale = getenv("LANG");
1689 if (!locale)
1690 locale = "C";
1691
1692 composeTable =
1693 xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale,
1694 XKB_COMPOSE_COMPILE_NO_FLAGS);
1695 if (composeTable)
1696 {
1697 composeState =
1698 xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS);
1699 xkb_compose_table_unref(composeTable);
1700 if (composeState)
1701 _glfw.wl.xkb.composeState = composeState;
1702 else
1703 _glfwInputError(GLFW_PLATFORM_ERROR,
1704 "Wayland: Failed to create XKB compose state");
1705 }
1706 else
1707 {
1708 _glfwInputError(GLFW_PLATFORM_ERROR,
1709 "Wayland: Failed to create XKB compose table");
1710 }
1711
1712 xkb_keymap_unref(_glfw.wl.xkb.keymap);
1713 xkb_state_unref(_glfw.wl.xkb.state);
1714 _glfw.wl.xkb.keymap = keymap;
1715 _glfw.wl.xkb.state = state;
1716
1717 _glfw.wl.xkb.controlIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control");
1718 _glfw.wl.xkb.altIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1");
1719 _glfw.wl.xkb.shiftIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift");
1720 _glfw.wl.xkb.superIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4");
1721 _glfw.wl.xkb.capsLockIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Lock");
1722 _glfw.wl.xkb.numLockIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2");
1723 }
1724
keyboardHandleEnter(void * userData,struct wl_keyboard * keyboard,uint32_t serial,struct wl_surface * surface,struct wl_array * keys)1725 static void keyboardHandleEnter(void* userData,
1726 struct wl_keyboard* keyboard,
1727 uint32_t serial,
1728 struct wl_surface* surface,
1729 struct wl_array* keys)
1730 {
1731 // Happens in the case we just destroyed the surface.
1732 if (!surface)
1733 return;
1734
1735 if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag)
1736 return;
1737
1738 _GLFWwindow* window = wl_surface_get_user_data(surface);
1739 if (surface != window->wl.surface)
1740 return;
1741
1742 _glfw.wl.serial = serial;
1743 _glfw.wl.keyboardFocus = window;
1744 _glfwInputWindowFocus(window, GLFW_TRUE);
1745 }
1746
keyboardHandleLeave(void * userData,struct wl_keyboard * keyboard,uint32_t serial,struct wl_surface * surface)1747 static void keyboardHandleLeave(void* userData,
1748 struct wl_keyboard* keyboard,
1749 uint32_t serial,
1750 struct wl_surface* surface)
1751 {
1752 _GLFWwindow* window = _glfw.wl.keyboardFocus;
1753
1754 if (!window)
1755 return;
1756
1757 struct itimerspec timer = {0};
1758 timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL);
1759
1760 _glfw.wl.serial = serial;
1761 _glfw.wl.keyboardFocus = NULL;
1762 _glfwInputWindowFocus(window, GLFW_FALSE);
1763 }
1764
keyboardHandleKey(void * userData,struct wl_keyboard * keyboard,uint32_t serial,uint32_t time,uint32_t scancode,uint32_t state)1765 static void keyboardHandleKey(void* userData,
1766 struct wl_keyboard* keyboard,
1767 uint32_t serial,
1768 uint32_t time,
1769 uint32_t scancode,
1770 uint32_t state)
1771 {
1772 _GLFWwindow* window = _glfw.wl.keyboardFocus;
1773 if (!window)
1774 return;
1775
1776 const int key = translateKey(scancode);
1777 const int action =
1778 state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE;
1779
1780 _glfw.wl.serial = serial;
1781
1782 struct itimerspec timer = {0};
1783
1784 if (action == GLFW_PRESS)
1785 {
1786 const xkb_keycode_t keycode = scancode + 8;
1787
1788 if (xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, keycode) &&
1789 _glfw.wl.keyRepeatRate > 0)
1790 {
1791 _glfw.wl.keyRepeatScancode = scancode;
1792 if (_glfw.wl.keyRepeatRate > 1)
1793 timer.it_interval.tv_nsec = 1000000000 / _glfw.wl.keyRepeatRate;
1794 else
1795 timer.it_interval.tv_sec = 1;
1796
1797 timer.it_value.tv_sec = _glfw.wl.keyRepeatDelay / 1000;
1798 timer.it_value.tv_nsec = (_glfw.wl.keyRepeatDelay % 1000) * 1000000;
1799 }
1800 }
1801
1802 timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL);
1803
1804 _glfwInputKey(window, key, scancode, action, _glfw.wl.xkb.modifiers);
1805
1806 if (action == GLFW_PRESS)
1807 inputText(window, scancode);
1808 }
1809
keyboardHandleModifiers(void * userData,struct wl_keyboard * keyboard,uint32_t serial,uint32_t modsDepressed,uint32_t modsLatched,uint32_t modsLocked,uint32_t group)1810 static void keyboardHandleModifiers(void* userData,
1811 struct wl_keyboard* keyboard,
1812 uint32_t serial,
1813 uint32_t modsDepressed,
1814 uint32_t modsLatched,
1815 uint32_t modsLocked,
1816 uint32_t group)
1817 {
1818 _glfw.wl.serial = serial;
1819
1820 if (!_glfw.wl.xkb.keymap)
1821 return;
1822
1823 xkb_state_update_mask(_glfw.wl.xkb.state,
1824 modsDepressed,
1825 modsLatched,
1826 modsLocked,
1827 0,
1828 0,
1829 group);
1830
1831 _glfw.wl.xkb.modifiers = 0;
1832
1833 struct
1834 {
1835 xkb_mod_index_t index;
1836 unsigned int bit;
1837 } modifiers[] =
1838 {
1839 { _glfw.wl.xkb.controlIndex, GLFW_MOD_CONTROL },
1840 { _glfw.wl.xkb.altIndex, GLFW_MOD_ALT },
1841 { _glfw.wl.xkb.shiftIndex, GLFW_MOD_SHIFT },
1842 { _glfw.wl.xkb.superIndex, GLFW_MOD_SUPER },
1843 { _glfw.wl.xkb.capsLockIndex, GLFW_MOD_CAPS_LOCK },
1844 { _glfw.wl.xkb.numLockIndex, GLFW_MOD_NUM_LOCK }
1845 };
1846
1847 for (size_t i = 0; i < sizeof(modifiers) / sizeof(modifiers[0]); i++)
1848 {
1849 if (xkb_state_mod_index_is_active(_glfw.wl.xkb.state,
1850 modifiers[i].index,
1851 XKB_STATE_MODS_EFFECTIVE) == 1)
1852 {
1853 _glfw.wl.xkb.modifiers |= modifiers[i].bit;
1854 }
1855 }
1856 }
1857
keyboardHandleRepeatInfo(void * userData,struct wl_keyboard * keyboard,int32_t rate,int32_t delay)1858 static void keyboardHandleRepeatInfo(void* userData,
1859 struct wl_keyboard* keyboard,
1860 int32_t rate,
1861 int32_t delay)
1862 {
1863 if (keyboard != _glfw.wl.keyboard)
1864 return;
1865
1866 _glfw.wl.keyRepeatRate = rate;
1867 _glfw.wl.keyRepeatDelay = delay;
1868 }
1869
1870 static const struct wl_keyboard_listener keyboardListener =
1871 {
1872 keyboardHandleKeymap,
1873 keyboardHandleEnter,
1874 keyboardHandleLeave,
1875 keyboardHandleKey,
1876 keyboardHandleModifiers,
1877 keyboardHandleRepeatInfo,
1878 };
1879
seatHandleCapabilities(void * userData,struct wl_seat * seat,enum wl_seat_capability caps)1880 static void seatHandleCapabilities(void* userData,
1881 struct wl_seat* seat,
1882 enum wl_seat_capability caps)
1883 {
1884 if ((caps & WL_SEAT_CAPABILITY_POINTER) && !_glfw.wl.pointer)
1885 {
1886 _glfw.wl.pointer = wl_seat_get_pointer(seat);
1887 wl_pointer_add_listener(_glfw.wl.pointer, &pointerListener, NULL);
1888 }
1889 else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer)
1890 {
1891 wl_pointer_destroy(_glfw.wl.pointer);
1892 _glfw.wl.pointer = NULL;
1893 }
1894
1895 if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard)
1896 {
1897 _glfw.wl.keyboard = wl_seat_get_keyboard(seat);
1898 wl_keyboard_add_listener(_glfw.wl.keyboard, &keyboardListener, NULL);
1899 }
1900 else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard)
1901 {
1902 wl_keyboard_destroy(_glfw.wl.keyboard);
1903 _glfw.wl.keyboard = NULL;
1904 }
1905 }
1906
seatHandleName(void * userData,struct wl_seat * seat,const char * name)1907 static void seatHandleName(void* userData,
1908 struct wl_seat* seat,
1909 const char* name)
1910 {
1911 }
1912
1913 static const struct wl_seat_listener seatListener =
1914 {
1915 seatHandleCapabilities,
1916 seatHandleName,
1917 };
1918
dataOfferHandleOffer(void * userData,struct wl_data_offer * offer,const char * mimeType)1919 static void dataOfferHandleOffer(void* userData,
1920 struct wl_data_offer* offer,
1921 const char* mimeType)
1922 {
1923 for (unsigned int i = 0; i < _glfw.wl.offerCount; i++)
1924 {
1925 if (_glfw.wl.offers[i].offer == offer)
1926 {
1927 if (strcmp(mimeType, "text/plain;charset=utf-8") == 0)
1928 _glfw.wl.offers[i].text_plain_utf8 = GLFW_TRUE;
1929 else if (strcmp(mimeType, "text/uri-list") == 0)
1930 _glfw.wl.offers[i].text_uri_list = GLFW_TRUE;
1931
1932 break;
1933 }
1934 }
1935 }
1936
1937 static const struct wl_data_offer_listener dataOfferListener =
1938 {
1939 dataOfferHandleOffer
1940 };
1941
dataDeviceHandleDataOffer(void * userData,struct wl_data_device * device,struct wl_data_offer * offer)1942 static void dataDeviceHandleDataOffer(void* userData,
1943 struct wl_data_device* device,
1944 struct wl_data_offer* offer)
1945 {
1946 _GLFWofferWayland* offers =
1947 _glfw_realloc(_glfw.wl.offers,
1948 sizeof(_GLFWofferWayland) * (_glfw.wl.offerCount + 1));
1949 if (!offers)
1950 {
1951 _glfwInputError(GLFW_OUT_OF_MEMORY, NULL);
1952 return;
1953 }
1954
1955 _glfw.wl.offers = offers;
1956 _glfw.wl.offerCount++;
1957
1958 _glfw.wl.offers[_glfw.wl.offerCount - 1] = (_GLFWofferWayland) { offer };
1959 wl_data_offer_add_listener(offer, &dataOfferListener, NULL);
1960 }
1961
dataDeviceHandleEnter(void * userData,struct wl_data_device * device,uint32_t serial,struct wl_surface * surface,wl_fixed_t x,wl_fixed_t y,struct wl_data_offer * offer)1962 static void dataDeviceHandleEnter(void* userData,
1963 struct wl_data_device* device,
1964 uint32_t serial,
1965 struct wl_surface* surface,
1966 wl_fixed_t x,
1967 wl_fixed_t y,
1968 struct wl_data_offer* offer)
1969 {
1970 if (_glfw.wl.dragOffer)
1971 {
1972 wl_data_offer_destroy(_glfw.wl.dragOffer);
1973 _glfw.wl.dragOffer = NULL;
1974 _glfw.wl.dragFocus = NULL;
1975 }
1976
1977 unsigned int i;
1978
1979 for (i = 0; i < _glfw.wl.offerCount; i++)
1980 {
1981 if (_glfw.wl.offers[i].offer == offer)
1982 break;
1983 }
1984
1985 if (i == _glfw.wl.offerCount)
1986 return;
1987
1988 if (surface && wl_proxy_get_tag((struct wl_proxy*) surface) == &_glfw.wl.tag)
1989 {
1990 _GLFWwindow* window = wl_surface_get_user_data(surface);
1991 if (window->wl.surface == surface)
1992 {
1993 if (_glfw.wl.offers[i].text_uri_list)
1994 {
1995 _glfw.wl.dragOffer = offer;
1996 _glfw.wl.dragFocus = window;
1997 _glfw.wl.dragSerial = serial;
1998
1999 wl_data_offer_accept(offer, serial, "text/uri-list");
2000 }
2001 }
2002 }
2003
2004 if (!_glfw.wl.dragOffer)
2005 {
2006 wl_data_offer_accept(offer, serial, NULL);
2007 wl_data_offer_destroy(offer);
2008 }
2009
2010 _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1];
2011 _glfw.wl.offerCount--;
2012 }
2013
dataDeviceHandleLeave(void * userData,struct wl_data_device * device)2014 static void dataDeviceHandleLeave(void* userData,
2015 struct wl_data_device* device)
2016 {
2017 if (_glfw.wl.dragOffer)
2018 {
2019 wl_data_offer_destroy(_glfw.wl.dragOffer);
2020 _glfw.wl.dragOffer = NULL;
2021 _glfw.wl.dragFocus = NULL;
2022 }
2023 }
2024
dataDeviceHandleMotion(void * userData,struct wl_data_device * device,uint32_t time,wl_fixed_t x,wl_fixed_t y)2025 static void dataDeviceHandleMotion(void* userData,
2026 struct wl_data_device* device,
2027 uint32_t time,
2028 wl_fixed_t x,
2029 wl_fixed_t y)
2030 {
2031 }
2032
dataDeviceHandleDrop(void * userData,struct wl_data_device * device)2033 static void dataDeviceHandleDrop(void* userData,
2034 struct wl_data_device* device)
2035 {
2036 if (!_glfw.wl.dragOffer)
2037 return;
2038
2039 char* string = readDataOfferAsString(_glfw.wl.dragOffer, "text/uri-list");
2040 if (string)
2041 {
2042 int count;
2043 char** paths = _glfwParseUriList(string, &count);
2044 if (paths)
2045 {
2046 _glfwInputDrop(_glfw.wl.dragFocus, count, (const char**) paths);
2047
2048 for (int i = 0; i < count; i++)
2049 _glfw_free(paths[i]);
2050
2051 _glfw_free(paths);
2052 }
2053
2054 _glfw_free(string);
2055 }
2056 }
2057
dataDeviceHandleSelection(void * userData,struct wl_data_device * device,struct wl_data_offer * offer)2058 static void dataDeviceHandleSelection(void* userData,
2059 struct wl_data_device* device,
2060 struct wl_data_offer* offer)
2061 {
2062 if (_glfw.wl.selectionOffer)
2063 {
2064 wl_data_offer_destroy(_glfw.wl.selectionOffer);
2065 _glfw.wl.selectionOffer = NULL;
2066 }
2067
2068 for (unsigned int i = 0; i < _glfw.wl.offerCount; i++)
2069 {
2070 if (_glfw.wl.offers[i].offer == offer)
2071 {
2072 if (_glfw.wl.offers[i].text_plain_utf8)
2073 _glfw.wl.selectionOffer = offer;
2074 else
2075 wl_data_offer_destroy(offer);
2076
2077 _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1];
2078 _glfw.wl.offerCount--;
2079 break;
2080 }
2081 }
2082 }
2083
2084 const struct wl_data_device_listener dataDeviceListener =
2085 {
2086 dataDeviceHandleDataOffer,
2087 dataDeviceHandleEnter,
2088 dataDeviceHandleLeave,
2089 dataDeviceHandleMotion,
2090 dataDeviceHandleDrop,
2091 dataDeviceHandleSelection,
2092 };
2093
xdgActivationHandleDone(void * userData,struct xdg_activation_token_v1 * activationToken,const char * token)2094 static void xdgActivationHandleDone(void* userData,
2095 struct xdg_activation_token_v1* activationToken,
2096 const char* token)
2097 {
2098 _GLFWwindow* window = userData;
2099
2100 if (activationToken != window->wl.activationToken)
2101 return;
2102
2103 xdg_activation_v1_activate(_glfw.wl.activationManager, token, window->wl.surface);
2104 xdg_activation_token_v1_destroy(window->wl.activationToken);
2105 window->wl.activationToken = NULL;
2106 }
2107
2108 static const struct xdg_activation_token_v1_listener xdgActivationListener =
2109 {
2110 xdgActivationHandleDone
2111 };
2112
_glfwAddSeatListenerWayland(struct wl_seat * seat)2113 void _glfwAddSeatListenerWayland(struct wl_seat* seat)
2114 {
2115 wl_seat_add_listener(seat, &seatListener, NULL);
2116 }
2117
_glfwAddDataDeviceListenerWayland(struct wl_data_device * device)2118 void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device)
2119 {
2120 wl_data_device_add_listener(device, &dataDeviceListener, NULL);
2121 }
2122
2123
2124 //////////////////////////////////////////////////////////////////////////
2125 ////// GLFW platform API //////
2126 //////////////////////////////////////////////////////////////////////////
2127
_glfwCreateWindowWayland(_GLFWwindow * window,const _GLFWwndconfig * wndconfig,const _GLFWctxconfig * ctxconfig,const _GLFWfbconfig * fbconfig)2128 GLFWbool _glfwCreateWindowWayland(_GLFWwindow* window,
2129 const _GLFWwndconfig* wndconfig,
2130 const _GLFWctxconfig* ctxconfig,
2131 const _GLFWfbconfig* fbconfig)
2132 {
2133 if (!createNativeSurface(window, wndconfig, fbconfig))
2134 return GLFW_FALSE;
2135
2136 if (ctxconfig->client != GLFW_NO_API)
2137 {
2138 if (ctxconfig->source == GLFW_EGL_CONTEXT_API ||
2139 ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
2140 {
2141 window->wl.egl.window = wl_egl_window_create(window->wl.surface,
2142 window->wl.fbWidth,
2143 window->wl.fbHeight);
2144 if (!window->wl.egl.window)
2145 {
2146 _glfwInputError(GLFW_PLATFORM_ERROR,
2147 "Wayland: Failed to create EGL window");
2148 return GLFW_FALSE;
2149 }
2150
2151 if (!_glfwInitEGL())
2152 return GLFW_FALSE;
2153 if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
2154 return GLFW_FALSE;
2155 }
2156 else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
2157 {
2158 if (!_glfwInitOSMesa())
2159 return GLFW_FALSE;
2160 if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
2161 return GLFW_FALSE;
2162 }
2163
2164 if (!_glfwRefreshContextAttribs(window, ctxconfig))
2165 return GLFW_FALSE;
2166 }
2167
2168 if (wndconfig->mousePassthrough)
2169 _glfwSetWindowMousePassthroughWayland(window, GLFW_TRUE);
2170
2171 if (window->monitor || wndconfig->visible)
2172 {
2173 if (!createShellObjects(window))
2174 return GLFW_FALSE;
2175 }
2176
2177 return GLFW_TRUE;
2178 }
2179
_glfwDestroyWindowWayland(_GLFWwindow * window)2180 void _glfwDestroyWindowWayland(_GLFWwindow* window)
2181 {
2182 if (window == _glfw.wl.pointerFocus)
2183 _glfw.wl.pointerFocus = NULL;
2184
2185 if (window == _glfw.wl.keyboardFocus)
2186 _glfw.wl.keyboardFocus = NULL;
2187
2188 if (window->wl.fractionalScale)
2189 wp_fractional_scale_v1_destroy(window->wl.fractionalScale);
2190
2191 if (window->wl.scalingViewport)
2192 wp_viewport_destroy(window->wl.scalingViewport);
2193
2194 if (window->wl.activationToken)
2195 xdg_activation_token_v1_destroy(window->wl.activationToken);
2196
2197 if (window->wl.idleInhibitor)
2198 zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor);
2199
2200 if (window->wl.relativePointer)
2201 zwp_relative_pointer_v1_destroy(window->wl.relativePointer);
2202
2203 if (window->wl.lockedPointer)
2204 zwp_locked_pointer_v1_destroy(window->wl.lockedPointer);
2205
2206 if (window->wl.confinedPointer)
2207 zwp_confined_pointer_v1_destroy(window->wl.confinedPointer);
2208
2209 if (window->context.destroy)
2210 window->context.destroy(window);
2211
2212 destroyShellObjects(window);
2213
2214 if (window->wl.fallback.buffer)
2215 wl_buffer_destroy(window->wl.fallback.buffer);
2216
2217 if (window->wl.egl.window)
2218 wl_egl_window_destroy(window->wl.egl.window);
2219
2220 if (window->wl.surface)
2221 wl_surface_destroy(window->wl.surface);
2222
2223 _glfw_free(window->wl.appId);
2224 _glfw_free(window->wl.outputScales);
2225 }
2226
_glfwSetWindowTitleWayland(_GLFWwindow * window,const char * title)2227 void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title)
2228 {
2229 if (window->wl.libdecor.frame)
2230 libdecor_frame_set_title(window->wl.libdecor.frame, title);
2231 else if (window->wl.xdg.toplevel)
2232 xdg_toplevel_set_title(window->wl.xdg.toplevel, title);
2233 }
2234
_glfwSetWindowIconWayland(_GLFWwindow * window,int count,const GLFWimage * images)2235 void _glfwSetWindowIconWayland(_GLFWwindow* window,
2236 int count, const GLFWimage* images)
2237 {
2238 _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2239 "Wayland: The platform does not support setting the window icon");
2240 }
2241
_glfwGetWindowPosWayland(_GLFWwindow * window,int * xpos,int * ypos)2242 void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos)
2243 {
2244 // A Wayland client is not aware of its position, so just warn and leave it
2245 // as (0, 0)
2246
2247 _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2248 "Wayland: The platform does not provide the window position");
2249 }
2250
_glfwSetWindowPosWayland(_GLFWwindow * window,int xpos,int ypos)2251 void _glfwSetWindowPosWayland(_GLFWwindow* window, int xpos, int ypos)
2252 {
2253 // A Wayland client can not set its position, so just warn
2254
2255 _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2256 "Wayland: The platform does not support setting the window position");
2257 }
2258
_glfwGetWindowSizeWayland(_GLFWwindow * window,int * width,int * height)2259 void _glfwGetWindowSizeWayland(_GLFWwindow* window, int* width, int* height)
2260 {
2261 if (width)
2262 *width = window->wl.width;
2263 if (height)
2264 *height = window->wl.height;
2265 }
2266
_glfwSetWindowSizeWayland(_GLFWwindow * window,int width,int height)2267 void _glfwSetWindowSizeWayland(_GLFWwindow* window, int width, int height)
2268 {
2269 if (window->monitor)
2270 {
2271 // Video mode setting is not available on Wayland
2272 }
2273 else
2274 {
2275 if (!resizeWindow(window, width, height))
2276 return;
2277
2278 if (window->wl.libdecor.frame)
2279 {
2280 struct libdecor_state* frameState =
2281 libdecor_state_new(window->wl.width, window->wl.height);
2282 libdecor_frame_commit(window->wl.libdecor.frame, frameState, NULL);
2283 libdecor_state_free(frameState);
2284 }
2285
2286 if (window->wl.visible)
2287 _glfwInputWindowDamage(window);
2288 }
2289 }
2290
_glfwSetWindowSizeLimitsWayland(_GLFWwindow * window,int minwidth,int minheight,int maxwidth,int maxheight)2291 void _glfwSetWindowSizeLimitsWayland(_GLFWwindow* window,
2292 int minwidth, int minheight,
2293 int maxwidth, int maxheight)
2294 {
2295 if (window->wl.libdecor.frame)
2296 {
2297 if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE)
2298 minwidth = minheight = 0;
2299
2300 if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)
2301 maxwidth = maxheight = 0;
2302
2303 libdecor_frame_set_min_content_size(window->wl.libdecor.frame,
2304 minwidth, minheight);
2305 libdecor_frame_set_max_content_size(window->wl.libdecor.frame,
2306 maxwidth, maxheight);
2307 }
2308 else if (window->wl.xdg.toplevel)
2309 updateXdgSizeLimits(window);
2310 }
2311
_glfwSetWindowAspectRatioWayland(_GLFWwindow * window,int numer,int denom)2312 void _glfwSetWindowAspectRatioWayland(_GLFWwindow* window, int numer, int denom)
2313 {
2314 if (window->wl.maximized || window->wl.fullscreen)
2315 return;
2316
2317 int width = window->wl.width, height = window->wl.height;
2318
2319 if (numer != GLFW_DONT_CARE && denom != GLFW_DONT_CARE)
2320 {
2321 const float aspectRatio = (float) width / (float) height;
2322 const float targetRatio = (float) numer / (float) denom;
2323 if (aspectRatio < targetRatio)
2324 height /= targetRatio;
2325 else if (aspectRatio > targetRatio)
2326 width *= targetRatio;
2327 }
2328
2329 if (resizeWindow(window, width, height))
2330 {
2331 if (window->wl.libdecor.frame)
2332 {
2333 struct libdecor_state* frameState =
2334 libdecor_state_new(window->wl.width, window->wl.height);
2335 libdecor_frame_commit(window->wl.libdecor.frame, frameState, NULL);
2336 libdecor_state_free(frameState);
2337 }
2338
2339 _glfwInputWindowSize(window, window->wl.width, window->wl.height);
2340
2341 if (window->wl.visible)
2342 _glfwInputWindowDamage(window);
2343 }
2344 }
2345
_glfwGetFramebufferSizeWayland(_GLFWwindow * window,int * width,int * height)2346 void _glfwGetFramebufferSizeWayland(_GLFWwindow* window, int* width, int* height)
2347 {
2348 if (width)
2349 *width = window->wl.fbWidth;
2350 if (height)
2351 *height = window->wl.fbHeight;
2352 }
2353
_glfwGetWindowFrameSizeWayland(_GLFWwindow * window,int * left,int * top,int * right,int * bottom)2354 void _glfwGetWindowFrameSizeWayland(_GLFWwindow* window,
2355 int* left, int* top,
2356 int* right, int* bottom)
2357 {
2358 if (window->wl.fallback.decorations)
2359 {
2360 if (top)
2361 *top = GLFW_CAPTION_HEIGHT;
2362 if (left)
2363 *left = GLFW_BORDER_SIZE;
2364 if (right)
2365 *right = GLFW_BORDER_SIZE;
2366 if (bottom)
2367 *bottom = GLFW_BORDER_SIZE;
2368 }
2369 }
2370
_glfwGetWindowContentScaleWayland(_GLFWwindow * window,float * xscale,float * yscale)2371 void _glfwGetWindowContentScaleWayland(_GLFWwindow* window,
2372 float* xscale, float* yscale)
2373 {
2374 if (window->wl.fractionalScale)
2375 {
2376 if (xscale)
2377 *xscale = (float) window->wl.scalingNumerator / 120.f;
2378 if (yscale)
2379 *yscale = (float) window->wl.scalingNumerator / 120.f;
2380 }
2381 else
2382 {
2383 if (xscale)
2384 *xscale = (float) window->wl.bufferScale;
2385 if (yscale)
2386 *yscale = (float) window->wl.bufferScale;
2387 }
2388 }
2389
_glfwIconifyWindowWayland(_GLFWwindow * window)2390 void _glfwIconifyWindowWayland(_GLFWwindow* window)
2391 {
2392 if (window->wl.libdecor.frame)
2393 libdecor_frame_set_minimized(window->wl.libdecor.frame);
2394 else if (window->wl.xdg.toplevel)
2395 xdg_toplevel_set_minimized(window->wl.xdg.toplevel);
2396 }
2397
_glfwRestoreWindowWayland(_GLFWwindow * window)2398 void _glfwRestoreWindowWayland(_GLFWwindow* window)
2399 {
2400 if (window->monitor)
2401 {
2402 // There is no way to unset minimized, or even to know if we are
2403 // minimized, so there is nothing to do in this case.
2404 }
2405 else
2406 {
2407 // We assume we are not minimized and act only on maximization
2408
2409 if (window->wl.maximized)
2410 {
2411 if (window->wl.libdecor.frame)
2412 libdecor_frame_unset_maximized(window->wl.libdecor.frame);
2413 else if (window->wl.xdg.toplevel)
2414 xdg_toplevel_unset_maximized(window->wl.xdg.toplevel);
2415 else
2416 window->wl.maximized = GLFW_FALSE;
2417 }
2418 }
2419 }
2420
_glfwMaximizeWindowWayland(_GLFWwindow * window)2421 void _glfwMaximizeWindowWayland(_GLFWwindow* window)
2422 {
2423 if (window->wl.libdecor.frame)
2424 libdecor_frame_set_maximized(window->wl.libdecor.frame);
2425 else if (window->wl.xdg.toplevel)
2426 xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
2427 else
2428 window->wl.maximized = GLFW_TRUE;
2429 }
2430
_glfwShowWindowWayland(_GLFWwindow * window)2431 void _glfwShowWindowWayland(_GLFWwindow* window)
2432 {
2433 if (!window->wl.libdecor.frame && !window->wl.xdg.toplevel)
2434 {
2435 // NOTE: The XDG surface and role are created here so command-line applications
2436 // with off-screen windows do not appear in for example the Unity dock
2437 createShellObjects(window);
2438 }
2439 }
2440
_glfwHideWindowWayland(_GLFWwindow * window)2441 void _glfwHideWindowWayland(_GLFWwindow* window)
2442 {
2443 if (window->wl.visible)
2444 {
2445 window->wl.visible = GLFW_FALSE;
2446 destroyShellObjects(window);
2447
2448 wl_surface_attach(window->wl.surface, NULL, 0, 0);
2449 wl_surface_commit(window->wl.surface);
2450 }
2451 }
2452
_glfwRequestWindowAttentionWayland(_GLFWwindow * window)2453 void _glfwRequestWindowAttentionWayland(_GLFWwindow* window)
2454 {
2455 if (!_glfw.wl.activationManager)
2456 return;
2457
2458 // We're about to overwrite this with a new request
2459 if (window->wl.activationToken)
2460 xdg_activation_token_v1_destroy(window->wl.activationToken);
2461
2462 window->wl.activationToken =
2463 xdg_activation_v1_get_activation_token(_glfw.wl.activationManager);
2464 xdg_activation_token_v1_add_listener(window->wl.activationToken,
2465 &xdgActivationListener,
2466 window);
2467
2468 xdg_activation_token_v1_commit(window->wl.activationToken);
2469 }
2470
_glfwFocusWindowWayland(_GLFWwindow * window)2471 void _glfwFocusWindowWayland(_GLFWwindow* window)
2472 {
2473 if (!_glfw.wl.activationManager)
2474 return;
2475
2476 if (window->wl.activationToken)
2477 xdg_activation_token_v1_destroy(window->wl.activationToken);
2478
2479 window->wl.activationToken =
2480 xdg_activation_v1_get_activation_token(_glfw.wl.activationManager);
2481 xdg_activation_token_v1_add_listener(window->wl.activationToken,
2482 &xdgActivationListener,
2483 window);
2484
2485 xdg_activation_token_v1_set_serial(window->wl.activationToken,
2486 _glfw.wl.serial,
2487 _glfw.wl.seat);
2488
2489 _GLFWwindow* requester = _glfw.wl.keyboardFocus;
2490 if (requester)
2491 {
2492 xdg_activation_token_v1_set_surface(window->wl.activationToken,
2493 requester->wl.surface);
2494
2495 if (requester->wl.appId)
2496 {
2497 xdg_activation_token_v1_set_app_id(window->wl.activationToken,
2498 requester->wl.appId);
2499 }
2500 }
2501
2502 xdg_activation_token_v1_commit(window->wl.activationToken);
2503 }
2504
_glfwSetWindowMonitorWayland(_GLFWwindow * window,_GLFWmonitor * monitor,int xpos,int ypos,int width,int height,int refreshRate)2505 void _glfwSetWindowMonitorWayland(_GLFWwindow* window,
2506 _GLFWmonitor* monitor,
2507 int xpos, int ypos,
2508 int width, int height,
2509 int refreshRate)
2510 {
2511 if (window->monitor == monitor)
2512 {
2513 if (!monitor)
2514 _glfwSetWindowSizeWayland(window, width, height);
2515
2516 return;
2517 }
2518
2519 if (window->monitor)
2520 releaseMonitor(window);
2521
2522 _glfwInputWindowMonitor(window, monitor);
2523
2524 if (window->monitor)
2525 acquireMonitor(window);
2526 else
2527 _glfwSetWindowSizeWayland(window, width, height);
2528 }
2529
_glfwWindowFocusedWayland(_GLFWwindow * window)2530 GLFWbool _glfwWindowFocusedWayland(_GLFWwindow* window)
2531 {
2532 return _glfw.wl.keyboardFocus == window;
2533 }
2534
_glfwWindowIconifiedWayland(_GLFWwindow * window)2535 GLFWbool _glfwWindowIconifiedWayland(_GLFWwindow* window)
2536 {
2537 // xdg-shell doesn’t give any way to request whether a surface is
2538 // iconified.
2539 return GLFW_FALSE;
2540 }
2541
_glfwWindowVisibleWayland(_GLFWwindow * window)2542 GLFWbool _glfwWindowVisibleWayland(_GLFWwindow* window)
2543 {
2544 return window->wl.visible;
2545 }
2546
_glfwWindowMaximizedWayland(_GLFWwindow * window)2547 GLFWbool _glfwWindowMaximizedWayland(_GLFWwindow* window)
2548 {
2549 return window->wl.maximized;
2550 }
2551
_glfwWindowHoveredWayland(_GLFWwindow * window)2552 GLFWbool _glfwWindowHoveredWayland(_GLFWwindow* window)
2553 {
2554 return window->wl.hovered;
2555 }
2556
_glfwFramebufferTransparentWayland(_GLFWwindow * window)2557 GLFWbool _glfwFramebufferTransparentWayland(_GLFWwindow* window)
2558 {
2559 return window->wl.transparent;
2560 }
2561
_glfwSetWindowResizableWayland(_GLFWwindow * window,GLFWbool enabled)2562 void _glfwSetWindowResizableWayland(_GLFWwindow* window, GLFWbool enabled)
2563 {
2564 if (window->wl.libdecor.frame)
2565 {
2566 if (enabled)
2567 {
2568 libdecor_frame_set_capabilities(window->wl.libdecor.frame,
2569 LIBDECOR_ACTION_RESIZE);
2570 }
2571 else
2572 {
2573 libdecor_frame_unset_capabilities(window->wl.libdecor.frame,
2574 LIBDECOR_ACTION_RESIZE);
2575 }
2576 }
2577 else if (window->wl.xdg.toplevel)
2578 updateXdgSizeLimits(window);
2579 }
2580
_glfwSetWindowDecoratedWayland(_GLFWwindow * window,GLFWbool enabled)2581 void _glfwSetWindowDecoratedWayland(_GLFWwindow* window, GLFWbool enabled)
2582 {
2583 if (window->wl.libdecor.frame)
2584 {
2585 libdecor_frame_set_visibility(window->wl.libdecor.frame, enabled);
2586 }
2587 else if (window->wl.xdg.decoration)
2588 {
2589 uint32_t mode;
2590
2591 if (enabled)
2592 mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
2593 else
2594 mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
2595
2596 zxdg_toplevel_decoration_v1_set_mode(window->wl.xdg.decoration, mode);
2597 }
2598 else if (window->wl.xdg.toplevel)
2599 {
2600 if (enabled)
2601 createFallbackDecorations(window);
2602 else
2603 destroyFallbackDecorations(window);
2604 }
2605 }
2606
_glfwSetWindowFloatingWayland(_GLFWwindow * window,GLFWbool enabled)2607 void _glfwSetWindowFloatingWayland(_GLFWwindow* window, GLFWbool enabled)
2608 {
2609 _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2610 "Wayland: Platform does not support making a window floating");
2611 }
2612
_glfwSetWindowMousePassthroughWayland(_GLFWwindow * window,GLFWbool enabled)2613 void _glfwSetWindowMousePassthroughWayland(_GLFWwindow* window, GLFWbool enabled)
2614 {
2615 if (enabled)
2616 {
2617 struct wl_region* region = wl_compositor_create_region(_glfw.wl.compositor);
2618 wl_surface_set_input_region(window->wl.surface, region);
2619 wl_region_destroy(region);
2620 }
2621 else
2622 wl_surface_set_input_region(window->wl.surface, NULL);
2623 }
2624
_glfwGetWindowOpacityWayland(_GLFWwindow * window)2625 float _glfwGetWindowOpacityWayland(_GLFWwindow* window)
2626 {
2627 return 1.f;
2628 }
2629
_glfwSetWindowOpacityWayland(_GLFWwindow * window,float opacity)2630 void _glfwSetWindowOpacityWayland(_GLFWwindow* window, float opacity)
2631 {
2632 _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2633 "Wayland: The platform does not support setting the window opacity");
2634 }
2635
_glfwSetRawMouseMotionWayland(_GLFWwindow * window,GLFWbool enabled)2636 void _glfwSetRawMouseMotionWayland(_GLFWwindow* window, GLFWbool enabled)
2637 {
2638 // This is handled in relativePointerHandleRelativeMotion
2639 }
2640
_glfwRawMouseMotionSupportedWayland(void)2641 GLFWbool _glfwRawMouseMotionSupportedWayland(void)
2642 {
2643 return GLFW_TRUE;
2644 }
2645
_glfwPollEventsWayland(void)2646 void _glfwPollEventsWayland(void)
2647 {
2648 double timeout = 0.0;
2649 handleEvents(&timeout);
2650 }
2651
_glfwWaitEventsWayland(void)2652 void _glfwWaitEventsWayland(void)
2653 {
2654 handleEvents(NULL);
2655 }
2656
_glfwWaitEventsTimeoutWayland(double timeout)2657 void _glfwWaitEventsTimeoutWayland(double timeout)
2658 {
2659 handleEvents(&timeout);
2660 }
2661
_glfwPostEmptyEventWayland(void)2662 void _glfwPostEmptyEventWayland(void)
2663 {
2664 wl_display_sync(_glfw.wl.display);
2665 flushDisplay();
2666 }
2667
_glfwGetCursorPosWayland(_GLFWwindow * window,double * xpos,double * ypos)2668 void _glfwGetCursorPosWayland(_GLFWwindow* window, double* xpos, double* ypos)
2669 {
2670 if (xpos)
2671 *xpos = window->wl.cursorPosX;
2672 if (ypos)
2673 *ypos = window->wl.cursorPosY;
2674 }
2675
_glfwSetCursorPosWayland(_GLFWwindow * window,double x,double y)2676 void _glfwSetCursorPosWayland(_GLFWwindow* window, double x, double y)
2677 {
2678 _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2679 "Wayland: The platform does not support setting the cursor position");
2680 }
2681
_glfwSetCursorModeWayland(_GLFWwindow * window,int mode)2682 void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode)
2683 {
2684 _glfwSetCursorWayland(window, window->wl.currentCursor);
2685 }
2686
_glfwGetScancodeNameWayland(int scancode)2687 const char* _glfwGetScancodeNameWayland(int scancode)
2688 {
2689 if (scancode < 0 || scancode > 255)
2690 {
2691 _glfwInputError(GLFW_INVALID_VALUE,
2692 "Wayland: Invalid scancode %i",
2693 scancode);
2694 return NULL;
2695 }
2696
2697 const int key = _glfw.wl.keycodes[scancode];
2698 if (key == GLFW_KEY_UNKNOWN)
2699 return NULL;
2700
2701 const xkb_keycode_t keycode = scancode + 8;
2702 const xkb_layout_index_t layout =
2703 xkb_state_key_get_layout(_glfw.wl.xkb.state, keycode);
2704 if (layout == XKB_LAYOUT_INVALID)
2705 {
2706 _glfwInputError(GLFW_PLATFORM_ERROR,
2707 "Wayland: Failed to retrieve layout for key name");
2708 return NULL;
2709 }
2710
2711 const xkb_keysym_t* keysyms = NULL;
2712 xkb_keymap_key_get_syms_by_level(_glfw.wl.xkb.keymap,
2713 keycode,
2714 layout,
2715 0,
2716 &keysyms);
2717 if (keysyms == NULL)
2718 {
2719 _glfwInputError(GLFW_PLATFORM_ERROR,
2720 "Wayland: Failed to retrieve keysym for key name");
2721 return NULL;
2722 }
2723
2724 const uint32_t codepoint = _glfwKeySym2Unicode(keysyms[0]);
2725 if (codepoint == GLFW_INVALID_CODEPOINT)
2726 {
2727 _glfwInputError(GLFW_PLATFORM_ERROR,
2728 "Wayland: Failed to retrieve codepoint for key name");
2729 return NULL;
2730 }
2731
2732 const size_t count = _glfwEncodeUTF8(_glfw.wl.keynames[key], codepoint);
2733 if (count == 0)
2734 {
2735 _glfwInputError(GLFW_PLATFORM_ERROR,
2736 "Wayland: Failed to encode codepoint for key name");
2737 return NULL;
2738 }
2739
2740 _glfw.wl.keynames[key][count] = '\0';
2741 return _glfw.wl.keynames[key];
2742 }
2743
_glfwGetKeyScancodeWayland(int key)2744 int _glfwGetKeyScancodeWayland(int key)
2745 {
2746 return _glfw.wl.scancodes[key];
2747 }
2748
_glfwCreateCursorWayland(_GLFWcursor * cursor,const GLFWimage * image,int xhot,int yhot)2749 GLFWbool _glfwCreateCursorWayland(_GLFWcursor* cursor,
2750 const GLFWimage* image,
2751 int xhot, int yhot)
2752 {
2753 cursor->wl.buffer = createShmBuffer(image);
2754 if (!cursor->wl.buffer)
2755 return GLFW_FALSE;
2756
2757 cursor->wl.width = image->width;
2758 cursor->wl.height = image->height;
2759 cursor->wl.xhot = xhot;
2760 cursor->wl.yhot = yhot;
2761 return GLFW_TRUE;
2762 }
2763
_glfwCreateStandardCursorWayland(_GLFWcursor * cursor,int shape)2764 GLFWbool _glfwCreateStandardCursorWayland(_GLFWcursor* cursor, int shape)
2765 {
2766 const char* name = NULL;
2767
2768 // Try the XDG names first
2769 switch (shape)
2770 {
2771 case GLFW_ARROW_CURSOR:
2772 name = "default";
2773 break;
2774 case GLFW_IBEAM_CURSOR:
2775 name = "text";
2776 break;
2777 case GLFW_CROSSHAIR_CURSOR:
2778 name = "crosshair";
2779 break;
2780 case GLFW_POINTING_HAND_CURSOR:
2781 name = "pointer";
2782 break;
2783 case GLFW_RESIZE_EW_CURSOR:
2784 name = "ew-resize";
2785 break;
2786 case GLFW_RESIZE_NS_CURSOR:
2787 name = "ns-resize";
2788 break;
2789 case GLFW_RESIZE_NWSE_CURSOR:
2790 name = "nwse-resize";
2791 break;
2792 case GLFW_RESIZE_NESW_CURSOR:
2793 name = "nesw-resize";
2794 break;
2795 case GLFW_RESIZE_ALL_CURSOR:
2796 name = "all-scroll";
2797 break;
2798 case GLFW_NOT_ALLOWED_CURSOR:
2799 name = "not-allowed";
2800 break;
2801 }
2802
2803 cursor->wl.cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name);
2804
2805 if (_glfw.wl.cursorThemeHiDPI)
2806 {
2807 cursor->wl.cursorHiDPI =
2808 wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, name);
2809 }
2810
2811 if (!cursor->wl.cursor)
2812 {
2813 // Fall back to the core X11 names
2814 switch (shape)
2815 {
2816 case GLFW_ARROW_CURSOR:
2817 name = "left_ptr";
2818 break;
2819 case GLFW_IBEAM_CURSOR:
2820 name = "xterm";
2821 break;
2822 case GLFW_CROSSHAIR_CURSOR:
2823 name = "crosshair";
2824 break;
2825 case GLFW_POINTING_HAND_CURSOR:
2826 name = "hand2";
2827 break;
2828 case GLFW_RESIZE_EW_CURSOR:
2829 name = "sb_h_double_arrow";
2830 break;
2831 case GLFW_RESIZE_NS_CURSOR:
2832 name = "sb_v_double_arrow";
2833 break;
2834 case GLFW_RESIZE_ALL_CURSOR:
2835 name = "fleur";
2836 break;
2837 default:
2838 _glfwInputError(GLFW_CURSOR_UNAVAILABLE,
2839 "Wayland: Standard cursor shape unavailable");
2840 return GLFW_FALSE;
2841 }
2842
2843 cursor->wl.cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name);
2844 if (!cursor->wl.cursor)
2845 {
2846 _glfwInputError(GLFW_CURSOR_UNAVAILABLE,
2847 "Wayland: Failed to create standard cursor \"%s\"",
2848 name);
2849 return GLFW_FALSE;
2850 }
2851
2852 if (_glfw.wl.cursorThemeHiDPI)
2853 {
2854 if (!cursor->wl.cursorHiDPI)
2855 {
2856 cursor->wl.cursorHiDPI =
2857 wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, name);
2858 }
2859 }
2860 }
2861
2862 return GLFW_TRUE;
2863 }
2864
_glfwDestroyCursorWayland(_GLFWcursor * cursor)2865 void _glfwDestroyCursorWayland(_GLFWcursor* cursor)
2866 {
2867 // If it's a standard cursor we don't need to do anything here
2868 if (cursor->wl.cursor)
2869 return;
2870
2871 if (cursor->wl.buffer)
2872 wl_buffer_destroy(cursor->wl.buffer);
2873 }
2874
relativePointerHandleRelativeMotion(void * userData,struct zwp_relative_pointer_v1 * pointer,uint32_t timeHi,uint32_t timeLo,wl_fixed_t dx,wl_fixed_t dy,wl_fixed_t dxUnaccel,wl_fixed_t dyUnaccel)2875 static void relativePointerHandleRelativeMotion(void* userData,
2876 struct zwp_relative_pointer_v1* pointer,
2877 uint32_t timeHi,
2878 uint32_t timeLo,
2879 wl_fixed_t dx,
2880 wl_fixed_t dy,
2881 wl_fixed_t dxUnaccel,
2882 wl_fixed_t dyUnaccel)
2883 {
2884 _GLFWwindow* window = userData;
2885 double xpos = window->virtualCursorPosX;
2886 double ypos = window->virtualCursorPosY;
2887
2888 if (window->cursorMode != GLFW_CURSOR_DISABLED)
2889 return;
2890
2891 if (window->rawMouseMotion)
2892 {
2893 xpos += wl_fixed_to_double(dxUnaccel);
2894 ypos += wl_fixed_to_double(dyUnaccel);
2895 }
2896 else
2897 {
2898 xpos += wl_fixed_to_double(dx);
2899 ypos += wl_fixed_to_double(dy);
2900 }
2901
2902 _glfwInputCursorPos(window, xpos, ypos);
2903 }
2904
2905 static const struct zwp_relative_pointer_v1_listener relativePointerListener =
2906 {
2907 relativePointerHandleRelativeMotion
2908 };
2909
lockedPointerHandleLocked(void * userData,struct zwp_locked_pointer_v1 * lockedPointer)2910 static void lockedPointerHandleLocked(void* userData,
2911 struct zwp_locked_pointer_v1* lockedPointer)
2912 {
2913 }
2914
lockedPointerHandleUnlocked(void * userData,struct zwp_locked_pointer_v1 * lockedPointer)2915 static void lockedPointerHandleUnlocked(void* userData,
2916 struct zwp_locked_pointer_v1* lockedPointer)
2917 {
2918 }
2919
2920 static const struct zwp_locked_pointer_v1_listener lockedPointerListener =
2921 {
2922 lockedPointerHandleLocked,
2923 lockedPointerHandleUnlocked
2924 };
2925
lockPointer(_GLFWwindow * window)2926 static void lockPointer(_GLFWwindow* window)
2927 {
2928 if (!_glfw.wl.relativePointerManager)
2929 {
2930 _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2931 "Wayland: The compositor does not support pointer locking");
2932 return;
2933 }
2934
2935 window->wl.relativePointer =
2936 zwp_relative_pointer_manager_v1_get_relative_pointer(
2937 _glfw.wl.relativePointerManager,
2938 _glfw.wl.pointer);
2939 zwp_relative_pointer_v1_add_listener(window->wl.relativePointer,
2940 &relativePointerListener,
2941 window);
2942
2943 window->wl.lockedPointer =
2944 zwp_pointer_constraints_v1_lock_pointer(
2945 _glfw.wl.pointerConstraints,
2946 window->wl.surface,
2947 _glfw.wl.pointer,
2948 NULL,
2949 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
2950 zwp_locked_pointer_v1_add_listener(window->wl.lockedPointer,
2951 &lockedPointerListener,
2952 window);
2953 }
2954
unlockPointer(_GLFWwindow * window)2955 static void unlockPointer(_GLFWwindow* window)
2956 {
2957 zwp_relative_pointer_v1_destroy(window->wl.relativePointer);
2958 window->wl.relativePointer = NULL;
2959
2960 zwp_locked_pointer_v1_destroy(window->wl.lockedPointer);
2961 window->wl.lockedPointer = NULL;
2962 }
2963
confinedPointerHandleConfined(void * userData,struct zwp_confined_pointer_v1 * confinedPointer)2964 static void confinedPointerHandleConfined(void* userData,
2965 struct zwp_confined_pointer_v1* confinedPointer)
2966 {
2967 }
2968
confinedPointerHandleUnconfined(void * userData,struct zwp_confined_pointer_v1 * confinedPointer)2969 static void confinedPointerHandleUnconfined(void* userData,
2970 struct zwp_confined_pointer_v1* confinedPointer)
2971 {
2972 }
2973
2974 static const struct zwp_confined_pointer_v1_listener confinedPointerListener =
2975 {
2976 confinedPointerHandleConfined,
2977 confinedPointerHandleUnconfined
2978 };
2979
confinePointer(_GLFWwindow * window)2980 static void confinePointer(_GLFWwindow* window)
2981 {
2982 window->wl.confinedPointer =
2983 zwp_pointer_constraints_v1_confine_pointer(
2984 _glfw.wl.pointerConstraints,
2985 window->wl.surface,
2986 _glfw.wl.pointer,
2987 NULL,
2988 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
2989
2990 zwp_confined_pointer_v1_add_listener(window->wl.confinedPointer,
2991 &confinedPointerListener,
2992 window);
2993 }
2994
unconfinePointer(_GLFWwindow * window)2995 static void unconfinePointer(_GLFWwindow* window)
2996 {
2997 zwp_confined_pointer_v1_destroy(window->wl.confinedPointer);
2998 window->wl.confinedPointer = NULL;
2999 }
3000
_glfwSetCursorWayland(_GLFWwindow * window,_GLFWcursor * cursor)3001 void _glfwSetCursorWayland(_GLFWwindow* window, _GLFWcursor* cursor)
3002 {
3003 if (!_glfw.wl.pointer)
3004 return;
3005
3006 window->wl.currentCursor = cursor;
3007
3008 // If we're not in the correct window just save the cursor
3009 // the next time the pointer enters the window the cursor will change
3010 if (!window->wl.hovered)
3011 return;
3012
3013 // Update pointer lock to match cursor mode
3014 if (window->cursorMode == GLFW_CURSOR_DISABLED)
3015 {
3016 if (window->wl.confinedPointer)
3017 unconfinePointer(window);
3018 if (!window->wl.lockedPointer)
3019 lockPointer(window);
3020 }
3021 else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
3022 {
3023 if (window->wl.lockedPointer)
3024 unlockPointer(window);
3025 if (!window->wl.confinedPointer)
3026 confinePointer(window);
3027 }
3028 else if (window->cursorMode == GLFW_CURSOR_NORMAL ||
3029 window->cursorMode == GLFW_CURSOR_HIDDEN)
3030 {
3031 if (window->wl.lockedPointer)
3032 unlockPointer(window);
3033 else if (window->wl.confinedPointer)
3034 unconfinePointer(window);
3035 }
3036
3037 if (window->cursorMode == GLFW_CURSOR_NORMAL ||
3038 window->cursorMode == GLFW_CURSOR_CAPTURED)
3039 {
3040 if (cursor)
3041 setCursorImage(window, &cursor->wl);
3042 else
3043 {
3044 struct wl_cursor* defaultCursor =
3045 wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, "left_ptr");
3046 if (!defaultCursor)
3047 {
3048 _glfwInputError(GLFW_PLATFORM_ERROR,
3049 "Wayland: Standard cursor not found");
3050 return;
3051 }
3052
3053 struct wl_cursor* defaultCursorHiDPI = NULL;
3054 if (_glfw.wl.cursorThemeHiDPI)
3055 {
3056 defaultCursorHiDPI =
3057 wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, "left_ptr");
3058 }
3059
3060 _GLFWcursorWayland cursorWayland =
3061 {
3062 defaultCursor,
3063 defaultCursorHiDPI,
3064 NULL,
3065 0, 0,
3066 0, 0,
3067 0
3068 };
3069
3070 setCursorImage(window, &cursorWayland);
3071 }
3072 }
3073 else if (window->cursorMode == GLFW_CURSOR_HIDDEN ||
3074 window->cursorMode == GLFW_CURSOR_DISABLED)
3075 {
3076 wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial, NULL, 0, 0);
3077 }
3078 }
3079
dataSourceHandleTarget(void * userData,struct wl_data_source * source,const char * mimeType)3080 static void dataSourceHandleTarget(void* userData,
3081 struct wl_data_source* source,
3082 const char* mimeType)
3083 {
3084 if (_glfw.wl.selectionSource != source)
3085 {
3086 _glfwInputError(GLFW_PLATFORM_ERROR,
3087 "Wayland: Unknown clipboard data source");
3088 return;
3089 }
3090 }
3091
dataSourceHandleSend(void * userData,struct wl_data_source * source,const char * mimeType,int fd)3092 static void dataSourceHandleSend(void* userData,
3093 struct wl_data_source* source,
3094 const char* mimeType,
3095 int fd)
3096 {
3097 // Ignore it if this is an outdated or invalid request
3098 if (_glfw.wl.selectionSource != source ||
3099 strcmp(mimeType, "text/plain;charset=utf-8") != 0)
3100 {
3101 close(fd);
3102 return;
3103 }
3104
3105 char* string = _glfw.wl.clipboardString;
3106 size_t length = strlen(string);
3107
3108 while (length > 0)
3109 {
3110 const ssize_t result = write(fd, string, length);
3111 if (result == -1)
3112 {
3113 if (errno == EINTR)
3114 continue;
3115
3116 _glfwInputError(GLFW_PLATFORM_ERROR,
3117 "Wayland: Error while writing the clipboard: %s",
3118 strerror(errno));
3119 break;
3120 }
3121
3122 length -= result;
3123 string += result;
3124 }
3125
3126 close(fd);
3127 }
3128
dataSourceHandleCancelled(void * userData,struct wl_data_source * source)3129 static void dataSourceHandleCancelled(void* userData,
3130 struct wl_data_source* source)
3131 {
3132 wl_data_source_destroy(source);
3133
3134 if (_glfw.wl.selectionSource != source)
3135 return;
3136
3137 _glfw.wl.selectionSource = NULL;
3138 }
3139
3140 static const struct wl_data_source_listener dataSourceListener =
3141 {
3142 dataSourceHandleTarget,
3143 dataSourceHandleSend,
3144 dataSourceHandleCancelled,
3145 };
3146
_glfwSetClipboardStringWayland(const char * string)3147 void _glfwSetClipboardStringWayland(const char* string)
3148 {
3149 if (_glfw.wl.selectionSource)
3150 {
3151 wl_data_source_destroy(_glfw.wl.selectionSource);
3152 _glfw.wl.selectionSource = NULL;
3153 }
3154
3155 char* copy = _glfw_strdup(string);
3156 if (!copy)
3157 {
3158 _glfwInputError(GLFW_OUT_OF_MEMORY, NULL);
3159 return;
3160 }
3161
3162 _glfw_free(_glfw.wl.clipboardString);
3163 _glfw.wl.clipboardString = copy;
3164
3165 _glfw.wl.selectionSource =
3166 wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager);
3167 if (!_glfw.wl.selectionSource)
3168 {
3169 _glfwInputError(GLFW_PLATFORM_ERROR,
3170 "Wayland: Failed to create clipboard data source");
3171 return;
3172 }
3173 wl_data_source_add_listener(_glfw.wl.selectionSource,
3174 &dataSourceListener,
3175 NULL);
3176 wl_data_source_offer(_glfw.wl.selectionSource, "text/plain;charset=utf-8");
3177 wl_data_device_set_selection(_glfw.wl.dataDevice,
3178 _glfw.wl.selectionSource,
3179 _glfw.wl.serial);
3180 }
3181
_glfwGetClipboardStringWayland(void)3182 const char* _glfwGetClipboardStringWayland(void)
3183 {
3184 if (!_glfw.wl.selectionOffer)
3185 {
3186 _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
3187 "Wayland: No clipboard data available");
3188 return NULL;
3189 }
3190
3191 if (_glfw.wl.selectionSource)
3192 return _glfw.wl.clipboardString;
3193
3194 _glfw_free(_glfw.wl.clipboardString);
3195 _glfw.wl.clipboardString =
3196 readDataOfferAsString(_glfw.wl.selectionOffer, "text/plain;charset=utf-8");
3197 return _glfw.wl.clipboardString;
3198 }
3199
_glfwGetEGLPlatformWayland(EGLint ** attribs)3200 EGLenum _glfwGetEGLPlatformWayland(EGLint** attribs)
3201 {
3202 if (_glfw.egl.EXT_platform_base && _glfw.egl.EXT_platform_wayland)
3203 return EGL_PLATFORM_WAYLAND_EXT;
3204 else
3205 return 0;
3206 }
3207
_glfwGetEGLNativeDisplayWayland(void)3208 EGLNativeDisplayType _glfwGetEGLNativeDisplayWayland(void)
3209 {
3210 return _glfw.wl.display;
3211 }
3212
_glfwGetEGLNativeWindowWayland(_GLFWwindow * window)3213 EGLNativeWindowType _glfwGetEGLNativeWindowWayland(_GLFWwindow* window)
3214 {
3215 return window->wl.egl.window;
3216 }
3217
_glfwGetRequiredInstanceExtensionsWayland(char ** extensions)3218 void _glfwGetRequiredInstanceExtensionsWayland(char** extensions)
3219 {
3220 if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_wayland_surface)
3221 return;
3222
3223 extensions[0] = "VK_KHR_surface";
3224 extensions[1] = "VK_KHR_wayland_surface";
3225 }
3226
_glfwGetPhysicalDevicePresentationSupportWayland(VkInstance instance,VkPhysicalDevice device,uint32_t queuefamily)3227 GLFWbool _glfwGetPhysicalDevicePresentationSupportWayland(VkInstance instance,
3228 VkPhysicalDevice device,
3229 uint32_t queuefamily)
3230 {
3231 PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
3232 vkGetPhysicalDeviceWaylandPresentationSupportKHR =
3233 (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)
3234 vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR");
3235 if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR)
3236 {
3237 _glfwInputError(GLFW_API_UNAVAILABLE,
3238 "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension");
3239 return VK_NULL_HANDLE;
3240 }
3241
3242 return vkGetPhysicalDeviceWaylandPresentationSupportKHR(device,
3243 queuefamily,
3244 _glfw.wl.display);
3245 }
3246
_glfwCreateWindowSurfaceWayland(VkInstance instance,_GLFWwindow * window,const VkAllocationCallbacks * allocator,VkSurfaceKHR * surface)3247 VkResult _glfwCreateWindowSurfaceWayland(VkInstance instance,
3248 _GLFWwindow* window,
3249 const VkAllocationCallbacks* allocator,
3250 VkSurfaceKHR* surface)
3251 {
3252 VkResult err;
3253 VkWaylandSurfaceCreateInfoKHR sci;
3254 PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR;
3255
3256 vkCreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR)
3257 vkGetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR");
3258 if (!vkCreateWaylandSurfaceKHR)
3259 {
3260 _glfwInputError(GLFW_API_UNAVAILABLE,
3261 "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension");
3262 return VK_ERROR_EXTENSION_NOT_PRESENT;
3263 }
3264
3265 memset(&sci, 0, sizeof(sci));
3266 sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
3267 sci.display = _glfw.wl.display;
3268 sci.surface = window->wl.surface;
3269
3270 err = vkCreateWaylandSurfaceKHR(instance, &sci, allocator, surface);
3271 if (err)
3272 {
3273 _glfwInputError(GLFW_PLATFORM_ERROR,
3274 "Wayland: Failed to create Vulkan surface: %s",
3275 _glfwGetVulkanResultString(err));
3276 }
3277
3278 return err;
3279 }
3280
3281
3282 //////////////////////////////////////////////////////////////////////////
3283 ////// GLFW native API //////
3284 //////////////////////////////////////////////////////////////////////////
3285
glfwGetWaylandDisplay(void)3286 GLFWAPI struct wl_display* glfwGetWaylandDisplay(void)
3287 {
3288 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
3289
3290 if (_glfw.platform.platformID != GLFW_PLATFORM_WAYLAND)
3291 {
3292 _glfwInputError(GLFW_PLATFORM_UNAVAILABLE,
3293 "Wayland: Platform not initialized");
3294 return NULL;
3295 }
3296
3297 return _glfw.wl.display;
3298 }
3299
glfwGetWaylandWindow(GLFWwindow * handle)3300 GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle)
3301 {
3302 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
3303
3304 if (_glfw.platform.platformID != GLFW_PLATFORM_WAYLAND)
3305 {
3306 _glfwInputError(GLFW_PLATFORM_UNAVAILABLE,
3307 "Wayland: Platform not initialized");
3308 return NULL;
3309 }
3310
3311 _GLFWwindow* window = (_GLFWwindow*) handle;
3312 assert(window != NULL);
3313
3314 return window->wl.surface;
3315 }
3316
3317 #endif // _GLFW_WAYLAND
3318
3319