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