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 _glfw_free(string);
1333 close(fds[0]);
1334 return NULL;
1335 }
1336
1337 string = longer;
1338 size = requiredSize;
1339 }
1340
1341 const ssize_t result = read(fds[0], string + length, readSize);
1342 if (result == 0)
1343 break;
1344 else if (result == -1)
1345 {
1346 if (errno == EINTR)
1347 continue;
1348
1349 _glfwInputError(GLFW_PLATFORM_ERROR,
1350 "Wayland: Failed to read from data offer pipe: %s",
1351 strerror(errno));
1352 _glfw_free(string);
1353 close(fds[0]);
1354 return NULL;
1355 }
1356
1357 length += result;
1358 }
1359
1360 close(fds[0]);
1361
1362 string[length] = '\0';
1363 return string;
1364 }
1365
pointerHandleEnter(void * userData,struct wl_pointer * pointer,uint32_t serial,struct wl_surface * surface,wl_fixed_t sx,wl_fixed_t sy)1366 static void pointerHandleEnter(void* userData,
1367 struct wl_pointer* pointer,
1368 uint32_t serial,
1369 struct wl_surface* surface,
1370 wl_fixed_t sx,
1371 wl_fixed_t sy)
1372 {
1373 // Happens in the case we just destroyed the surface.
1374 if (!surface)
1375 return;
1376
1377 if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag)
1378 return;
1379
1380 _GLFWwindow* window = wl_surface_get_user_data(surface);
1381
1382 _glfw.wl.serial = serial;
1383 _glfw.wl.pointerEnterSerial = serial;
1384 _glfw.wl.pointerFocus = window;
1385
1386 if (surface == window->wl.surface)
1387 {
1388 window->wl.hovered = GLFW_TRUE;
1389 _glfwSetCursorWayland(window, window->wl.currentCursor);
1390 _glfwInputCursorEnter(window, GLFW_TRUE);
1391 }
1392 else
1393 {
1394 if (window->wl.fallback.decorations)
1395 window->wl.fallback.focus = surface;
1396 }
1397 }
1398
pointerHandleLeave(void * userData,struct wl_pointer * pointer,uint32_t serial,struct wl_surface * surface)1399 static void pointerHandleLeave(void* userData,
1400 struct wl_pointer* pointer,
1401 uint32_t serial,
1402 struct wl_surface* surface)
1403 {
1404 if (!surface)
1405 return;
1406
1407 if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag)
1408 return;
1409
1410 _GLFWwindow* window = _glfw.wl.pointerFocus;
1411 if (!window)
1412 return;
1413
1414 _glfw.wl.serial = serial;
1415 _glfw.wl.pointerFocus = NULL;
1416 _glfw.wl.cursorPreviousName = NULL;
1417
1418 if (window->wl.hovered)
1419 {
1420 window->wl.hovered = GLFW_FALSE;
1421 _glfwInputCursorEnter(window, GLFW_FALSE);
1422 }
1423 else
1424 {
1425 if (window->wl.fallback.decorations)
1426 window->wl.fallback.focus = NULL;
1427 }
1428 }
1429
pointerHandleMotion(void * userData,struct wl_pointer * pointer,uint32_t time,wl_fixed_t sx,wl_fixed_t sy)1430 static void pointerHandleMotion(void* userData,
1431 struct wl_pointer* pointer,
1432 uint32_t time,
1433 wl_fixed_t sx,
1434 wl_fixed_t sy)
1435 {
1436 _GLFWwindow* window = _glfw.wl.pointerFocus;
1437 if (!window)
1438 return;
1439
1440 if (window->cursorMode == GLFW_CURSOR_DISABLED)
1441 return;
1442
1443 const double xpos = wl_fixed_to_double(sx);
1444 const double ypos = wl_fixed_to_double(sy);
1445 window->wl.cursorPosX = xpos;
1446 window->wl.cursorPosY = ypos;
1447
1448 if (window->wl.hovered)
1449 {
1450 _glfw.wl.cursorPreviousName = NULL;
1451 _glfwInputCursorPos(window, xpos, ypos);
1452 return;
1453 }
1454
1455 if (window->wl.fallback.decorations)
1456 {
1457 const char* cursorName = "left_ptr";
1458
1459 if (window->resizable)
1460 {
1461 if (window->wl.fallback.focus == window->wl.fallback.top.surface)
1462 {
1463 if (ypos < GLFW_BORDER_SIZE)
1464 cursorName = "n-resize";
1465 }
1466 else if (window->wl.fallback.focus == window->wl.fallback.left.surface)
1467 {
1468 if (ypos < GLFW_BORDER_SIZE)
1469 cursorName = "nw-resize";
1470 else
1471 cursorName = "w-resize";
1472 }
1473 else if (window->wl.fallback.focus == window->wl.fallback.right.surface)
1474 {
1475 if (ypos < GLFW_BORDER_SIZE)
1476 cursorName = "ne-resize";
1477 else
1478 cursorName = "e-resize";
1479 }
1480 else if (window->wl.fallback.focus == window->wl.fallback.bottom.surface)
1481 {
1482 if (xpos < GLFW_BORDER_SIZE)
1483 cursorName = "sw-resize";
1484 else if (xpos > window->wl.width + GLFW_BORDER_SIZE)
1485 cursorName = "se-resize";
1486 else
1487 cursorName = "s-resize";
1488 }
1489 }
1490
1491 if (_glfw.wl.cursorPreviousName != cursorName)
1492 {
1493 struct wl_surface* surface = _glfw.wl.cursorSurface;
1494 struct wl_cursor_theme* theme = _glfw.wl.cursorTheme;
1495 int scale = 1;
1496
1497 if (window->wl.bufferScale > 1 && _glfw.wl.cursorThemeHiDPI)
1498 {
1499 // We only support up to scale=2 for now, since libwayland-cursor
1500 // requires us to load a different theme for each size.
1501 scale = 2;
1502 theme = _glfw.wl.cursorThemeHiDPI;
1503 }
1504
1505 struct wl_cursor* cursor = wl_cursor_theme_get_cursor(theme, cursorName);
1506 if (!cursor)
1507 return;
1508
1509 // TODO: handle animated cursors too.
1510 struct wl_cursor_image* image = cursor->images[0];
1511 if (!image)
1512 return;
1513
1514 struct wl_buffer* buffer = wl_cursor_image_get_buffer(image);
1515 if (!buffer)
1516 return;
1517
1518 wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial,
1519 surface,
1520 image->hotspot_x / scale,
1521 image->hotspot_y / scale);
1522 wl_surface_set_buffer_scale(surface, scale);
1523 wl_surface_attach(surface, buffer, 0, 0);
1524 wl_surface_damage(surface, 0, 0, image->width, image->height);
1525 wl_surface_commit(surface);
1526
1527 _glfw.wl.cursorPreviousName = cursorName;
1528 }
1529 }
1530 }
1531
pointerHandleButton(void * userData,struct wl_pointer * pointer,uint32_t serial,uint32_t time,uint32_t button,uint32_t state)1532 static void pointerHandleButton(void* userData,
1533 struct wl_pointer* pointer,
1534 uint32_t serial,
1535 uint32_t time,
1536 uint32_t button,
1537 uint32_t state)
1538 {
1539 _GLFWwindow* window = _glfw.wl.pointerFocus;
1540 if (!window)
1541 return;
1542
1543 if (window->wl.hovered)
1544 {
1545 _glfw.wl.serial = serial;
1546
1547 _glfwInputMouseClick(window,
1548 button - BTN_LEFT,
1549 state == WL_POINTER_BUTTON_STATE_PRESSED,
1550 _glfw.wl.xkb.modifiers);
1551 return;
1552 }
1553
1554 if (window->wl.fallback.decorations)
1555 {
1556 if (button == BTN_LEFT)
1557 {
1558 uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE;
1559
1560 if (window->wl.fallback.focus == window->wl.fallback.top.surface)
1561 {
1562 if (window->wl.cursorPosY < GLFW_BORDER_SIZE)
1563 edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP;
1564 else
1565 xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial);
1566 }
1567 else if (window->wl.fallback.focus == window->wl.fallback.left.surface)
1568 {
1569 if (window->wl.cursorPosY < GLFW_BORDER_SIZE)
1570 edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
1571 else
1572 edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
1573 }
1574 else if (window->wl.fallback.focus == window->wl.fallback.right.surface)
1575 {
1576 if (window->wl.cursorPosY < GLFW_BORDER_SIZE)
1577 edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
1578 else
1579 edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
1580 }
1581 else if (window->wl.fallback.focus == window->wl.fallback.bottom.surface)
1582 {
1583 if (window->wl.cursorPosX < GLFW_BORDER_SIZE)
1584 edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
1585 else if (window->wl.cursorPosX > window->wl.width + GLFW_BORDER_SIZE)
1586 edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
1587 else
1588 edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
1589 }
1590
1591 if (edges != XDG_TOPLEVEL_RESIZE_EDGE_NONE)
1592 {
1593 xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat,
1594 serial, edges);
1595 }
1596 }
1597 else if (button == BTN_RIGHT)
1598 {
1599 if (window->wl.xdg.toplevel)
1600 {
1601 xdg_toplevel_show_window_menu(window->wl.xdg.toplevel,
1602 _glfw.wl.seat, serial,
1603 window->wl.cursorPosX,
1604 window->wl.cursorPosY);
1605 }
1606 }
1607 }
1608 }
1609
pointerHandleAxis(void * userData,struct wl_pointer * pointer,uint32_t time,uint32_t axis,wl_fixed_t value)1610 static void pointerHandleAxis(void* userData,
1611 struct wl_pointer* pointer,
1612 uint32_t time,
1613 uint32_t axis,
1614 wl_fixed_t value)
1615 {
1616 _GLFWwindow* window = _glfw.wl.pointerFocus;
1617 if (!window)
1618 return;
1619
1620 // NOTE: 10 units of motion per mouse wheel step seems to be a common ratio
1621 if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
1622 _glfwInputScroll(window, -wl_fixed_to_double(value) / 10.0, 0.0);
1623 else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
1624 _glfwInputScroll(window, 0.0, -wl_fixed_to_double(value) / 10.0);
1625 }
1626
1627 static const struct wl_pointer_listener pointerListener =
1628 {
1629 pointerHandleEnter,
1630 pointerHandleLeave,
1631 pointerHandleMotion,
1632 pointerHandleButton,
1633 pointerHandleAxis,
1634 };
1635
keyboardHandleKeymap(void * userData,struct wl_keyboard * keyboard,uint32_t format,int fd,uint32_t size)1636 static void keyboardHandleKeymap(void* userData,
1637 struct wl_keyboard* keyboard,
1638 uint32_t format,
1639 int fd,
1640 uint32_t size)
1641 {
1642 struct xkb_keymap* keymap;
1643 struct xkb_state* state;
1644 struct xkb_compose_table* composeTable;
1645 struct xkb_compose_state* composeState;
1646
1647 char* mapStr;
1648 const char* locale;
1649
1650 if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
1651 {
1652 close(fd);
1653 return;
1654 }
1655
1656 mapStr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
1657 if (mapStr == MAP_FAILED) {
1658 close(fd);
1659 return;
1660 }
1661
1662 keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context,
1663 mapStr,
1664 XKB_KEYMAP_FORMAT_TEXT_V1,
1665 0);
1666 munmap(mapStr, size);
1667 close(fd);
1668
1669 if (!keymap)
1670 {
1671 _glfwInputError(GLFW_PLATFORM_ERROR,
1672 "Wayland: Failed to compile keymap");
1673 return;
1674 }
1675
1676 state = xkb_state_new(keymap);
1677 if (!state)
1678 {
1679 _glfwInputError(GLFW_PLATFORM_ERROR,
1680 "Wayland: Failed to create XKB state");
1681 xkb_keymap_unref(keymap);
1682 return;
1683 }
1684
1685 // Look up the preferred locale, falling back to "C" as default.
1686 locale = getenv("LC_ALL");
1687 if (!locale)
1688 locale = getenv("LC_CTYPE");
1689 if (!locale)
1690 locale = getenv("LANG");
1691 if (!locale)
1692 locale = "C";
1693
1694 composeTable =
1695 xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale,
1696 XKB_COMPOSE_COMPILE_NO_FLAGS);
1697 if (composeTable)
1698 {
1699 composeState =
1700 xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS);
1701 xkb_compose_table_unref(composeTable);
1702 if (composeState)
1703 _glfw.wl.xkb.composeState = composeState;
1704 else
1705 _glfwInputError(GLFW_PLATFORM_ERROR,
1706 "Wayland: Failed to create XKB compose state");
1707 }
1708 else
1709 {
1710 _glfwInputError(GLFW_PLATFORM_ERROR,
1711 "Wayland: Failed to create XKB compose table");
1712 }
1713
1714 xkb_keymap_unref(_glfw.wl.xkb.keymap);
1715 xkb_state_unref(_glfw.wl.xkb.state);
1716 _glfw.wl.xkb.keymap = keymap;
1717 _glfw.wl.xkb.state = state;
1718
1719 _glfw.wl.xkb.controlIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control");
1720 _glfw.wl.xkb.altIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1");
1721 _glfw.wl.xkb.shiftIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift");
1722 _glfw.wl.xkb.superIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4");
1723 _glfw.wl.xkb.capsLockIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Lock");
1724 _glfw.wl.xkb.numLockIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2");
1725 }
1726
keyboardHandleEnter(void * userData,struct wl_keyboard * keyboard,uint32_t serial,struct wl_surface * surface,struct wl_array * keys)1727 static void keyboardHandleEnter(void* userData,
1728 struct wl_keyboard* keyboard,
1729 uint32_t serial,
1730 struct wl_surface* surface,
1731 struct wl_array* keys)
1732 {
1733 // Happens in the case we just destroyed the surface.
1734 if (!surface)
1735 return;
1736
1737 if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag)
1738 return;
1739
1740 _GLFWwindow* window = wl_surface_get_user_data(surface);
1741 if (surface != window->wl.surface)
1742 return;
1743
1744 _glfw.wl.serial = serial;
1745 _glfw.wl.keyboardFocus = window;
1746 _glfwInputWindowFocus(window, GLFW_TRUE);
1747 }
1748
keyboardHandleLeave(void * userData,struct wl_keyboard * keyboard,uint32_t serial,struct wl_surface * surface)1749 static void keyboardHandleLeave(void* userData,
1750 struct wl_keyboard* keyboard,
1751 uint32_t serial,
1752 struct wl_surface* surface)
1753 {
1754 _GLFWwindow* window = _glfw.wl.keyboardFocus;
1755
1756 if (!window)
1757 return;
1758
1759 struct itimerspec timer = {0};
1760 timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL);
1761
1762 _glfw.wl.serial = serial;
1763 _glfw.wl.keyboardFocus = NULL;
1764 _glfwInputWindowFocus(window, GLFW_FALSE);
1765 }
1766
keyboardHandleKey(void * userData,struct wl_keyboard * keyboard,uint32_t serial,uint32_t time,uint32_t scancode,uint32_t state)1767 static void keyboardHandleKey(void* userData,
1768 struct wl_keyboard* keyboard,
1769 uint32_t serial,
1770 uint32_t time,
1771 uint32_t scancode,
1772 uint32_t state)
1773 {
1774 _GLFWwindow* window = _glfw.wl.keyboardFocus;
1775 if (!window)
1776 return;
1777
1778 const int key = translateKey(scancode);
1779 const int action =
1780 state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE;
1781
1782 _glfw.wl.serial = serial;
1783
1784 struct itimerspec timer = {0};
1785
1786 if (action == GLFW_PRESS)
1787 {
1788 const xkb_keycode_t keycode = scancode + 8;
1789
1790 if (xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, keycode) &&
1791 _glfw.wl.keyRepeatRate > 0)
1792 {
1793 _glfw.wl.keyRepeatScancode = scancode;
1794 if (_glfw.wl.keyRepeatRate > 1)
1795 timer.it_interval.tv_nsec = 1000000000 / _glfw.wl.keyRepeatRate;
1796 else
1797 timer.it_interval.tv_sec = 1;
1798
1799 timer.it_value.tv_sec = _glfw.wl.keyRepeatDelay / 1000;
1800 timer.it_value.tv_nsec = (_glfw.wl.keyRepeatDelay % 1000) * 1000000;
1801 }
1802 }
1803
1804 timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL);
1805
1806 _glfwInputKey(window, key, scancode, action, _glfw.wl.xkb.modifiers);
1807
1808 if (action == GLFW_PRESS)
1809 inputText(window, scancode);
1810 }
1811
keyboardHandleModifiers(void * userData,struct wl_keyboard * keyboard,uint32_t serial,uint32_t modsDepressed,uint32_t modsLatched,uint32_t modsLocked,uint32_t group)1812 static void keyboardHandleModifiers(void* userData,
1813 struct wl_keyboard* keyboard,
1814 uint32_t serial,
1815 uint32_t modsDepressed,
1816 uint32_t modsLatched,
1817 uint32_t modsLocked,
1818 uint32_t group)
1819 {
1820 _glfw.wl.serial = serial;
1821
1822 if (!_glfw.wl.xkb.keymap)
1823 return;
1824
1825 xkb_state_update_mask(_glfw.wl.xkb.state,
1826 modsDepressed,
1827 modsLatched,
1828 modsLocked,
1829 0,
1830 0,
1831 group);
1832
1833 _glfw.wl.xkb.modifiers = 0;
1834
1835 struct
1836 {
1837 xkb_mod_index_t index;
1838 unsigned int bit;
1839 } modifiers[] =
1840 {
1841 { _glfw.wl.xkb.controlIndex, GLFW_MOD_CONTROL },
1842 { _glfw.wl.xkb.altIndex, GLFW_MOD_ALT },
1843 { _glfw.wl.xkb.shiftIndex, GLFW_MOD_SHIFT },
1844 { _glfw.wl.xkb.superIndex, GLFW_MOD_SUPER },
1845 { _glfw.wl.xkb.capsLockIndex, GLFW_MOD_CAPS_LOCK },
1846 { _glfw.wl.xkb.numLockIndex, GLFW_MOD_NUM_LOCK }
1847 };
1848
1849 for (size_t i = 0; i < sizeof(modifiers) / sizeof(modifiers[0]); i++)
1850 {
1851 if (xkb_state_mod_index_is_active(_glfw.wl.xkb.state,
1852 modifiers[i].index,
1853 XKB_STATE_MODS_EFFECTIVE) == 1)
1854 {
1855 _glfw.wl.xkb.modifiers |= modifiers[i].bit;
1856 }
1857 }
1858 }
1859
keyboardHandleRepeatInfo(void * userData,struct wl_keyboard * keyboard,int32_t rate,int32_t delay)1860 static void keyboardHandleRepeatInfo(void* userData,
1861 struct wl_keyboard* keyboard,
1862 int32_t rate,
1863 int32_t delay)
1864 {
1865 if (keyboard != _glfw.wl.keyboard)
1866 return;
1867
1868 _glfw.wl.keyRepeatRate = rate;
1869 _glfw.wl.keyRepeatDelay = delay;
1870 }
1871
1872 static const struct wl_keyboard_listener keyboardListener =
1873 {
1874 keyboardHandleKeymap,
1875 keyboardHandleEnter,
1876 keyboardHandleLeave,
1877 keyboardHandleKey,
1878 keyboardHandleModifiers,
1879 keyboardHandleRepeatInfo,
1880 };
1881
seatHandleCapabilities(void * userData,struct wl_seat * seat,enum wl_seat_capability caps)1882 static void seatHandleCapabilities(void* userData,
1883 struct wl_seat* seat,
1884 enum wl_seat_capability caps)
1885 {
1886 if ((caps & WL_SEAT_CAPABILITY_POINTER) && !_glfw.wl.pointer)
1887 {
1888 _glfw.wl.pointer = wl_seat_get_pointer(seat);
1889 wl_pointer_add_listener(_glfw.wl.pointer, &pointerListener, NULL);
1890 }
1891 else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer)
1892 {
1893 wl_pointer_destroy(_glfw.wl.pointer);
1894 _glfw.wl.pointer = NULL;
1895 }
1896
1897 if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard)
1898 {
1899 _glfw.wl.keyboard = wl_seat_get_keyboard(seat);
1900 wl_keyboard_add_listener(_glfw.wl.keyboard, &keyboardListener, NULL);
1901 }
1902 else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard)
1903 {
1904 wl_keyboard_destroy(_glfw.wl.keyboard);
1905 _glfw.wl.keyboard = NULL;
1906 }
1907 }
1908
seatHandleName(void * userData,struct wl_seat * seat,const char * name)1909 static void seatHandleName(void* userData,
1910 struct wl_seat* seat,
1911 const char* name)
1912 {
1913 }
1914
1915 static const struct wl_seat_listener seatListener =
1916 {
1917 seatHandleCapabilities,
1918 seatHandleName,
1919 };
1920
dataOfferHandleOffer(void * userData,struct wl_data_offer * offer,const char * mimeType)1921 static void dataOfferHandleOffer(void* userData,
1922 struct wl_data_offer* offer,
1923 const char* mimeType)
1924 {
1925 for (unsigned int i = 0; i < _glfw.wl.offerCount; i++)
1926 {
1927 if (_glfw.wl.offers[i].offer == offer)
1928 {
1929 if (strcmp(mimeType, "text/plain;charset=utf-8") == 0)
1930 _glfw.wl.offers[i].text_plain_utf8 = GLFW_TRUE;
1931 else if (strcmp(mimeType, "text/uri-list") == 0)
1932 _glfw.wl.offers[i].text_uri_list = GLFW_TRUE;
1933
1934 break;
1935 }
1936 }
1937 }
1938
1939 static const struct wl_data_offer_listener dataOfferListener =
1940 {
1941 dataOfferHandleOffer
1942 };
1943
dataDeviceHandleDataOffer(void * userData,struct wl_data_device * device,struct wl_data_offer * offer)1944 static void dataDeviceHandleDataOffer(void* userData,
1945 struct wl_data_device* device,
1946 struct wl_data_offer* offer)
1947 {
1948 _GLFWofferWayland* offers =
1949 _glfw_realloc(_glfw.wl.offers,
1950 sizeof(_GLFWofferWayland) * (_glfw.wl.offerCount + 1));
1951 if (!offers)
1952 {
1953 _glfwInputError(GLFW_OUT_OF_MEMORY, NULL);
1954 return;
1955 }
1956
1957 _glfw.wl.offers = offers;
1958 _glfw.wl.offerCount++;
1959
1960 _glfw.wl.offers[_glfw.wl.offerCount - 1] = (_GLFWofferWayland) { offer };
1961 wl_data_offer_add_listener(offer, &dataOfferListener, NULL);
1962 }
1963
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)1964 static void dataDeviceHandleEnter(void* userData,
1965 struct wl_data_device* device,
1966 uint32_t serial,
1967 struct wl_surface* surface,
1968 wl_fixed_t x,
1969 wl_fixed_t y,
1970 struct wl_data_offer* offer)
1971 {
1972 if (_glfw.wl.dragOffer)
1973 {
1974 wl_data_offer_destroy(_glfw.wl.dragOffer);
1975 _glfw.wl.dragOffer = NULL;
1976 _glfw.wl.dragFocus = NULL;
1977 }
1978
1979 unsigned int i;
1980
1981 for (i = 0; i < _glfw.wl.offerCount; i++)
1982 {
1983 if (_glfw.wl.offers[i].offer == offer)
1984 break;
1985 }
1986
1987 if (i == _glfw.wl.offerCount)
1988 return;
1989
1990 if (surface && wl_proxy_get_tag((struct wl_proxy*) surface) == &_glfw.wl.tag)
1991 {
1992 _GLFWwindow* window = wl_surface_get_user_data(surface);
1993 if (window->wl.surface == surface)
1994 {
1995 if (_glfw.wl.offers[i].text_uri_list)
1996 {
1997 _glfw.wl.dragOffer = offer;
1998 _glfw.wl.dragFocus = window;
1999 _glfw.wl.dragSerial = serial;
2000
2001 wl_data_offer_accept(offer, serial, "text/uri-list");
2002 }
2003 }
2004 }
2005
2006 if (!_glfw.wl.dragOffer)
2007 {
2008 wl_data_offer_accept(offer, serial, NULL);
2009 wl_data_offer_destroy(offer);
2010 }
2011
2012 _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1];
2013 _glfw.wl.offerCount--;
2014 }
2015
dataDeviceHandleLeave(void * userData,struct wl_data_device * device)2016 static void dataDeviceHandleLeave(void* userData,
2017 struct wl_data_device* device)
2018 {
2019 if (_glfw.wl.dragOffer)
2020 {
2021 wl_data_offer_destroy(_glfw.wl.dragOffer);
2022 _glfw.wl.dragOffer = NULL;
2023 _glfw.wl.dragFocus = NULL;
2024 }
2025 }
2026
dataDeviceHandleMotion(void * userData,struct wl_data_device * device,uint32_t time,wl_fixed_t x,wl_fixed_t y)2027 static void dataDeviceHandleMotion(void* userData,
2028 struct wl_data_device* device,
2029 uint32_t time,
2030 wl_fixed_t x,
2031 wl_fixed_t y)
2032 {
2033 }
2034
dataDeviceHandleDrop(void * userData,struct wl_data_device * device)2035 static void dataDeviceHandleDrop(void* userData,
2036 struct wl_data_device* device)
2037 {
2038 if (!_glfw.wl.dragOffer)
2039 return;
2040
2041 char* string = readDataOfferAsString(_glfw.wl.dragOffer, "text/uri-list");
2042 if (string)
2043 {
2044 int count;
2045 char** paths = _glfwParseUriList(string, &count);
2046 if (paths)
2047 {
2048 _glfwInputDrop(_glfw.wl.dragFocus, count, (const char**) paths);
2049
2050 for (int i = 0; i < count; i++)
2051 _glfw_free(paths[i]);
2052
2053 _glfw_free(paths);
2054 }
2055
2056 _glfw_free(string);
2057 }
2058 }
2059
dataDeviceHandleSelection(void * userData,struct wl_data_device * device,struct wl_data_offer * offer)2060 static void dataDeviceHandleSelection(void* userData,
2061 struct wl_data_device* device,
2062 struct wl_data_offer* offer)
2063 {
2064 if (_glfw.wl.selectionOffer)
2065 {
2066 wl_data_offer_destroy(_glfw.wl.selectionOffer);
2067 _glfw.wl.selectionOffer = NULL;
2068 }
2069
2070 for (unsigned int i = 0; i < _glfw.wl.offerCount; i++)
2071 {
2072 if (_glfw.wl.offers[i].offer == offer)
2073 {
2074 if (_glfw.wl.offers[i].text_plain_utf8)
2075 _glfw.wl.selectionOffer = offer;
2076 else
2077 wl_data_offer_destroy(offer);
2078
2079 _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1];
2080 _glfw.wl.offerCount--;
2081 break;
2082 }
2083 }
2084 }
2085
2086 const struct wl_data_device_listener dataDeviceListener =
2087 {
2088 dataDeviceHandleDataOffer,
2089 dataDeviceHandleEnter,
2090 dataDeviceHandleLeave,
2091 dataDeviceHandleMotion,
2092 dataDeviceHandleDrop,
2093 dataDeviceHandleSelection,
2094 };
2095
xdgActivationHandleDone(void * userData,struct xdg_activation_token_v1 * activationToken,const char * token)2096 static void xdgActivationHandleDone(void* userData,
2097 struct xdg_activation_token_v1* activationToken,
2098 const char* token)
2099 {
2100 _GLFWwindow* window = userData;
2101
2102 if (activationToken != window->wl.activationToken)
2103 return;
2104
2105 xdg_activation_v1_activate(_glfw.wl.activationManager, token, window->wl.surface);
2106 xdg_activation_token_v1_destroy(window->wl.activationToken);
2107 window->wl.activationToken = NULL;
2108 }
2109
2110 static const struct xdg_activation_token_v1_listener xdgActivationListener =
2111 {
2112 xdgActivationHandleDone
2113 };
2114
_glfwAddSeatListenerWayland(struct wl_seat * seat)2115 void _glfwAddSeatListenerWayland(struct wl_seat* seat)
2116 {
2117 wl_seat_add_listener(seat, &seatListener, NULL);
2118 }
2119
_glfwAddDataDeviceListenerWayland(struct wl_data_device * device)2120 void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device)
2121 {
2122 wl_data_device_add_listener(device, &dataDeviceListener, NULL);
2123 }
2124
2125
2126 //////////////////////////////////////////////////////////////////////////
2127 ////// GLFW platform API //////
2128 //////////////////////////////////////////////////////////////////////////
2129
_glfwCreateWindowWayland(_GLFWwindow * window,const _GLFWwndconfig * wndconfig,const _GLFWctxconfig * ctxconfig,const _GLFWfbconfig * fbconfig)2130 GLFWbool _glfwCreateWindowWayland(_GLFWwindow* window,
2131 const _GLFWwndconfig* wndconfig,
2132 const _GLFWctxconfig* ctxconfig,
2133 const _GLFWfbconfig* fbconfig)
2134 {
2135 if (!createNativeSurface(window, wndconfig, fbconfig))
2136 return GLFW_FALSE;
2137
2138 if (ctxconfig->client != GLFW_NO_API)
2139 {
2140 if (ctxconfig->source == GLFW_EGL_CONTEXT_API ||
2141 ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
2142 {
2143 window->wl.egl.window = wl_egl_window_create(window->wl.surface,
2144 window->wl.fbWidth,
2145 window->wl.fbHeight);
2146 if (!window->wl.egl.window)
2147 {
2148 _glfwInputError(GLFW_PLATFORM_ERROR,
2149 "Wayland: Failed to create EGL window");
2150 return GLFW_FALSE;
2151 }
2152
2153 if (!_glfwInitEGL())
2154 return GLFW_FALSE;
2155 if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
2156 return GLFW_FALSE;
2157 }
2158 else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
2159 {
2160 if (!_glfwInitOSMesa())
2161 return GLFW_FALSE;
2162 if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
2163 return GLFW_FALSE;
2164 }
2165
2166 if (!_glfwRefreshContextAttribs(window, ctxconfig))
2167 return GLFW_FALSE;
2168 }
2169
2170 if (wndconfig->mousePassthrough)
2171 _glfwSetWindowMousePassthroughWayland(window, GLFW_TRUE);
2172
2173 if (window->monitor || wndconfig->visible)
2174 {
2175 if (!createShellObjects(window))
2176 return GLFW_FALSE;
2177 }
2178
2179 return GLFW_TRUE;
2180 }
2181
_glfwDestroyWindowWayland(_GLFWwindow * window)2182 void _glfwDestroyWindowWayland(_GLFWwindow* window)
2183 {
2184 if (window == _glfw.wl.pointerFocus)
2185 _glfw.wl.pointerFocus = NULL;
2186
2187 if (window == _glfw.wl.keyboardFocus)
2188 _glfw.wl.keyboardFocus = NULL;
2189
2190 if (window->wl.fractionalScale)
2191 wp_fractional_scale_v1_destroy(window->wl.fractionalScale);
2192
2193 if (window->wl.scalingViewport)
2194 wp_viewport_destroy(window->wl.scalingViewport);
2195
2196 if (window->wl.activationToken)
2197 xdg_activation_token_v1_destroy(window->wl.activationToken);
2198
2199 if (window->wl.idleInhibitor)
2200 zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor);
2201
2202 if (window->wl.relativePointer)
2203 zwp_relative_pointer_v1_destroy(window->wl.relativePointer);
2204
2205 if (window->wl.lockedPointer)
2206 zwp_locked_pointer_v1_destroy(window->wl.lockedPointer);
2207
2208 if (window->wl.confinedPointer)
2209 zwp_confined_pointer_v1_destroy(window->wl.confinedPointer);
2210
2211 if (window->context.destroy)
2212 window->context.destroy(window);
2213
2214 destroyShellObjects(window);
2215
2216 if (window->wl.fallback.buffer)
2217 wl_buffer_destroy(window->wl.fallback.buffer);
2218
2219 if (window->wl.egl.window)
2220 wl_egl_window_destroy(window->wl.egl.window);
2221
2222 if (window->wl.surface)
2223 wl_surface_destroy(window->wl.surface);
2224
2225 _glfw_free(window->wl.appId);
2226 _glfw_free(window->wl.outputScales);
2227 }
2228
_glfwSetWindowTitleWayland(_GLFWwindow * window,const char * title)2229 void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title)
2230 {
2231 if (window->wl.libdecor.frame)
2232 libdecor_frame_set_title(window->wl.libdecor.frame, title);
2233 else if (window->wl.xdg.toplevel)
2234 xdg_toplevel_set_title(window->wl.xdg.toplevel, title);
2235 }
2236
_glfwSetWindowIconWayland(_GLFWwindow * window,int count,const GLFWimage * images)2237 void _glfwSetWindowIconWayland(_GLFWwindow* window,
2238 int count, const GLFWimage* images)
2239 {
2240 _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2241 "Wayland: The platform does not support setting the window icon");
2242 }
2243
_glfwGetWindowPosWayland(_GLFWwindow * window,int * xpos,int * ypos)2244 void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos)
2245 {
2246 // A Wayland client is not aware of its position, so just warn and leave it
2247 // as (0, 0)
2248
2249 _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2250 "Wayland: The platform does not provide the window position");
2251 }
2252
_glfwSetWindowPosWayland(_GLFWwindow * window,int xpos,int ypos)2253 void _glfwSetWindowPosWayland(_GLFWwindow* window, int xpos, int ypos)
2254 {
2255 // A Wayland client can not set its position, so just warn
2256
2257 _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2258 "Wayland: The platform does not support setting the window position");
2259 }
2260
_glfwGetWindowSizeWayland(_GLFWwindow * window,int * width,int * height)2261 void _glfwGetWindowSizeWayland(_GLFWwindow* window, int* width, int* height)
2262 {
2263 if (width)
2264 *width = window->wl.width;
2265 if (height)
2266 *height = window->wl.height;
2267 }
2268
_glfwSetWindowSizeWayland(_GLFWwindow * window,int width,int height)2269 void _glfwSetWindowSizeWayland(_GLFWwindow* window, int width, int height)
2270 {
2271 if (window->monitor)
2272 {
2273 // Video mode setting is not available on Wayland
2274 }
2275 else
2276 {
2277 if (!resizeWindow(window, width, height))
2278 return;
2279
2280 if (window->wl.libdecor.frame)
2281 {
2282 struct libdecor_state* frameState =
2283 libdecor_state_new(window->wl.width, window->wl.height);
2284 libdecor_frame_commit(window->wl.libdecor.frame, frameState, NULL);
2285 libdecor_state_free(frameState);
2286 }
2287
2288 if (window->wl.visible)
2289 _glfwInputWindowDamage(window);
2290 }
2291 }
2292
_glfwSetWindowSizeLimitsWayland(_GLFWwindow * window,int minwidth,int minheight,int maxwidth,int maxheight)2293 void _glfwSetWindowSizeLimitsWayland(_GLFWwindow* window,
2294 int minwidth, int minheight,
2295 int maxwidth, int maxheight)
2296 {
2297 if (window->wl.libdecor.frame)
2298 {
2299 if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE)
2300 minwidth = minheight = 0;
2301
2302 if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)
2303 maxwidth = maxheight = 0;
2304
2305 libdecor_frame_set_min_content_size(window->wl.libdecor.frame,
2306 minwidth, minheight);
2307 libdecor_frame_set_max_content_size(window->wl.libdecor.frame,
2308 maxwidth, maxheight);
2309 }
2310 else if (window->wl.xdg.toplevel)
2311 updateXdgSizeLimits(window);
2312 }
2313
_glfwSetWindowAspectRatioWayland(_GLFWwindow * window,int numer,int denom)2314 void _glfwSetWindowAspectRatioWayland(_GLFWwindow* window, int numer, int denom)
2315 {
2316 if (window->wl.maximized || window->wl.fullscreen)
2317 return;
2318
2319 int width = window->wl.width, height = window->wl.height;
2320
2321 if (numer != GLFW_DONT_CARE && denom != GLFW_DONT_CARE)
2322 {
2323 const float aspectRatio = (float) width / (float) height;
2324 const float targetRatio = (float) numer / (float) denom;
2325 if (aspectRatio < targetRatio)
2326 height /= targetRatio;
2327 else if (aspectRatio > targetRatio)
2328 width *= targetRatio;
2329 }
2330
2331 if (resizeWindow(window, width, height))
2332 {
2333 if (window->wl.libdecor.frame)
2334 {
2335 struct libdecor_state* frameState =
2336 libdecor_state_new(window->wl.width, window->wl.height);
2337 libdecor_frame_commit(window->wl.libdecor.frame, frameState, NULL);
2338 libdecor_state_free(frameState);
2339 }
2340
2341 _glfwInputWindowSize(window, window->wl.width, window->wl.height);
2342
2343 if (window->wl.visible)
2344 _glfwInputWindowDamage(window);
2345 }
2346 }
2347
_glfwGetFramebufferSizeWayland(_GLFWwindow * window,int * width,int * height)2348 void _glfwGetFramebufferSizeWayland(_GLFWwindow* window, int* width, int* height)
2349 {
2350 if (width)
2351 *width = window->wl.fbWidth;
2352 if (height)
2353 *height = window->wl.fbHeight;
2354 }
2355
_glfwGetWindowFrameSizeWayland(_GLFWwindow * window,int * left,int * top,int * right,int * bottom)2356 void _glfwGetWindowFrameSizeWayland(_GLFWwindow* window,
2357 int* left, int* top,
2358 int* right, int* bottom)
2359 {
2360 if (window->wl.fallback.decorations)
2361 {
2362 if (top)
2363 *top = GLFW_CAPTION_HEIGHT;
2364 if (left)
2365 *left = GLFW_BORDER_SIZE;
2366 if (right)
2367 *right = GLFW_BORDER_SIZE;
2368 if (bottom)
2369 *bottom = GLFW_BORDER_SIZE;
2370 }
2371 }
2372
_glfwGetWindowContentScaleWayland(_GLFWwindow * window,float * xscale,float * yscale)2373 void _glfwGetWindowContentScaleWayland(_GLFWwindow* window,
2374 float* xscale, float* yscale)
2375 {
2376 if (window->wl.fractionalScale)
2377 {
2378 if (xscale)
2379 *xscale = (float) window->wl.scalingNumerator / 120.f;
2380 if (yscale)
2381 *yscale = (float) window->wl.scalingNumerator / 120.f;
2382 }
2383 else
2384 {
2385 if (xscale)
2386 *xscale = (float) window->wl.bufferScale;
2387 if (yscale)
2388 *yscale = (float) window->wl.bufferScale;
2389 }
2390 }
2391
_glfwIconifyWindowWayland(_GLFWwindow * window)2392 void _glfwIconifyWindowWayland(_GLFWwindow* window)
2393 {
2394 if (window->wl.libdecor.frame)
2395 libdecor_frame_set_minimized(window->wl.libdecor.frame);
2396 else if (window->wl.xdg.toplevel)
2397 xdg_toplevel_set_minimized(window->wl.xdg.toplevel);
2398 }
2399
_glfwRestoreWindowWayland(_GLFWwindow * window)2400 void _glfwRestoreWindowWayland(_GLFWwindow* window)
2401 {
2402 if (window->monitor)
2403 {
2404 // There is no way to unset minimized, or even to know if we are
2405 // minimized, so there is nothing to do in this case.
2406 }
2407 else
2408 {
2409 // We assume we are not minimized and act only on maximization
2410
2411 if (window->wl.maximized)
2412 {
2413 if (window->wl.libdecor.frame)
2414 libdecor_frame_unset_maximized(window->wl.libdecor.frame);
2415 else if (window->wl.xdg.toplevel)
2416 xdg_toplevel_unset_maximized(window->wl.xdg.toplevel);
2417 else
2418 window->wl.maximized = GLFW_FALSE;
2419 }
2420 }
2421 }
2422
_glfwMaximizeWindowWayland(_GLFWwindow * window)2423 void _glfwMaximizeWindowWayland(_GLFWwindow* window)
2424 {
2425 if (window->wl.libdecor.frame)
2426 libdecor_frame_set_maximized(window->wl.libdecor.frame);
2427 else if (window->wl.xdg.toplevel)
2428 xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
2429 else
2430 window->wl.maximized = GLFW_TRUE;
2431 }
2432
_glfwShowWindowWayland(_GLFWwindow * window)2433 void _glfwShowWindowWayland(_GLFWwindow* window)
2434 {
2435 if (!window->wl.libdecor.frame && !window->wl.xdg.toplevel)
2436 {
2437 // NOTE: The XDG surface and role are created here so command-line applications
2438 // with off-screen windows do not appear in for example the Unity dock
2439 createShellObjects(window);
2440 }
2441 }
2442
_glfwHideWindowWayland(_GLFWwindow * window)2443 void _glfwHideWindowWayland(_GLFWwindow* window)
2444 {
2445 if (window->wl.visible)
2446 {
2447 window->wl.visible = GLFW_FALSE;
2448 destroyShellObjects(window);
2449
2450 wl_surface_attach(window->wl.surface, NULL, 0, 0);
2451 wl_surface_commit(window->wl.surface);
2452 }
2453 }
2454
_glfwRequestWindowAttentionWayland(_GLFWwindow * window)2455 void _glfwRequestWindowAttentionWayland(_GLFWwindow* window)
2456 {
2457 if (!_glfw.wl.activationManager)
2458 return;
2459
2460 // We're about to overwrite this with a new request
2461 if (window->wl.activationToken)
2462 xdg_activation_token_v1_destroy(window->wl.activationToken);
2463
2464 window->wl.activationToken =
2465 xdg_activation_v1_get_activation_token(_glfw.wl.activationManager);
2466 xdg_activation_token_v1_add_listener(window->wl.activationToken,
2467 &xdgActivationListener,
2468 window);
2469
2470 xdg_activation_token_v1_commit(window->wl.activationToken);
2471 }
2472
_glfwFocusWindowWayland(_GLFWwindow * window)2473 void _glfwFocusWindowWayland(_GLFWwindow* window)
2474 {
2475 if (!_glfw.wl.activationManager)
2476 return;
2477
2478 if (window->wl.activationToken)
2479 xdg_activation_token_v1_destroy(window->wl.activationToken);
2480
2481 window->wl.activationToken =
2482 xdg_activation_v1_get_activation_token(_glfw.wl.activationManager);
2483 xdg_activation_token_v1_add_listener(window->wl.activationToken,
2484 &xdgActivationListener,
2485 window);
2486
2487 xdg_activation_token_v1_set_serial(window->wl.activationToken,
2488 _glfw.wl.serial,
2489 _glfw.wl.seat);
2490
2491 _GLFWwindow* requester = _glfw.wl.keyboardFocus;
2492 if (requester)
2493 {
2494 xdg_activation_token_v1_set_surface(window->wl.activationToken,
2495 requester->wl.surface);
2496
2497 if (requester->wl.appId)
2498 {
2499 xdg_activation_token_v1_set_app_id(window->wl.activationToken,
2500 requester->wl.appId);
2501 }
2502 }
2503
2504 xdg_activation_token_v1_commit(window->wl.activationToken);
2505 }
2506
_glfwSetWindowMonitorWayland(_GLFWwindow * window,_GLFWmonitor * monitor,int xpos,int ypos,int width,int height,int refreshRate)2507 void _glfwSetWindowMonitorWayland(_GLFWwindow* window,
2508 _GLFWmonitor* monitor,
2509 int xpos, int ypos,
2510 int width, int height,
2511 int refreshRate)
2512 {
2513 if (window->monitor == monitor)
2514 {
2515 if (!monitor)
2516 _glfwSetWindowSizeWayland(window, width, height);
2517
2518 return;
2519 }
2520
2521 if (window->monitor)
2522 releaseMonitor(window);
2523
2524 _glfwInputWindowMonitor(window, monitor);
2525
2526 if (window->monitor)
2527 acquireMonitor(window);
2528 else
2529 _glfwSetWindowSizeWayland(window, width, height);
2530 }
2531
_glfwWindowFocusedWayland(_GLFWwindow * window)2532 GLFWbool _glfwWindowFocusedWayland(_GLFWwindow* window)
2533 {
2534 return _glfw.wl.keyboardFocus == window;
2535 }
2536
_glfwWindowIconifiedWayland(_GLFWwindow * window)2537 GLFWbool _glfwWindowIconifiedWayland(_GLFWwindow* window)
2538 {
2539 // xdg-shell doesn’t give any way to request whether a surface is
2540 // iconified.
2541 return GLFW_FALSE;
2542 }
2543
_glfwWindowVisibleWayland(_GLFWwindow * window)2544 GLFWbool _glfwWindowVisibleWayland(_GLFWwindow* window)
2545 {
2546 return window->wl.visible;
2547 }
2548
_glfwWindowMaximizedWayland(_GLFWwindow * window)2549 GLFWbool _glfwWindowMaximizedWayland(_GLFWwindow* window)
2550 {
2551 return window->wl.maximized;
2552 }
2553
_glfwWindowHoveredWayland(_GLFWwindow * window)2554 GLFWbool _glfwWindowHoveredWayland(_GLFWwindow* window)
2555 {
2556 return window->wl.hovered;
2557 }
2558
_glfwFramebufferTransparentWayland(_GLFWwindow * window)2559 GLFWbool _glfwFramebufferTransparentWayland(_GLFWwindow* window)
2560 {
2561 return window->wl.transparent;
2562 }
2563
_glfwSetWindowResizableWayland(_GLFWwindow * window,GLFWbool enabled)2564 void _glfwSetWindowResizableWayland(_GLFWwindow* window, GLFWbool enabled)
2565 {
2566 if (window->wl.libdecor.frame)
2567 {
2568 if (enabled)
2569 {
2570 libdecor_frame_set_capabilities(window->wl.libdecor.frame,
2571 LIBDECOR_ACTION_RESIZE);
2572 }
2573 else
2574 {
2575 libdecor_frame_unset_capabilities(window->wl.libdecor.frame,
2576 LIBDECOR_ACTION_RESIZE);
2577 }
2578 }
2579 else if (window->wl.xdg.toplevel)
2580 updateXdgSizeLimits(window);
2581 }
2582
_glfwSetWindowDecoratedWayland(_GLFWwindow * window,GLFWbool enabled)2583 void _glfwSetWindowDecoratedWayland(_GLFWwindow* window, GLFWbool enabled)
2584 {
2585 if (window->wl.libdecor.frame)
2586 {
2587 libdecor_frame_set_visibility(window->wl.libdecor.frame, enabled);
2588 }
2589 else if (window->wl.xdg.decoration)
2590 {
2591 uint32_t mode;
2592
2593 if (enabled)
2594 mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
2595 else
2596 mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
2597
2598 zxdg_toplevel_decoration_v1_set_mode(window->wl.xdg.decoration, mode);
2599 }
2600 else if (window->wl.xdg.toplevel)
2601 {
2602 if (enabled)
2603 createFallbackDecorations(window);
2604 else
2605 destroyFallbackDecorations(window);
2606 }
2607 }
2608
_glfwSetWindowFloatingWayland(_GLFWwindow * window,GLFWbool enabled)2609 void _glfwSetWindowFloatingWayland(_GLFWwindow* window, GLFWbool enabled)
2610 {
2611 _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2612 "Wayland: Platform does not support making a window floating");
2613 }
2614
_glfwSetWindowMousePassthroughWayland(_GLFWwindow * window,GLFWbool enabled)2615 void _glfwSetWindowMousePassthroughWayland(_GLFWwindow* window, GLFWbool enabled)
2616 {
2617 if (enabled)
2618 {
2619 struct wl_region* region = wl_compositor_create_region(_glfw.wl.compositor);
2620 wl_surface_set_input_region(window->wl.surface, region);
2621 wl_region_destroy(region);
2622 }
2623 else
2624 wl_surface_set_input_region(window->wl.surface, NULL);
2625 }
2626
_glfwGetWindowOpacityWayland(_GLFWwindow * window)2627 float _glfwGetWindowOpacityWayland(_GLFWwindow* window)
2628 {
2629 return 1.f;
2630 }
2631
_glfwSetWindowOpacityWayland(_GLFWwindow * window,float opacity)2632 void _glfwSetWindowOpacityWayland(_GLFWwindow* window, float opacity)
2633 {
2634 _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2635 "Wayland: The platform does not support setting the window opacity");
2636 }
2637
_glfwSetRawMouseMotionWayland(_GLFWwindow * window,GLFWbool enabled)2638 void _glfwSetRawMouseMotionWayland(_GLFWwindow* window, GLFWbool enabled)
2639 {
2640 // This is handled in relativePointerHandleRelativeMotion
2641 }
2642
_glfwRawMouseMotionSupportedWayland(void)2643 GLFWbool _glfwRawMouseMotionSupportedWayland(void)
2644 {
2645 return GLFW_TRUE;
2646 }
2647
_glfwPollEventsWayland(void)2648 void _glfwPollEventsWayland(void)
2649 {
2650 double timeout = 0.0;
2651 handleEvents(&timeout);
2652 }
2653
_glfwWaitEventsWayland(void)2654 void _glfwWaitEventsWayland(void)
2655 {
2656 handleEvents(NULL);
2657 }
2658
_glfwWaitEventsTimeoutWayland(double timeout)2659 void _glfwWaitEventsTimeoutWayland(double timeout)
2660 {
2661 handleEvents(&timeout);
2662 }
2663
_glfwPostEmptyEventWayland(void)2664 void _glfwPostEmptyEventWayland(void)
2665 {
2666 wl_display_sync(_glfw.wl.display);
2667 flushDisplay();
2668 }
2669
_glfwGetCursorPosWayland(_GLFWwindow * window,double * xpos,double * ypos)2670 void _glfwGetCursorPosWayland(_GLFWwindow* window, double* xpos, double* ypos)
2671 {
2672 if (xpos)
2673 *xpos = window->wl.cursorPosX;
2674 if (ypos)
2675 *ypos = window->wl.cursorPosY;
2676 }
2677
_glfwSetCursorPosWayland(_GLFWwindow * window,double x,double y)2678 void _glfwSetCursorPosWayland(_GLFWwindow* window, double x, double y)
2679 {
2680 _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2681 "Wayland: The platform does not support setting the cursor position");
2682 }
2683
_glfwSetCursorModeWayland(_GLFWwindow * window,int mode)2684 void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode)
2685 {
2686 _glfwSetCursorWayland(window, window->wl.currentCursor);
2687 }
2688
_glfwGetScancodeNameWayland(int scancode)2689 const char* _glfwGetScancodeNameWayland(int scancode)
2690 {
2691 if (scancode < 0 || scancode > 255)
2692 {
2693 _glfwInputError(GLFW_INVALID_VALUE,
2694 "Wayland: Invalid scancode %i",
2695 scancode);
2696 return NULL;
2697 }
2698
2699 const int key = _glfw.wl.keycodes[scancode];
2700 if (key == GLFW_KEY_UNKNOWN)
2701 return NULL;
2702
2703 const xkb_keycode_t keycode = scancode + 8;
2704 const xkb_layout_index_t layout =
2705 xkb_state_key_get_layout(_glfw.wl.xkb.state, keycode);
2706 if (layout == XKB_LAYOUT_INVALID)
2707 {
2708 _glfwInputError(GLFW_PLATFORM_ERROR,
2709 "Wayland: Failed to retrieve layout for key name");
2710 return NULL;
2711 }
2712
2713 const xkb_keysym_t* keysyms = NULL;
2714 xkb_keymap_key_get_syms_by_level(_glfw.wl.xkb.keymap,
2715 keycode,
2716 layout,
2717 0,
2718 &keysyms);
2719 if (keysyms == NULL)
2720 {
2721 _glfwInputError(GLFW_PLATFORM_ERROR,
2722 "Wayland: Failed to retrieve keysym for key name");
2723 return NULL;
2724 }
2725
2726 const uint32_t codepoint = _glfwKeySym2Unicode(keysyms[0]);
2727 if (codepoint == GLFW_INVALID_CODEPOINT)
2728 {
2729 _glfwInputError(GLFW_PLATFORM_ERROR,
2730 "Wayland: Failed to retrieve codepoint for key name");
2731 return NULL;
2732 }
2733
2734 const size_t count = _glfwEncodeUTF8(_glfw.wl.keynames[key], codepoint);
2735 if (count == 0)
2736 {
2737 _glfwInputError(GLFW_PLATFORM_ERROR,
2738 "Wayland: Failed to encode codepoint for key name");
2739 return NULL;
2740 }
2741
2742 _glfw.wl.keynames[key][count] = '\0';
2743 return _glfw.wl.keynames[key];
2744 }
2745
_glfwGetKeyScancodeWayland(int key)2746 int _glfwGetKeyScancodeWayland(int key)
2747 {
2748 return _glfw.wl.scancodes[key];
2749 }
2750
_glfwCreateCursorWayland(_GLFWcursor * cursor,const GLFWimage * image,int xhot,int yhot)2751 GLFWbool _glfwCreateCursorWayland(_GLFWcursor* cursor,
2752 const GLFWimage* image,
2753 int xhot, int yhot)
2754 {
2755 cursor->wl.buffer = createShmBuffer(image);
2756 if (!cursor->wl.buffer)
2757 return GLFW_FALSE;
2758
2759 cursor->wl.width = image->width;
2760 cursor->wl.height = image->height;
2761 cursor->wl.xhot = xhot;
2762 cursor->wl.yhot = yhot;
2763 return GLFW_TRUE;
2764 }
2765
_glfwCreateStandardCursorWayland(_GLFWcursor * cursor,int shape)2766 GLFWbool _glfwCreateStandardCursorWayland(_GLFWcursor* cursor, int shape)
2767 {
2768 const char* name = NULL;
2769
2770 // Try the XDG names first
2771 switch (shape)
2772 {
2773 case GLFW_ARROW_CURSOR:
2774 name = "default";
2775 break;
2776 case GLFW_IBEAM_CURSOR:
2777 name = "text";
2778 break;
2779 case GLFW_CROSSHAIR_CURSOR:
2780 name = "crosshair";
2781 break;
2782 case GLFW_POINTING_HAND_CURSOR:
2783 name = "pointer";
2784 break;
2785 case GLFW_RESIZE_EW_CURSOR:
2786 name = "ew-resize";
2787 break;
2788 case GLFW_RESIZE_NS_CURSOR:
2789 name = "ns-resize";
2790 break;
2791 case GLFW_RESIZE_NWSE_CURSOR:
2792 name = "nwse-resize";
2793 break;
2794 case GLFW_RESIZE_NESW_CURSOR:
2795 name = "nesw-resize";
2796 break;
2797 case GLFW_RESIZE_ALL_CURSOR:
2798 name = "all-scroll";
2799 break;
2800 case GLFW_NOT_ALLOWED_CURSOR:
2801 name = "not-allowed";
2802 break;
2803 }
2804
2805 cursor->wl.cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name);
2806
2807 if (_glfw.wl.cursorThemeHiDPI)
2808 {
2809 cursor->wl.cursorHiDPI =
2810 wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, name);
2811 }
2812
2813 if (!cursor->wl.cursor)
2814 {
2815 // Fall back to the core X11 names
2816 switch (shape)
2817 {
2818 case GLFW_ARROW_CURSOR:
2819 name = "left_ptr";
2820 break;
2821 case GLFW_IBEAM_CURSOR:
2822 name = "xterm";
2823 break;
2824 case GLFW_CROSSHAIR_CURSOR:
2825 name = "crosshair";
2826 break;
2827 case GLFW_POINTING_HAND_CURSOR:
2828 name = "hand2";
2829 break;
2830 case GLFW_RESIZE_EW_CURSOR:
2831 name = "sb_h_double_arrow";
2832 break;
2833 case GLFW_RESIZE_NS_CURSOR:
2834 name = "sb_v_double_arrow";
2835 break;
2836 case GLFW_RESIZE_ALL_CURSOR:
2837 name = "fleur";
2838 break;
2839 default:
2840 _glfwInputError(GLFW_CURSOR_UNAVAILABLE,
2841 "Wayland: Standard cursor shape unavailable");
2842 return GLFW_FALSE;
2843 }
2844
2845 cursor->wl.cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name);
2846 if (!cursor->wl.cursor)
2847 {
2848 _glfwInputError(GLFW_CURSOR_UNAVAILABLE,
2849 "Wayland: Failed to create standard cursor \"%s\"",
2850 name);
2851 return GLFW_FALSE;
2852 }
2853
2854 if (_glfw.wl.cursorThemeHiDPI)
2855 {
2856 if (!cursor->wl.cursorHiDPI)
2857 {
2858 cursor->wl.cursorHiDPI =
2859 wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, name);
2860 }
2861 }
2862 }
2863
2864 return GLFW_TRUE;
2865 }
2866
_glfwDestroyCursorWayland(_GLFWcursor * cursor)2867 void _glfwDestroyCursorWayland(_GLFWcursor* cursor)
2868 {
2869 // If it's a standard cursor we don't need to do anything here
2870 if (cursor->wl.cursor)
2871 return;
2872
2873 if (cursor->wl.buffer)
2874 wl_buffer_destroy(cursor->wl.buffer);
2875 }
2876
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)2877 static void relativePointerHandleRelativeMotion(void* userData,
2878 struct zwp_relative_pointer_v1* pointer,
2879 uint32_t timeHi,
2880 uint32_t timeLo,
2881 wl_fixed_t dx,
2882 wl_fixed_t dy,
2883 wl_fixed_t dxUnaccel,
2884 wl_fixed_t dyUnaccel)
2885 {
2886 _GLFWwindow* window = userData;
2887 double xpos = window->virtualCursorPosX;
2888 double ypos = window->virtualCursorPosY;
2889
2890 if (window->cursorMode != GLFW_CURSOR_DISABLED)
2891 return;
2892
2893 if (window->rawMouseMotion)
2894 {
2895 xpos += wl_fixed_to_double(dxUnaccel);
2896 ypos += wl_fixed_to_double(dyUnaccel);
2897 }
2898 else
2899 {
2900 xpos += wl_fixed_to_double(dx);
2901 ypos += wl_fixed_to_double(dy);
2902 }
2903
2904 _glfwInputCursorPos(window, xpos, ypos);
2905 }
2906
2907 static const struct zwp_relative_pointer_v1_listener relativePointerListener =
2908 {
2909 relativePointerHandleRelativeMotion
2910 };
2911
lockedPointerHandleLocked(void * userData,struct zwp_locked_pointer_v1 * lockedPointer)2912 static void lockedPointerHandleLocked(void* userData,
2913 struct zwp_locked_pointer_v1* lockedPointer)
2914 {
2915 }
2916
lockedPointerHandleUnlocked(void * userData,struct zwp_locked_pointer_v1 * lockedPointer)2917 static void lockedPointerHandleUnlocked(void* userData,
2918 struct zwp_locked_pointer_v1* lockedPointer)
2919 {
2920 }
2921
2922 static const struct zwp_locked_pointer_v1_listener lockedPointerListener =
2923 {
2924 lockedPointerHandleLocked,
2925 lockedPointerHandleUnlocked
2926 };
2927
lockPointer(_GLFWwindow * window)2928 static void lockPointer(_GLFWwindow* window)
2929 {
2930 if (!_glfw.wl.relativePointerManager)
2931 {
2932 _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
2933 "Wayland: The compositor does not support pointer locking");
2934 return;
2935 }
2936
2937 window->wl.relativePointer =
2938 zwp_relative_pointer_manager_v1_get_relative_pointer(
2939 _glfw.wl.relativePointerManager,
2940 _glfw.wl.pointer);
2941 zwp_relative_pointer_v1_add_listener(window->wl.relativePointer,
2942 &relativePointerListener,
2943 window);
2944
2945 window->wl.lockedPointer =
2946 zwp_pointer_constraints_v1_lock_pointer(
2947 _glfw.wl.pointerConstraints,
2948 window->wl.surface,
2949 _glfw.wl.pointer,
2950 NULL,
2951 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
2952 zwp_locked_pointer_v1_add_listener(window->wl.lockedPointer,
2953 &lockedPointerListener,
2954 window);
2955 }
2956
unlockPointer(_GLFWwindow * window)2957 static void unlockPointer(_GLFWwindow* window)
2958 {
2959 zwp_relative_pointer_v1_destroy(window->wl.relativePointer);
2960 window->wl.relativePointer = NULL;
2961
2962 zwp_locked_pointer_v1_destroy(window->wl.lockedPointer);
2963 window->wl.lockedPointer = NULL;
2964 }
2965
confinedPointerHandleConfined(void * userData,struct zwp_confined_pointer_v1 * confinedPointer)2966 static void confinedPointerHandleConfined(void* userData,
2967 struct zwp_confined_pointer_v1* confinedPointer)
2968 {
2969 }
2970
confinedPointerHandleUnconfined(void * userData,struct zwp_confined_pointer_v1 * confinedPointer)2971 static void confinedPointerHandleUnconfined(void* userData,
2972 struct zwp_confined_pointer_v1* confinedPointer)
2973 {
2974 }
2975
2976 static const struct zwp_confined_pointer_v1_listener confinedPointerListener =
2977 {
2978 confinedPointerHandleConfined,
2979 confinedPointerHandleUnconfined
2980 };
2981
confinePointer(_GLFWwindow * window)2982 static void confinePointer(_GLFWwindow* window)
2983 {
2984 window->wl.confinedPointer =
2985 zwp_pointer_constraints_v1_confine_pointer(
2986 _glfw.wl.pointerConstraints,
2987 window->wl.surface,
2988 _glfw.wl.pointer,
2989 NULL,
2990 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
2991
2992 zwp_confined_pointer_v1_add_listener(window->wl.confinedPointer,
2993 &confinedPointerListener,
2994 window);
2995 }
2996
unconfinePointer(_GLFWwindow * window)2997 static void unconfinePointer(_GLFWwindow* window)
2998 {
2999 zwp_confined_pointer_v1_destroy(window->wl.confinedPointer);
3000 window->wl.confinedPointer = NULL;
3001 }
3002
_glfwSetCursorWayland(_GLFWwindow * window,_GLFWcursor * cursor)3003 void _glfwSetCursorWayland(_GLFWwindow* window, _GLFWcursor* cursor)
3004 {
3005 if (!_glfw.wl.pointer)
3006 return;
3007
3008 window->wl.currentCursor = cursor;
3009
3010 // If we're not in the correct window just save the cursor
3011 // the next time the pointer enters the window the cursor will change
3012 if (!window->wl.hovered)
3013 return;
3014
3015 // Update pointer lock to match cursor mode
3016 if (window->cursorMode == GLFW_CURSOR_DISABLED)
3017 {
3018 if (window->wl.confinedPointer)
3019 unconfinePointer(window);
3020 if (!window->wl.lockedPointer)
3021 lockPointer(window);
3022 }
3023 else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
3024 {
3025 if (window->wl.lockedPointer)
3026 unlockPointer(window);
3027 if (!window->wl.confinedPointer)
3028 confinePointer(window);
3029 }
3030 else if (window->cursorMode == GLFW_CURSOR_NORMAL ||
3031 window->cursorMode == GLFW_CURSOR_HIDDEN)
3032 {
3033 if (window->wl.lockedPointer)
3034 unlockPointer(window);
3035 else if (window->wl.confinedPointer)
3036 unconfinePointer(window);
3037 }
3038
3039 if (window->cursorMode == GLFW_CURSOR_NORMAL ||
3040 window->cursorMode == GLFW_CURSOR_CAPTURED)
3041 {
3042 if (cursor)
3043 setCursorImage(window, &cursor->wl);
3044 else
3045 {
3046 struct wl_cursor* defaultCursor =
3047 wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, "left_ptr");
3048 if (!defaultCursor)
3049 {
3050 _glfwInputError(GLFW_PLATFORM_ERROR,
3051 "Wayland: Standard cursor not found");
3052 return;
3053 }
3054
3055 struct wl_cursor* defaultCursorHiDPI = NULL;
3056 if (_glfw.wl.cursorThemeHiDPI)
3057 {
3058 defaultCursorHiDPI =
3059 wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, "left_ptr");
3060 }
3061
3062 _GLFWcursorWayland cursorWayland =
3063 {
3064 defaultCursor,
3065 defaultCursorHiDPI,
3066 NULL,
3067 0, 0,
3068 0, 0,
3069 0
3070 };
3071
3072 setCursorImage(window, &cursorWayland);
3073 }
3074 }
3075 else if (window->cursorMode == GLFW_CURSOR_HIDDEN ||
3076 window->cursorMode == GLFW_CURSOR_DISABLED)
3077 {
3078 wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial, NULL, 0, 0);
3079 }
3080 }
3081
dataSourceHandleTarget(void * userData,struct wl_data_source * source,const char * mimeType)3082 static void dataSourceHandleTarget(void* userData,
3083 struct wl_data_source* source,
3084 const char* mimeType)
3085 {
3086 if (_glfw.wl.selectionSource != source)
3087 {
3088 _glfwInputError(GLFW_PLATFORM_ERROR,
3089 "Wayland: Unknown clipboard data source");
3090 return;
3091 }
3092 }
3093
dataSourceHandleSend(void * userData,struct wl_data_source * source,const char * mimeType,int fd)3094 static void dataSourceHandleSend(void* userData,
3095 struct wl_data_source* source,
3096 const char* mimeType,
3097 int fd)
3098 {
3099 // Ignore it if this is an outdated or invalid request
3100 if (_glfw.wl.selectionSource != source ||
3101 strcmp(mimeType, "text/plain;charset=utf-8") != 0)
3102 {
3103 close(fd);
3104 return;
3105 }
3106
3107 char* string = _glfw.wl.clipboardString;
3108 size_t length = strlen(string);
3109
3110 while (length > 0)
3111 {
3112 const ssize_t result = write(fd, string, length);
3113 if (result == -1)
3114 {
3115 if (errno == EINTR)
3116 continue;
3117
3118 _glfwInputError(GLFW_PLATFORM_ERROR,
3119 "Wayland: Error while writing the clipboard: %s",
3120 strerror(errno));
3121 break;
3122 }
3123
3124 length -= result;
3125 string += result;
3126 }
3127
3128 close(fd);
3129 }
3130
dataSourceHandleCancelled(void * userData,struct wl_data_source * source)3131 static void dataSourceHandleCancelled(void* userData,
3132 struct wl_data_source* source)
3133 {
3134 wl_data_source_destroy(source);
3135
3136 if (_glfw.wl.selectionSource != source)
3137 return;
3138
3139 _glfw.wl.selectionSource = NULL;
3140 }
3141
3142 static const struct wl_data_source_listener dataSourceListener =
3143 {
3144 dataSourceHandleTarget,
3145 dataSourceHandleSend,
3146 dataSourceHandleCancelled,
3147 };
3148
_glfwSetClipboardStringWayland(const char * string)3149 void _glfwSetClipboardStringWayland(const char* string)
3150 {
3151 if (_glfw.wl.selectionSource)
3152 {
3153 wl_data_source_destroy(_glfw.wl.selectionSource);
3154 _glfw.wl.selectionSource = NULL;
3155 }
3156
3157 char* copy = _glfw_strdup(string);
3158 if (!copy)
3159 {
3160 _glfwInputError(GLFW_OUT_OF_MEMORY, NULL);
3161 return;
3162 }
3163
3164 _glfw_free(_glfw.wl.clipboardString);
3165 _glfw.wl.clipboardString = copy;
3166
3167 _glfw.wl.selectionSource =
3168 wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager);
3169 if (!_glfw.wl.selectionSource)
3170 {
3171 _glfwInputError(GLFW_PLATFORM_ERROR,
3172 "Wayland: Failed to create clipboard data source");
3173 return;
3174 }
3175 wl_data_source_add_listener(_glfw.wl.selectionSource,
3176 &dataSourceListener,
3177 NULL);
3178 wl_data_source_offer(_glfw.wl.selectionSource, "text/plain;charset=utf-8");
3179 wl_data_device_set_selection(_glfw.wl.dataDevice,
3180 _glfw.wl.selectionSource,
3181 _glfw.wl.serial);
3182 }
3183
_glfwGetClipboardStringWayland(void)3184 const char* _glfwGetClipboardStringWayland(void)
3185 {
3186 if (!_glfw.wl.selectionOffer)
3187 {
3188 _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
3189 "Wayland: No clipboard data available");
3190 return NULL;
3191 }
3192
3193 if (_glfw.wl.selectionSource)
3194 return _glfw.wl.clipboardString;
3195
3196 _glfw_free(_glfw.wl.clipboardString);
3197 _glfw.wl.clipboardString =
3198 readDataOfferAsString(_glfw.wl.selectionOffer, "text/plain;charset=utf-8");
3199 return _glfw.wl.clipboardString;
3200 }
3201
_glfwGetEGLPlatformWayland(EGLint ** attribs)3202 EGLenum _glfwGetEGLPlatformWayland(EGLint** attribs)
3203 {
3204 if (_glfw.egl.EXT_platform_base && _glfw.egl.EXT_platform_wayland)
3205 return EGL_PLATFORM_WAYLAND_EXT;
3206 else
3207 return 0;
3208 }
3209
_glfwGetEGLNativeDisplayWayland(void)3210 EGLNativeDisplayType _glfwGetEGLNativeDisplayWayland(void)
3211 {
3212 return _glfw.wl.display;
3213 }
3214
_glfwGetEGLNativeWindowWayland(_GLFWwindow * window)3215 EGLNativeWindowType _glfwGetEGLNativeWindowWayland(_GLFWwindow* window)
3216 {
3217 return window->wl.egl.window;
3218 }
3219
_glfwGetRequiredInstanceExtensionsWayland(char ** extensions)3220 void _glfwGetRequiredInstanceExtensionsWayland(char** extensions)
3221 {
3222 if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_wayland_surface)
3223 return;
3224
3225 extensions[0] = "VK_KHR_surface";
3226 extensions[1] = "VK_KHR_wayland_surface";
3227 }
3228
_glfwGetPhysicalDevicePresentationSupportWayland(VkInstance instance,VkPhysicalDevice device,uint32_t queuefamily)3229 GLFWbool _glfwGetPhysicalDevicePresentationSupportWayland(VkInstance instance,
3230 VkPhysicalDevice device,
3231 uint32_t queuefamily)
3232 {
3233 PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
3234 vkGetPhysicalDeviceWaylandPresentationSupportKHR =
3235 (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)
3236 vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR");
3237 if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR)
3238 {
3239 _glfwInputError(GLFW_API_UNAVAILABLE,
3240 "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension");
3241 return VK_NULL_HANDLE;
3242 }
3243
3244 return vkGetPhysicalDeviceWaylandPresentationSupportKHR(device,
3245 queuefamily,
3246 _glfw.wl.display);
3247 }
3248
_glfwCreateWindowSurfaceWayland(VkInstance instance,_GLFWwindow * window,const VkAllocationCallbacks * allocator,VkSurfaceKHR * surface)3249 VkResult _glfwCreateWindowSurfaceWayland(VkInstance instance,
3250 _GLFWwindow* window,
3251 const VkAllocationCallbacks* allocator,
3252 VkSurfaceKHR* surface)
3253 {
3254 VkResult err;
3255 VkWaylandSurfaceCreateInfoKHR sci;
3256 PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR;
3257
3258 vkCreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR)
3259 vkGetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR");
3260 if (!vkCreateWaylandSurfaceKHR)
3261 {
3262 _glfwInputError(GLFW_API_UNAVAILABLE,
3263 "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension");
3264 return VK_ERROR_EXTENSION_NOT_PRESENT;
3265 }
3266
3267 memset(&sci, 0, sizeof(sci));
3268 sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
3269 sci.display = _glfw.wl.display;
3270 sci.surface = window->wl.surface;
3271
3272 err = vkCreateWaylandSurfaceKHR(instance, &sci, allocator, surface);
3273 if (err)
3274 {
3275 _glfwInputError(GLFW_PLATFORM_ERROR,
3276 "Wayland: Failed to create Vulkan surface: %s",
3277 _glfwGetVulkanResultString(err));
3278 }
3279
3280 return err;
3281 }
3282
3283
3284 //////////////////////////////////////////////////////////////////////////
3285 ////// GLFW native API //////
3286 //////////////////////////////////////////////////////////////////////////
3287
glfwGetWaylandDisplay(void)3288 GLFWAPI struct wl_display* glfwGetWaylandDisplay(void)
3289 {
3290 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
3291
3292 if (_glfw.platform.platformID != GLFW_PLATFORM_WAYLAND)
3293 {
3294 _glfwInputError(GLFW_PLATFORM_UNAVAILABLE,
3295 "Wayland: Platform not initialized");
3296 return NULL;
3297 }
3298
3299 return _glfw.wl.display;
3300 }
3301
glfwGetWaylandWindow(GLFWwindow * handle)3302 GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle)
3303 {
3304 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
3305
3306 if (_glfw.platform.platformID != GLFW_PLATFORM_WAYLAND)
3307 {
3308 _glfwInputError(GLFW_PLATFORM_UNAVAILABLE,
3309 "Wayland: Platform not initialized");
3310 return NULL;
3311 }
3312
3313 _GLFWwindow* window = (_GLFWwindow*) handle;
3314 assert(window != NULL);
3315
3316 return window->wl.surface;
3317 }
3318
3319 #endif // _GLFW_WAYLAND
3320
3321