wl_window.c (58560B)
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: %m", 212 length); 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: %m"); 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->surface) 316 wl_surface_destroy(decoration->surface); 317 if (decoration->subsurface) 318 wl_subsurface_destroy(decoration->subsurface); 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 for (i = 0; i < repeats; ++i) 874 _glfwInputKey(_glfw.wl.keyboardFocus, _glfw.wl.keyboardLastKey, 875 _glfw.wl.keyboardLastScancode, GLFW_REPEAT, 876 _glfw.wl.xkb.modifiers); 877 } 878 879 if (fds[2].revents & POLLIN) 880 { 881 read_ret = read(_glfw.wl.cursorTimerfd, &repeats, sizeof(repeats)); 882 if (read_ret != 8) 883 return; 884 885 incrementCursorImage(_glfw.wl.pointerFocus); 886 } 887 } 888 else 889 { 890 wl_display_cancel_read(display); 891 } 892 } 893 894 // Translates a GLFW standard cursor to a theme cursor name 895 // 896 static char *translateCursorShape(int shape) 897 { 898 switch (shape) 899 { 900 case GLFW_ARROW_CURSOR: 901 return "left_ptr"; 902 case GLFW_IBEAM_CURSOR: 903 return "xterm"; 904 case GLFW_CROSSHAIR_CURSOR: 905 return "crosshair"; 906 case GLFW_HAND_CURSOR: 907 return "hand2"; 908 case GLFW_HRESIZE_CURSOR: 909 return "sb_h_double_arrow"; 910 case GLFW_VRESIZE_CURSOR: 911 return "sb_v_double_arrow"; 912 } 913 return NULL; 914 } 915 916 ////////////////////////////////////////////////////////////////////////// 917 ////// GLFW platform API ////// 918 ////////////////////////////////////////////////////////////////////////// 919 920 int _glfwPlatformCreateWindow(_GLFWwindow* window, 921 const _GLFWwndconfig* wndconfig, 922 const _GLFWctxconfig* ctxconfig, 923 const _GLFWfbconfig* fbconfig) 924 { 925 window->wl.transparent = fbconfig->transparent; 926 927 if (!createSurface(window, wndconfig)) 928 return GLFW_FALSE; 929 930 if (ctxconfig->client != GLFW_NO_API) 931 { 932 if (ctxconfig->source == GLFW_EGL_CONTEXT_API || 933 ctxconfig->source == GLFW_NATIVE_CONTEXT_API) 934 { 935 if (!_glfwInitEGL()) 936 return GLFW_FALSE; 937 if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) 938 return GLFW_FALSE; 939 } 940 else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) 941 { 942 if (!_glfwInitOSMesa()) 943 return GLFW_FALSE; 944 if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) 945 return GLFW_FALSE; 946 } 947 } 948 949 if (wndconfig->title) 950 window->wl.title = _glfw_strdup(wndconfig->title); 951 952 if (wndconfig->visible) 953 { 954 if (_glfw.wl.wmBase) 955 { 956 if (!createXdgSurface(window)) 957 return GLFW_FALSE; 958 } 959 else 960 { 961 if (!createShellSurface(window)) 962 return GLFW_FALSE; 963 } 964 965 window->wl.visible = GLFW_TRUE; 966 } 967 else 968 { 969 window->wl.xdg.surface = NULL; 970 window->wl.xdg.toplevel = NULL; 971 window->wl.shellSurface = NULL; 972 window->wl.visible = GLFW_FALSE; 973 } 974 975 window->wl.currentCursor = NULL; 976 977 window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*)); 978 window->wl.monitorsCount = 0; 979 window->wl.monitorsSize = 1; 980 981 return GLFW_TRUE; 982 } 983 984 void _glfwPlatformDestroyWindow(_GLFWwindow* window) 985 { 986 if (window == _glfw.wl.pointerFocus) 987 { 988 _glfw.wl.pointerFocus = NULL; 989 _glfwInputCursorEnter(window, GLFW_FALSE); 990 } 991 if (window == _glfw.wl.keyboardFocus) 992 { 993 _glfw.wl.keyboardFocus = NULL; 994 _glfwInputWindowFocus(window, GLFW_FALSE); 995 } 996 997 if (window->wl.idleInhibitor) 998 zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor); 999 1000 if (window->context.destroy) 1001 window->context.destroy(window); 1002 1003 destroyDecorations(window); 1004 if (window->wl.xdg.decoration) 1005 zxdg_toplevel_decoration_v1_destroy(window->wl.xdg.decoration); 1006 1007 if (window->wl.decorations.buffer) 1008 wl_buffer_destroy(window->wl.decorations.buffer); 1009 1010 if (window->wl.native) 1011 wl_egl_window_destroy(window->wl.native); 1012 1013 if (window->wl.shellSurface) 1014 wl_shell_surface_destroy(window->wl.shellSurface); 1015 1016 if (window->wl.xdg.toplevel) 1017 xdg_toplevel_destroy(window->wl.xdg.toplevel); 1018 1019 if (window->wl.xdg.surface) 1020 xdg_surface_destroy(window->wl.xdg.surface); 1021 1022 if (window->wl.surface) 1023 wl_surface_destroy(window->wl.surface); 1024 1025 free(window->wl.title); 1026 free(window->wl.monitors); 1027 } 1028 1029 void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) 1030 { 1031 if (window->wl.title) 1032 free(window->wl.title); 1033 window->wl.title = _glfw_strdup(title); 1034 if (window->wl.xdg.toplevel) 1035 xdg_toplevel_set_title(window->wl.xdg.toplevel, title); 1036 else if (window->wl.shellSurface) 1037 wl_shell_surface_set_title(window->wl.shellSurface, title); 1038 } 1039 1040 void _glfwPlatformSetWindowIcon(_GLFWwindow* window, 1041 int count, const GLFWimage* images) 1042 { 1043 _glfwInputError(GLFW_PLATFORM_ERROR, 1044 "Wayland: Setting window icon not supported"); 1045 } 1046 1047 void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) 1048 { 1049 // A Wayland client is not aware of its position, so just warn and leave it 1050 // as (0, 0) 1051 1052 _glfwInputError(GLFW_PLATFORM_ERROR, 1053 "Wayland: Window position retrieval not supported"); 1054 } 1055 1056 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) 1057 { 1058 // A Wayland client can not set its position, so just warn 1059 1060 _glfwInputError(GLFW_PLATFORM_ERROR, 1061 "Wayland: Window position setting not supported"); 1062 } 1063 1064 void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) 1065 { 1066 if (width) 1067 *width = window->wl.width; 1068 if (height) 1069 *height = window->wl.height; 1070 } 1071 1072 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) 1073 { 1074 window->wl.width = width; 1075 window->wl.height = height; 1076 resizeWindow(window); 1077 } 1078 1079 void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, 1080 int minwidth, int minheight, 1081 int maxwidth, int maxheight) 1082 { 1083 if (_glfw.wl.wmBase) 1084 { 1085 if (window->wl.xdg.toplevel) 1086 { 1087 if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) 1088 minwidth = minheight = 0; 1089 if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE) 1090 maxwidth = maxheight = 0; 1091 xdg_toplevel_set_min_size(window->wl.xdg.toplevel, minwidth, minheight); 1092 xdg_toplevel_set_max_size(window->wl.xdg.toplevel, maxwidth, maxheight); 1093 wl_surface_commit(window->wl.surface); 1094 } 1095 } 1096 else 1097 { 1098 // TODO: find out how to trigger a resize. 1099 // The actual limits are checked in the wl_shell_surface::configure handler. 1100 } 1101 } 1102 1103 void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, 1104 int numer, int denom) 1105 { 1106 // TODO: find out how to trigger a resize. 1107 // The actual limits are checked in the wl_shell_surface::configure handler. 1108 } 1109 1110 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, 1111 int* width, int* height) 1112 { 1113 _glfwPlatformGetWindowSize(window, width, height); 1114 *width *= window->wl.scale; 1115 *height *= window->wl.scale; 1116 } 1117 1118 void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, 1119 int* left, int* top, 1120 int* right, int* bottom) 1121 { 1122 if (window->decorated && !window->monitor && !window->wl.decorations.serverSide) 1123 { 1124 if (top) 1125 *top = _GLFW_DECORATION_TOP; 1126 if (left) 1127 *left = _GLFW_DECORATION_WIDTH; 1128 if (right) 1129 *right = _GLFW_DECORATION_WIDTH; 1130 if (bottom) 1131 *bottom = _GLFW_DECORATION_WIDTH; 1132 } 1133 } 1134 1135 void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, 1136 float* xscale, float* yscale) 1137 { 1138 if (xscale) 1139 *xscale = (float) window->wl.scale; 1140 if (yscale) 1141 *yscale = (float) window->wl.scale; 1142 } 1143 1144 void _glfwPlatformIconifyWindow(_GLFWwindow* window) 1145 { 1146 if (_glfw.wl.wmBase) 1147 { 1148 if (window->wl.xdg.toplevel) 1149 xdg_toplevel_set_minimized(window->wl.xdg.toplevel); 1150 } 1151 else 1152 { 1153 _glfwInputError(GLFW_PLATFORM_ERROR, 1154 "Wayland: Iconify window not supported on wl_shell"); 1155 } 1156 } 1157 1158 void _glfwPlatformRestoreWindow(_GLFWwindow* window) 1159 { 1160 if (window->wl.xdg.toplevel) 1161 { 1162 if (window->monitor) 1163 xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel); 1164 if (window->wl.maximized) 1165 xdg_toplevel_unset_maximized(window->wl.xdg.toplevel); 1166 // There is no way to unset minimized, or even to know if we are 1167 // minimized, so there is nothing to do here. 1168 } 1169 else if (window->wl.shellSurface) 1170 { 1171 if (window->monitor || window->wl.maximized) 1172 wl_shell_surface_set_toplevel(window->wl.shellSurface); 1173 } 1174 _glfwInputWindowMonitor(window, NULL); 1175 window->wl.maximized = GLFW_FALSE; 1176 } 1177 1178 void _glfwPlatformMaximizeWindow(_GLFWwindow* window) 1179 { 1180 if (window->wl.xdg.toplevel) 1181 { 1182 xdg_toplevel_set_maximized(window->wl.xdg.toplevel); 1183 } 1184 else if (window->wl.shellSurface) 1185 { 1186 // Let the compositor select the best output. 1187 wl_shell_surface_set_maximized(window->wl.shellSurface, NULL); 1188 } 1189 window->wl.maximized = GLFW_TRUE; 1190 } 1191 1192 void _glfwPlatformShowWindow(_GLFWwindow* window) 1193 { 1194 if (!window->wl.visible) 1195 { 1196 if (_glfw.wl.wmBase) 1197 createXdgSurface(window); 1198 else if (!window->wl.shellSurface) 1199 createShellSurface(window); 1200 window->wl.visible = GLFW_TRUE; 1201 } 1202 } 1203 1204 void _glfwPlatformHideWindow(_GLFWwindow* window) 1205 { 1206 if (window->wl.xdg.toplevel) 1207 { 1208 xdg_toplevel_destroy(window->wl.xdg.toplevel); 1209 xdg_surface_destroy(window->wl.xdg.surface); 1210 window->wl.xdg.toplevel = NULL; 1211 window->wl.xdg.surface = NULL; 1212 } 1213 else if (window->wl.shellSurface) 1214 { 1215 wl_shell_surface_destroy(window->wl.shellSurface); 1216 window->wl.shellSurface = NULL; 1217 } 1218 window->wl.visible = GLFW_FALSE; 1219 } 1220 1221 void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) 1222 { 1223 // TODO 1224 _glfwInputError(GLFW_PLATFORM_ERROR, 1225 "Wayland: Window attention request not implemented yet"); 1226 } 1227 1228 void _glfwPlatformFocusWindow(_GLFWwindow* window) 1229 { 1230 _glfwInputError(GLFW_PLATFORM_ERROR, 1231 "Wayland: Focusing a window requires user interaction"); 1232 } 1233 1234 void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, 1235 _GLFWmonitor* monitor, 1236 int xpos, int ypos, 1237 int width, int height, 1238 int refreshRate) 1239 { 1240 if (monitor) 1241 { 1242 setFullscreen(window, monitor, refreshRate); 1243 } 1244 else 1245 { 1246 if (window->wl.xdg.toplevel) 1247 xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel); 1248 else if (window->wl.shellSurface) 1249 wl_shell_surface_set_toplevel(window->wl.shellSurface); 1250 setIdleInhibitor(window, GLFW_FALSE); 1251 if (!_glfw.wl.decorationManager) 1252 createDecorations(window); 1253 } 1254 _glfwInputWindowMonitor(window, monitor); 1255 } 1256 1257 int _glfwPlatformWindowFocused(_GLFWwindow* window) 1258 { 1259 return _glfw.wl.keyboardFocus == window; 1260 } 1261 1262 int _glfwPlatformWindowIconified(_GLFWwindow* window) 1263 { 1264 // wl_shell doesn't have any iconified concept, and xdg-shell doesn’t give 1265 // any way to request whether a surface is iconified. 1266 return GLFW_FALSE; 1267 } 1268 1269 int _glfwPlatformWindowVisible(_GLFWwindow* window) 1270 { 1271 return window->wl.visible; 1272 } 1273 1274 int _glfwPlatformWindowMaximized(_GLFWwindow* window) 1275 { 1276 return window->wl.maximized; 1277 } 1278 1279 int _glfwPlatformWindowHovered(_GLFWwindow* window) 1280 { 1281 return window->wl.hovered; 1282 } 1283 1284 int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) 1285 { 1286 return window->wl.transparent; 1287 } 1288 1289 void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) 1290 { 1291 // TODO 1292 _glfwInputError(GLFW_PLATFORM_ERROR, 1293 "Wayland: Window attribute setting not implemented yet"); 1294 } 1295 1296 void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) 1297 { 1298 if (!window->monitor) 1299 { 1300 if (enabled) 1301 createDecorations(window); 1302 else 1303 destroyDecorations(window); 1304 } 1305 } 1306 1307 void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) 1308 { 1309 // TODO 1310 _glfwInputError(GLFW_PLATFORM_ERROR, 1311 "Wayland: Window attribute setting not implemented yet"); 1312 } 1313 1314 float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) 1315 { 1316 return 1.f; 1317 } 1318 1319 void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) 1320 { 1321 } 1322 1323 void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) 1324 { 1325 // This is handled in relativePointerHandleRelativeMotion 1326 } 1327 1328 GLFWbool _glfwPlatformRawMouseMotionSupported(void) 1329 { 1330 return GLFW_TRUE; 1331 } 1332 1333 void _glfwPlatformPollEvents(void) 1334 { 1335 handleEvents(0); 1336 } 1337 1338 void _glfwPlatformWaitEvents(void) 1339 { 1340 handleEvents(-1); 1341 } 1342 1343 void _glfwPlatformWaitEventsTimeout(double timeout) 1344 { 1345 handleEvents((int) (timeout * 1e3)); 1346 } 1347 1348 void _glfwPlatformPostEmptyEvent(void) 1349 { 1350 wl_display_sync(_glfw.wl.display); 1351 } 1352 1353 void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) 1354 { 1355 if (xpos) 1356 *xpos = window->wl.cursorPosX; 1357 if (ypos) 1358 *ypos = window->wl.cursorPosY; 1359 } 1360 1361 static GLFWbool isPointerLocked(_GLFWwindow* window); 1362 1363 void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) 1364 { 1365 if (isPointerLocked(window)) 1366 { 1367 zwp_locked_pointer_v1_set_cursor_position_hint( 1368 window->wl.pointerLock.lockedPointer, 1369 wl_fixed_from_double(x), wl_fixed_from_double(y)); 1370 wl_surface_commit(window->wl.surface); 1371 } 1372 } 1373 1374 void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) 1375 { 1376 _glfwPlatformSetCursor(window, window->wl.currentCursor); 1377 } 1378 1379 const char* _glfwPlatformGetScancodeName(int scancode) 1380 { 1381 // TODO 1382 return NULL; 1383 } 1384 1385 int _glfwPlatformGetKeyScancode(int key) 1386 { 1387 return _glfw.wl.scancodes[key]; 1388 } 1389 1390 int _glfwPlatformCreateCursor(_GLFWcursor* cursor, 1391 const GLFWimage* image, 1392 int xhot, int yhot) 1393 { 1394 cursor->wl.buffer = createShmBuffer(image); 1395 if (!cursor->wl.buffer) 1396 return GLFW_FALSE; 1397 1398 cursor->wl.width = image->width; 1399 cursor->wl.height = image->height; 1400 cursor->wl.xhot = xhot; 1401 cursor->wl.yhot = yhot; 1402 return GLFW_TRUE; 1403 } 1404 1405 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) 1406 { 1407 struct wl_cursor* standardCursor; 1408 1409 standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, 1410 translateCursorShape(shape)); 1411 if (!standardCursor) 1412 { 1413 _glfwInputError(GLFW_PLATFORM_ERROR, 1414 "Wayland: Standard cursor \"%s\" not found", 1415 translateCursorShape(shape)); 1416 return GLFW_FALSE; 1417 } 1418 1419 cursor->wl.cursor = standardCursor; 1420 cursor->wl.currentImage = 0; 1421 1422 if (_glfw.wl.cursorThemeHiDPI) 1423 { 1424 standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, 1425 translateCursorShape(shape)); 1426 cursor->wl.cursorHiDPI = standardCursor; 1427 } 1428 1429 return GLFW_TRUE; 1430 } 1431 1432 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) 1433 { 1434 // If it's a standard cursor we don't need to do anything here 1435 if (cursor->wl.cursor) 1436 return; 1437 1438 if (cursor->wl.buffer) 1439 wl_buffer_destroy(cursor->wl.buffer); 1440 } 1441 1442 static void relativePointerHandleRelativeMotion(void* data, 1443 struct zwp_relative_pointer_v1* pointer, 1444 uint32_t timeHi, 1445 uint32_t timeLo, 1446 wl_fixed_t dx, 1447 wl_fixed_t dy, 1448 wl_fixed_t dxUnaccel, 1449 wl_fixed_t dyUnaccel) 1450 { 1451 _GLFWwindow* window = data; 1452 double xpos = window->virtualCursorPosX; 1453 double ypos = window->virtualCursorPosY; 1454 1455 if (window->cursorMode != GLFW_CURSOR_DISABLED) 1456 return; 1457 1458 if (window->rawMouseMotion) 1459 { 1460 xpos += wl_fixed_to_double(dxUnaccel); 1461 ypos += wl_fixed_to_double(dyUnaccel); 1462 } 1463 else 1464 { 1465 xpos += wl_fixed_to_double(dx); 1466 ypos += wl_fixed_to_double(dy); 1467 } 1468 1469 _glfwInputCursorPos(window, xpos, ypos); 1470 } 1471 1472 static const struct zwp_relative_pointer_v1_listener relativePointerListener = { 1473 relativePointerHandleRelativeMotion 1474 }; 1475 1476 static void lockedPointerHandleLocked(void* data, 1477 struct zwp_locked_pointer_v1* lockedPointer) 1478 { 1479 } 1480 1481 static void unlockPointer(_GLFWwindow* window) 1482 { 1483 struct zwp_relative_pointer_v1* relativePointer = 1484 window->wl.pointerLock.relativePointer; 1485 struct zwp_locked_pointer_v1* lockedPointer = 1486 window->wl.pointerLock.lockedPointer; 1487 1488 zwp_relative_pointer_v1_destroy(relativePointer); 1489 zwp_locked_pointer_v1_destroy(lockedPointer); 1490 1491 window->wl.pointerLock.relativePointer = NULL; 1492 window->wl.pointerLock.lockedPointer = NULL; 1493 } 1494 1495 static void lockPointer(_GLFWwindow* window); 1496 1497 static void lockedPointerHandleUnlocked(void* data, 1498 struct zwp_locked_pointer_v1* lockedPointer) 1499 { 1500 } 1501 1502 static const struct zwp_locked_pointer_v1_listener lockedPointerListener = { 1503 lockedPointerHandleLocked, 1504 lockedPointerHandleUnlocked 1505 }; 1506 1507 static void lockPointer(_GLFWwindow* window) 1508 { 1509 struct zwp_relative_pointer_v1* relativePointer; 1510 struct zwp_locked_pointer_v1* lockedPointer; 1511 1512 if (!_glfw.wl.relativePointerManager) 1513 { 1514 _glfwInputError(GLFW_PLATFORM_ERROR, 1515 "Wayland: no relative pointer manager"); 1516 return; 1517 } 1518 1519 relativePointer = 1520 zwp_relative_pointer_manager_v1_get_relative_pointer( 1521 _glfw.wl.relativePointerManager, 1522 _glfw.wl.pointer); 1523 zwp_relative_pointer_v1_add_listener(relativePointer, 1524 &relativePointerListener, 1525 window); 1526 1527 lockedPointer = 1528 zwp_pointer_constraints_v1_lock_pointer( 1529 _glfw.wl.pointerConstraints, 1530 window->wl.surface, 1531 _glfw.wl.pointer, 1532 NULL, 1533 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); 1534 zwp_locked_pointer_v1_add_listener(lockedPointer, 1535 &lockedPointerListener, 1536 window); 1537 1538 window->wl.pointerLock.relativePointer = relativePointer; 1539 window->wl.pointerLock.lockedPointer = lockedPointer; 1540 1541 wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, 1542 NULL, 0, 0); 1543 } 1544 1545 static GLFWbool isPointerLocked(_GLFWwindow* window) 1546 { 1547 return window->wl.pointerLock.lockedPointer != NULL; 1548 } 1549 1550 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) 1551 { 1552 struct wl_cursor* defaultCursor; 1553 struct wl_cursor* defaultCursorHiDPI = NULL; 1554 1555 if (!_glfw.wl.pointer) 1556 return; 1557 1558 window->wl.currentCursor = cursor; 1559 1560 // If we're not in the correct window just save the cursor 1561 // the next time the pointer enters the window the cursor will change 1562 if (window != _glfw.wl.pointerFocus || window->wl.decorations.focus != mainWindow) 1563 return; 1564 1565 // Unlock possible pointer lock if no longer disabled. 1566 if (window->cursorMode != GLFW_CURSOR_DISABLED && isPointerLocked(window)) 1567 unlockPointer(window); 1568 1569 if (window->cursorMode == GLFW_CURSOR_NORMAL) 1570 { 1571 if (cursor) 1572 setCursorImage(window, &cursor->wl); 1573 else 1574 { 1575 defaultCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, 1576 "left_ptr"); 1577 if (!defaultCursor) 1578 { 1579 _glfwInputError(GLFW_PLATFORM_ERROR, 1580 "Wayland: Standard cursor not found"); 1581 return; 1582 } 1583 if (_glfw.wl.cursorThemeHiDPI) 1584 defaultCursorHiDPI = 1585 wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, 1586 "left_ptr"); 1587 _GLFWcursorWayland cursorWayland = { 1588 defaultCursor, 1589 defaultCursorHiDPI, 1590 NULL, 1591 0, 0, 1592 0, 0, 1593 0 1594 }; 1595 setCursorImage(window, &cursorWayland); 1596 } 1597 } 1598 else if (window->cursorMode == GLFW_CURSOR_DISABLED) 1599 { 1600 if (!isPointerLocked(window)) 1601 lockPointer(window); 1602 } 1603 else if (window->cursorMode == GLFW_CURSOR_HIDDEN) 1604 { 1605 wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, NULL, 0, 0); 1606 } 1607 } 1608 1609 static void dataSourceHandleTarget(void* data, 1610 struct wl_data_source* dataSource, 1611 const char* mimeType) 1612 { 1613 if (_glfw.wl.dataSource != dataSource) 1614 { 1615 _glfwInputError(GLFW_PLATFORM_ERROR, 1616 "Wayland: Unknown clipboard data source"); 1617 return; 1618 } 1619 } 1620 1621 static void dataSourceHandleSend(void* data, 1622 struct wl_data_source* dataSource, 1623 const char* mimeType, 1624 int fd) 1625 { 1626 const char* string = _glfw.wl.clipboardSendString; 1627 size_t len = _glfw.wl.clipboardSendSize; 1628 int ret; 1629 1630 if (_glfw.wl.dataSource != dataSource) 1631 { 1632 _glfwInputError(GLFW_PLATFORM_ERROR, 1633 "Wayland: Unknown clipboard data source"); 1634 return; 1635 } 1636 1637 if (!string) 1638 { 1639 _glfwInputError(GLFW_PLATFORM_ERROR, 1640 "Wayland: Copy requested from an invalid string"); 1641 return; 1642 } 1643 1644 if (strcmp(mimeType, "text/plain;charset=utf-8") != 0) 1645 { 1646 _glfwInputError(GLFW_PLATFORM_ERROR, 1647 "Wayland: Wrong MIME type asked from clipboard"); 1648 close(fd); 1649 return; 1650 } 1651 1652 while (len > 0) 1653 { 1654 ret = write(fd, string, len); 1655 if (ret == -1 && errno == EINTR) 1656 continue; 1657 if (ret == -1) 1658 { 1659 // TODO: also report errno maybe. 1660 _glfwInputError(GLFW_PLATFORM_ERROR, 1661 "Wayland: Error while writing the clipboard"); 1662 close(fd); 1663 return; 1664 } 1665 len -= ret; 1666 } 1667 close(fd); 1668 } 1669 1670 static void dataSourceHandleCancelled(void* data, 1671 struct wl_data_source* dataSource) 1672 { 1673 wl_data_source_destroy(dataSource); 1674 1675 if (_glfw.wl.dataSource != dataSource) 1676 { 1677 _glfwInputError(GLFW_PLATFORM_ERROR, 1678 "Wayland: Unknown clipboard data source"); 1679 return; 1680 } 1681 1682 _glfw.wl.dataSource = NULL; 1683 } 1684 1685 static const struct wl_data_source_listener dataSourceListener = { 1686 dataSourceHandleTarget, 1687 dataSourceHandleSend, 1688 dataSourceHandleCancelled, 1689 }; 1690 1691 void _glfwPlatformSetClipboardString(const char* string) 1692 { 1693 if (_glfw.wl.dataSource) 1694 { 1695 wl_data_source_destroy(_glfw.wl.dataSource); 1696 _glfw.wl.dataSource = NULL; 1697 } 1698 1699 if (_glfw.wl.clipboardSendString) 1700 { 1701 free(_glfw.wl.clipboardSendString); 1702 _glfw.wl.clipboardSendString = NULL; 1703 } 1704 1705 _glfw.wl.clipboardSendString = strdup(string); 1706 if (!_glfw.wl.clipboardSendString) 1707 { 1708 _glfwInputError(GLFW_PLATFORM_ERROR, 1709 "Wayland: Impossible to allocate clipboard string"); 1710 return; 1711 } 1712 _glfw.wl.clipboardSendSize = strlen(string); 1713 _glfw.wl.dataSource = 1714 wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager); 1715 if (!_glfw.wl.dataSource) 1716 { 1717 _glfwInputError(GLFW_PLATFORM_ERROR, 1718 "Wayland: Impossible to create clipboard source"); 1719 free(_glfw.wl.clipboardSendString); 1720 return; 1721 } 1722 wl_data_source_add_listener(_glfw.wl.dataSource, 1723 &dataSourceListener, 1724 NULL); 1725 wl_data_source_offer(_glfw.wl.dataSource, "text/plain;charset=utf-8"); 1726 wl_data_device_set_selection(_glfw.wl.dataDevice, 1727 _glfw.wl.dataSource, 1728 _glfw.wl.serial); 1729 } 1730 1731 static GLFWbool growClipboardString(void) 1732 { 1733 char* clipboard = _glfw.wl.clipboardString; 1734 1735 clipboard = realloc(clipboard, _glfw.wl.clipboardSize * 2); 1736 if (!clipboard) 1737 { 1738 _glfwInputError(GLFW_PLATFORM_ERROR, 1739 "Wayland: Impossible to grow clipboard string"); 1740 return GLFW_FALSE; 1741 } 1742 _glfw.wl.clipboardString = clipboard; 1743 _glfw.wl.clipboardSize = _glfw.wl.clipboardSize * 2; 1744 return GLFW_TRUE; 1745 } 1746 1747 const char* _glfwPlatformGetClipboardString(void) 1748 { 1749 int fds[2]; 1750 int ret; 1751 size_t len = 0; 1752 1753 if (!_glfw.wl.dataOffer) 1754 { 1755 _glfwInputError(GLFW_FORMAT_UNAVAILABLE, 1756 "No clipboard data has been sent yet"); 1757 return NULL; 1758 } 1759 1760 ret = pipe2(fds, O_CLOEXEC); 1761 if (ret < 0) 1762 { 1763 // TODO: also report errno maybe? 1764 _glfwInputError(GLFW_PLATFORM_ERROR, 1765 "Wayland: Impossible to create clipboard pipe fds"); 1766 return NULL; 1767 } 1768 1769 wl_data_offer_receive(_glfw.wl.dataOffer, "text/plain;charset=utf-8", fds[1]); 1770 close(fds[1]); 1771 1772 // XXX: this is a huge hack, this function shouldn’t be synchronous! 1773 handleEvents(-1); 1774 1775 while (1) 1776 { 1777 // Grow the clipboard if we need to paste something bigger, there is no 1778 // shrink operation yet. 1779 if (len + 4096 > _glfw.wl.clipboardSize) 1780 { 1781 if (!growClipboardString()) 1782 { 1783 close(fds[0]); 1784 return NULL; 1785 } 1786 } 1787 1788 // Then read from the fd to the clipboard, handling all known errors. 1789 ret = read(fds[0], _glfw.wl.clipboardString + len, 4096); 1790 if (ret == 0) 1791 break; 1792 if (ret == -1 && errno == EINTR) 1793 continue; 1794 if (ret == -1) 1795 { 1796 // TODO: also report errno maybe. 1797 _glfwInputError(GLFW_PLATFORM_ERROR, 1798 "Wayland: Impossible to read from clipboard fd"); 1799 close(fds[0]); 1800 return NULL; 1801 } 1802 len += ret; 1803 } 1804 close(fds[0]); 1805 if (len + 1 > _glfw.wl.clipboardSize) 1806 { 1807 if (!growClipboardString()) 1808 return NULL; 1809 } 1810 _glfw.wl.clipboardString[len] = '\0'; 1811 return _glfw.wl.clipboardString; 1812 } 1813 1814 void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) 1815 { 1816 if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_wayland_surface) 1817 return; 1818 1819 extensions[0] = "VK_KHR_surface"; 1820 extensions[1] = "VK_KHR_wayland_surface"; 1821 } 1822 1823 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, 1824 VkPhysicalDevice device, 1825 uint32_t queuefamily) 1826 { 1827 PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR 1828 vkGetPhysicalDeviceWaylandPresentationSupportKHR = 1829 (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR) 1830 vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR"); 1831 if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR) 1832 { 1833 _glfwInputError(GLFW_API_UNAVAILABLE, 1834 "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension"); 1835 return VK_NULL_HANDLE; 1836 } 1837 1838 return vkGetPhysicalDeviceWaylandPresentationSupportKHR(device, 1839 queuefamily, 1840 _glfw.wl.display); 1841 } 1842 1843 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, 1844 _GLFWwindow* window, 1845 const VkAllocationCallbacks* allocator, 1846 VkSurfaceKHR* surface) 1847 { 1848 VkResult err; 1849 VkWaylandSurfaceCreateInfoKHR sci; 1850 PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR; 1851 1852 vkCreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR) 1853 vkGetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR"); 1854 if (!vkCreateWaylandSurfaceKHR) 1855 { 1856 _glfwInputError(GLFW_API_UNAVAILABLE, 1857 "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension"); 1858 return VK_ERROR_EXTENSION_NOT_PRESENT; 1859 } 1860 1861 memset(&sci, 0, sizeof(sci)); 1862 sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; 1863 sci.display = _glfw.wl.display; 1864 sci.surface = window->wl.surface; 1865 1866 err = vkCreateWaylandSurfaceKHR(instance, &sci, allocator, surface); 1867 if (err) 1868 { 1869 _glfwInputError(GLFW_PLATFORM_ERROR, 1870 "Wayland: Failed to create Vulkan surface: %s", 1871 _glfwGetVulkanResultString(err)); 1872 } 1873 1874 return err; 1875 } 1876 1877 1878 ////////////////////////////////////////////////////////////////////////// 1879 ////// GLFW native API ////// 1880 ////////////////////////////////////////////////////////////////////////// 1881 1882 GLFWAPI struct wl_display* glfwGetWaylandDisplay(void) 1883 { 1884 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 1885 return _glfw.wl.display; 1886 } 1887 1888 GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle) 1889 { 1890 _GLFWwindow* window = (_GLFWwindow*) handle; 1891 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 1892 return window->wl.surface; 1893 } 1894