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