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