wl_window.cc (32545B)
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 27 #define _GNU_SOURCE 28 29 #include "internal.h" 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <errno.h> 34 #include <unistd.h> 35 #include <string.h> 36 #include <fcntl.h> 37 #include <sys/mman.h> 38 #include <poll.h> 39 40 #include <wayland-egl.h> 41 #include <wayland-cursor.h> 42 43 44 static void handlePing(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 handleConfigure(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 (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE) 64 { 65 aspectRatio = (float)width / (float)height; 66 targetRatio = (float)window->numer / (float)window->denom; 67 if (aspectRatio < targetRatio) 68 height = width / targetRatio; 69 else if (aspectRatio > targetRatio) 70 width = height * targetRatio; 71 } 72 73 if (window->minwidth != GLFW_DONT_CARE && width < window->minwidth) 74 width = window->minwidth; 75 else if (window->maxwidth != GLFW_DONT_CARE && width > window->maxwidth) 76 width = window->maxwidth; 77 78 if (window->minheight != GLFW_DONT_CARE && height < window->minheight) 79 height = window->minheight; 80 else if (window->maxheight != GLFW_DONT_CARE && height > window->maxheight) 81 height = window->maxheight; 82 } 83 84 _glfwInputWindowSize(window, width, height); 85 _glfwPlatformSetWindowSize(window, width, height); 86 _glfwInputWindowDamage(window); 87 } 88 89 static void handlePopupDone(void* data, 90 struct wl_shell_surface* shellSurface) 91 { 92 } 93 94 static const struct wl_shell_surface_listener shellSurfaceListener = { 95 handlePing, 96 handleConfigure, 97 handlePopupDone 98 }; 99 100 static void checkScaleChange(_GLFWwindow* window) 101 { 102 int scaledWidth, scaledHeight; 103 int scale = 1; 104 int i; 105 int monitorScale; 106 107 // Check if we will be able to set the buffer scale or not. 108 if (_glfw.wl.compositorVersion < 3) 109 return; 110 111 // Get the scale factor from the highest scale monitor. 112 for (i = 0; i < window->wl.monitorsCount; ++i) 113 { 114 monitorScale = window->wl.monitors[i]->wl.scale; 115 if (scale < monitorScale) 116 scale = monitorScale; 117 } 118 119 // Only change the framebuffer size if the scale changed. 120 if (scale != window->wl.scale) 121 { 122 window->wl.scale = scale; 123 scaledWidth = window->wl.width * scale; 124 scaledHeight = window->wl.height * scale; 125 wl_surface_set_buffer_scale(window->wl.surface, scale); 126 wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0); 127 _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); 128 } 129 } 130 131 static void handleEnter(void *data, 132 struct wl_surface *surface, 133 struct wl_output *output) 134 { 135 _GLFWwindow* window = data; 136 _GLFWmonitor* monitor = wl_output_get_user_data(output); 137 138 if (window->wl.monitorsCount + 1 > window->wl.monitorsSize) 139 { 140 ++window->wl.monitorsSize; 141 window->wl.monitors = 142 realloc(window->wl.monitors, 143 window->wl.monitorsSize * sizeof(_GLFWmonitor*)); 144 } 145 146 window->wl.monitors[window->wl.monitorsCount++] = monitor; 147 148 checkScaleChange(window); 149 } 150 151 static void handleLeave(void *data, 152 struct wl_surface *surface, 153 struct wl_output *output) 154 { 155 _GLFWwindow* window = data; 156 _GLFWmonitor* monitor = wl_output_get_user_data(output); 157 GLFWbool found; 158 int i; 159 160 for (i = 0, found = GLFW_FALSE; i < window->wl.monitorsCount - 1; ++i) 161 { 162 if (monitor == window->wl.monitors[i]) 163 found = GLFW_TRUE; 164 if (found) 165 window->wl.monitors[i] = window->wl.monitors[i + 1]; 166 } 167 window->wl.monitors[--window->wl.monitorsCount] = NULL; 168 169 checkScaleChange(window); 170 } 171 172 static const struct wl_surface_listener surfaceListener = { 173 handleEnter, 174 handleLeave 175 }; 176 177 // Makes the surface considered as XRGB instead of ARGB. 178 static void setOpaqueRegion(_GLFWwindow* window) 179 { 180 struct wl_region* region; 181 182 region = wl_compositor_create_region(_glfw.wl.compositor); 183 if (!region) 184 return; 185 186 wl_region_add(region, 0, 0, window->wl.width, window->wl.height); 187 wl_surface_set_opaque_region(window->wl.surface, region); 188 wl_surface_commit(window->wl.surface); 189 wl_region_destroy(region); 190 } 191 192 static GLFWbool createSurface(_GLFWwindow* window, 193 const _GLFWwndconfig* wndconfig) 194 { 195 window->wl.surface = wl_compositor_create_surface(_glfw.wl.compositor); 196 if (!window->wl.surface) 197 return GLFW_FALSE; 198 199 wl_surface_add_listener(window->wl.surface, 200 &surfaceListener, 201 window); 202 203 wl_surface_set_user_data(window->wl.surface, window); 204 205 window->wl.native = wl_egl_window_create(window->wl.surface, 206 wndconfig->width, 207 wndconfig->height); 208 if (!window->wl.native) 209 return GLFW_FALSE; 210 211 window->wl.width = wndconfig->width; 212 window->wl.height = wndconfig->height; 213 window->wl.scale = 1; 214 215 // TODO: make this optional once issue #197 is fixed. 216 setOpaqueRegion(window); 217 218 return GLFW_TRUE; 219 } 220 221 static GLFWbool createShellSurface(_GLFWwindow* window) 222 { 223 window->wl.shellSurface = wl_shell_get_shell_surface(_glfw.wl.shell, 224 window->wl.surface); 225 if (!window->wl.shellSurface) 226 return GLFW_FALSE; 227 228 wl_shell_surface_add_listener(window->wl.shellSurface, 229 &shellSurfaceListener, 230 window); 231 232 if (window->wl.title) 233 wl_shell_surface_set_title(window->wl.shellSurface, window->wl.title); 234 235 if (window->monitor) 236 { 237 wl_shell_surface_set_fullscreen( 238 window->wl.shellSurface, 239 WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 240 0, 241 window->monitor->wl.output); 242 } 243 else if (window->wl.maximized) 244 { 245 wl_shell_surface_set_maximized(window->wl.shellSurface, NULL); 246 } 247 else 248 { 249 wl_shell_surface_set_toplevel(window->wl.shellSurface); 250 } 251 252 wl_surface_commit(window->wl.surface); 253 254 return GLFW_TRUE; 255 } 256 257 static int 258 createTmpfileCloexec(char* tmpname) 259 { 260 int fd; 261 262 fd = mkostemp(tmpname, O_CLOEXEC); 263 if (fd >= 0) 264 unlink(tmpname); 265 266 return fd; 267 } 268 269 static void 270 handleEvents(int timeout) 271 { 272 struct wl_display* display = _glfw.wl.display; 273 struct pollfd fds[] = { 274 { wl_display_get_fd(display), POLLIN }, 275 }; 276 277 while (wl_display_prepare_read(display) != 0) 278 wl_display_dispatch_pending(display); 279 280 // If an error different from EAGAIN happens, we have likely been 281 // disconnected from the Wayland session, try to handle that the best we 282 // can. 283 if (wl_display_flush(display) < 0 && errno != EAGAIN) 284 { 285 _GLFWwindow* window = _glfw.windowListHead; 286 while (window) 287 { 288 _glfwInputWindowCloseRequest(window); 289 window = window->next; 290 } 291 wl_display_cancel_read(display); 292 return; 293 } 294 295 if (poll(fds, 1, timeout) > 0) 296 { 297 wl_display_read_events(display); 298 wl_display_dispatch_pending(display); 299 } 300 else 301 { 302 wl_display_cancel_read(display); 303 } 304 } 305 306 /* 307 * Create a new, unique, anonymous file of the given size, and 308 * return the file descriptor for it. The file descriptor is set 309 * CLOEXEC. The file is immediately suitable for mmap()'ing 310 * the given size at offset zero. 311 * 312 * The file should not have a permanent backing store like a disk, 313 * but may have if XDG_RUNTIME_DIR is not properly implemented in OS. 314 * 315 * The file name is deleted from the file system. 316 * 317 * The file is suitable for buffer sharing between processes by 318 * transmitting the file descriptor over Unix sockets using the 319 * SCM_RIGHTS methods. 320 * 321 * posix_fallocate() is used to guarantee that disk space is available 322 * for the file at the given size. If disk space is insufficent, errno 323 * is set to ENOSPC. If posix_fallocate() is not supported, program may 324 * receive SIGBUS on accessing mmap()'ed file contents instead. 325 */ 326 int 327 createAnonymousFile(off_t size) 328 { 329 static const char template[] = "/glfw-shared-XXXXXX"; 330 const char* path; 331 char* name; 332 int fd; 333 int ret; 334 335 path = getenv("XDG_RUNTIME_DIR"); 336 if (!path) 337 { 338 errno = ENOENT; 339 return -1; 340 } 341 342 name = calloc(strlen(path) + sizeof(template), 1); 343 strcpy(name, path); 344 strcat(name, template); 345 346 fd = createTmpfileCloexec(name); 347 348 free(name); 349 350 if (fd < 0) 351 return -1; 352 ret = posix_fallocate(fd, 0, size); 353 if (ret != 0) 354 { 355 close(fd); 356 errno = ret; 357 return -1; 358 } 359 return fd; 360 } 361 362 // Translates a GLFW standard cursor to a theme cursor name 363 // 364 static char *translateCursorShape(int shape) 365 { 366 switch (shape) 367 { 368 case GLFW_ARROW_CURSOR: 369 return "left_ptr"; 370 case GLFW_IBEAM_CURSOR: 371 return "xterm"; 372 case GLFW_CROSSHAIR_CURSOR: 373 return "crosshair"; 374 case GLFW_HAND_CURSOR: 375 return "grabbing"; 376 case GLFW_HRESIZE_CURSOR: 377 return "sb_h_double_arrow"; 378 case GLFW_VRESIZE_CURSOR: 379 return "sb_v_double_arrow"; 380 } 381 return NULL; 382 } 383 384 ////////////////////////////////////////////////////////////////////////// 385 ////// GLFW platform API ////// 386 ////////////////////////////////////////////////////////////////////////// 387 388 int _glfwPlatformCreateWindow(_GLFWwindow* window, 389 const _GLFWwndconfig* wndconfig, 390 const _GLFWctxconfig* ctxconfig, 391 const _GLFWfbconfig* fbconfig) 392 { 393 if (!createSurface(window, wndconfig)) 394 return GLFW_FALSE; 395 396 if (ctxconfig->client != GLFW_NO_API) 397 { 398 if (ctxconfig->source == GLFW_EGL_CONTEXT_API || 399 ctxconfig->source == GLFW_NATIVE_CONTEXT_API) 400 { 401 if (!_glfwInitEGL()) 402 return GLFW_FALSE; 403 if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) 404 return GLFW_FALSE; 405 } 406 else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) 407 { 408 if (!_glfwInitOSMesa()) 409 return GLFW_FALSE; 410 if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) 411 return GLFW_FALSE; 412 } 413 } 414 415 if (wndconfig->title) 416 window->wl.title = strdup(wndconfig->title); 417 418 if (wndconfig->visible) 419 { 420 if (!createShellSurface(window)) 421 return GLFW_FALSE; 422 423 window->wl.visible = GLFW_TRUE; 424 } 425 else 426 { 427 window->wl.shellSurface = NULL; 428 window->wl.visible = GLFW_FALSE; 429 } 430 431 window->wl.currentCursor = NULL; 432 433 window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*)); 434 window->wl.monitorsCount = 0; 435 window->wl.monitorsSize = 1; 436 437 return GLFW_TRUE; 438 } 439 440 void _glfwPlatformDestroyWindow(_GLFWwindow* window) 441 { 442 if (window == _glfw.wl.pointerFocus) 443 { 444 _glfw.wl.pointerFocus = NULL; 445 _glfwInputCursorEnter(window, GLFW_FALSE); 446 } 447 if (window == _glfw.wl.keyboardFocus) 448 { 449 _glfw.wl.keyboardFocus = NULL; 450 _glfwInputWindowFocus(window, GLFW_FALSE); 451 } 452 453 if (window->context.destroy) 454 window->context.destroy(window); 455 456 if (window->wl.native) 457 wl_egl_window_destroy(window->wl.native); 458 459 if (window->wl.shellSurface) 460 wl_shell_surface_destroy(window->wl.shellSurface); 461 462 if (window->wl.surface) 463 wl_surface_destroy(window->wl.surface); 464 465 free(window->wl.title); 466 free(window->wl.monitors); 467 } 468 469 void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) 470 { 471 if (window->wl.title) 472 free(window->wl.title); 473 window->wl.title = strdup(title); 474 if (window->wl.shellSurface) 475 wl_shell_surface_set_title(window->wl.shellSurface, title); 476 } 477 478 void _glfwPlatformSetWindowIcon(_GLFWwindow* window, 479 int count, const GLFWimage* images) 480 { 481 _glfwInputError(GLFW_PLATFORM_ERROR, 482 "Wayland: Setting window icon not supported"); 483 } 484 485 void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) 486 { 487 // A Wayland client is not aware of its position, so just warn and leave it 488 // as (0, 0) 489 490 _glfwInputError(GLFW_PLATFORM_ERROR, 491 "Wayland: Window position retrieval not supported"); 492 } 493 494 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) 495 { 496 // A Wayland client can not set its position, so just warn 497 498 _glfwInputError(GLFW_PLATFORM_ERROR, 499 "Wayland: Window position setting not supported"); 500 } 501 502 void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) 503 { 504 if (width) 505 *width = window->wl.width; 506 if (height) 507 *height = window->wl.height; 508 } 509 510 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) 511 { 512 int scaledWidth = width * window->wl.scale; 513 int scaledHeight = height * window->wl.scale; 514 window->wl.width = width; 515 window->wl.height = height; 516 wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0); 517 setOpaqueRegion(window); 518 _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); 519 } 520 521 void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, 522 int minwidth, int minheight, 523 int maxwidth, int maxheight) 524 { 525 // TODO: find out how to trigger a resize. 526 // The actual limits are checked in the wl_shell_surface::configure handler. 527 } 528 529 void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) 530 { 531 // TODO: find out how to trigger a resize. 532 // The actual limits are checked in the wl_shell_surface::configure handler. 533 } 534 535 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) 536 { 537 _glfwPlatformGetWindowSize(window, width, height); 538 *width *= window->wl.scale; 539 *height *= window->wl.scale; 540 } 541 542 void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, 543 int* left, int* top, 544 int* right, int* bottom) 545 { 546 // TODO: will need a proper implementation once decorations are 547 // implemented, but for now just leave everything as 0. 548 } 549 550 void _glfwPlatformIconifyWindow(_GLFWwindow* window) 551 { 552 // TODO: move to xdg_shell instead of wl_shell. 553 _glfwInputError(GLFW_PLATFORM_ERROR, 554 "Wayland: Iconify window not supported"); 555 } 556 557 void _glfwPlatformRestoreWindow(_GLFWwindow* window) 558 { 559 // TODO: also do the same for iconified. 560 if (window->monitor || window->wl.maximized) 561 { 562 if (window->wl.shellSurface) 563 wl_shell_surface_set_toplevel(window->wl.shellSurface); 564 565 window->wl.maximized = GLFW_FALSE; 566 } 567 } 568 569 void _glfwPlatformMaximizeWindow(_GLFWwindow* window) 570 { 571 if (!window->monitor && !window->wl.maximized) 572 { 573 if (window->wl.shellSurface) 574 { 575 // Let the compositor select the best output. 576 wl_shell_surface_set_maximized(window->wl.shellSurface, NULL); 577 } 578 window->wl.maximized = GLFW_TRUE; 579 } 580 } 581 582 void _glfwPlatformShowWindow(_GLFWwindow* window) 583 { 584 if (!window->monitor) 585 { 586 if (!window->wl.shellSurface) 587 createShellSurface(window); 588 window->wl.visible = GLFW_TRUE; 589 } 590 } 591 592 void _glfwPlatformHideWindow(_GLFWwindow* window) 593 { 594 if (!window->monitor) 595 { 596 if (window->wl.shellSurface) 597 wl_shell_surface_destroy(window->wl.shellSurface); 598 window->wl.visible = GLFW_FALSE; 599 } 600 } 601 602 void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) 603 { 604 // TODO 605 _glfwInputError(GLFW_PLATFORM_ERROR, 606 "Wayland: Window attention request not implemented yet"); 607 } 608 609 void _glfwPlatformFocusWindow(_GLFWwindow* window) 610 { 611 _glfwInputError(GLFW_PLATFORM_ERROR, 612 "Wayland: Focusing a window requires user interaction"); 613 } 614 615 void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, 616 _GLFWmonitor* monitor, 617 int xpos, int ypos, 618 int width, int height, 619 int refreshRate) 620 { 621 if (monitor) 622 { 623 wl_shell_surface_set_fullscreen( 624 window->wl.shellSurface, 625 WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 626 refreshRate * 1000, // Convert Hz to mHz. 627 monitor->wl.output); 628 } 629 else 630 { 631 wl_shell_surface_set_toplevel(window->wl.shellSurface); 632 } 633 _glfwInputWindowMonitorChange(window, monitor); 634 } 635 636 int _glfwPlatformWindowFocused(_GLFWwindow* window) 637 { 638 return _glfw.wl.keyboardFocus == window; 639 } 640 641 int _glfwPlatformWindowIconified(_GLFWwindow* window) 642 { 643 // TODO: move to xdg_shell, wl_shell doesn't have any iconified concept. 644 return GLFW_FALSE; 645 } 646 647 int _glfwPlatformWindowVisible(_GLFWwindow* window) 648 { 649 return window->wl.visible; 650 } 651 652 int _glfwPlatformWindowMaximized(_GLFWwindow* window) 653 { 654 return window->wl.maximized; 655 } 656 657 void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) 658 { 659 // TODO 660 _glfwInputError(GLFW_PLATFORM_ERROR, 661 "Wayland: Window attribute setting not implemented yet"); 662 } 663 664 void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) 665 { 666 // TODO 667 _glfwInputError(GLFW_PLATFORM_ERROR, 668 "Wayland: Window attribute setting not implemented yet"); 669 } 670 671 void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) 672 { 673 // TODO 674 _glfwInputError(GLFW_PLATFORM_ERROR, 675 "Wayland: Window attribute setting not implemented yet"); 676 } 677 678 void _glfwPlatformPollEvents(void) 679 { 680 handleEvents(0); 681 } 682 683 void _glfwPlatformWaitEvents(void) 684 { 685 handleEvents(-1); 686 } 687 688 void _glfwPlatformWaitEventsTimeout(double timeout) 689 { 690 handleEvents((int) (timeout * 1e3)); 691 } 692 693 void _glfwPlatformPostEmptyEvent(void) 694 { 695 wl_display_sync(_glfw.wl.display); 696 } 697 698 void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) 699 { 700 if (xpos) 701 *xpos = window->wl.cursorPosX; 702 if (ypos) 703 *ypos = window->wl.cursorPosY; 704 } 705 706 static GLFWbool isPointerLocked(_GLFWwindow* window); 707 708 void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) 709 { 710 if (isPointerLocked(window)) 711 { 712 zwp_locked_pointer_v1_set_cursor_position_hint( 713 window->wl.pointerLock.lockedPointer, 714 wl_fixed_from_double(x), wl_fixed_from_double(y)); 715 wl_surface_commit(window->wl.surface); 716 } 717 } 718 719 void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) 720 { 721 _glfwPlatformSetCursor(window, window->wl.currentCursor); 722 } 723 724 const char* _glfwPlatformGetScancodeName(int scancode) 725 { 726 // TODO 727 return NULL; 728 } 729 730 int _glfwPlatformGetKeyScancode(int key) 731 { 732 return _glfw.wl.scancodes[key]; 733 } 734 735 int _glfwPlatformCreateCursor(_GLFWcursor* cursor, 736 const GLFWimage* image, 737 int xhot, int yhot) 738 { 739 struct wl_shm_pool* pool; 740 int stride = image->width * 4; 741 int length = image->width * image->height * 4; 742 void* data; 743 int fd, i; 744 745 fd = createAnonymousFile(length); 746 if (fd < 0) 747 { 748 _glfwInputError(GLFW_PLATFORM_ERROR, 749 "Wayland: Creating a buffer file for %d B failed: %m", 750 length); 751 return GLFW_FALSE; 752 } 753 754 data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 755 if (data == MAP_FAILED) 756 { 757 _glfwInputError(GLFW_PLATFORM_ERROR, 758 "Wayland: Cursor mmap failed: %m"); 759 close(fd); 760 return GLFW_FALSE; 761 } 762 763 pool = wl_shm_create_pool(_glfw.wl.shm, fd, length); 764 765 close(fd); 766 unsigned char* source = (unsigned char*) image->pixels; 767 unsigned char* target = data; 768 for (i = 0; i < image->width * image->height; i++, source += 4) 769 { 770 unsigned int alpha = source[3]; 771 772 *target++ = (unsigned char) ((source[2] * alpha) / 255); 773 *target++ = (unsigned char) ((source[1] * alpha) / 255); 774 *target++ = (unsigned char) ((source[0] * alpha) / 255); 775 *target++ = (unsigned char) alpha; 776 } 777 778 cursor->wl.buffer = 779 wl_shm_pool_create_buffer(pool, 0, 780 image->width, 781 image->height, 782 stride, WL_SHM_FORMAT_ARGB8888); 783 munmap(data, length); 784 wl_shm_pool_destroy(pool); 785 786 cursor->wl.width = image->width; 787 cursor->wl.height = image->height; 788 cursor->wl.xhot = xhot; 789 cursor->wl.yhot = yhot; 790 return GLFW_TRUE; 791 } 792 793 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) 794 { 795 struct wl_cursor* standardCursor; 796 797 standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, 798 translateCursorShape(shape)); 799 if (!standardCursor) 800 { 801 _glfwInputError(GLFW_PLATFORM_ERROR, 802 "Wayland: Standard cursor \"%s\" not found", 803 translateCursorShape(shape)); 804 return GLFW_FALSE; 805 } 806 807 cursor->wl.image = standardCursor->images[0]; 808 return GLFW_TRUE; 809 } 810 811 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) 812 { 813 // If it's a standard cursor we don't need to do anything here 814 if (cursor->wl.image) 815 return; 816 817 if (cursor->wl.buffer) 818 wl_buffer_destroy(cursor->wl.buffer); 819 } 820 821 static void handleRelativeMotion(void* data, 822 struct zwp_relative_pointer_v1* pointer, 823 uint32_t timeHi, 824 uint32_t timeLo, 825 wl_fixed_t dx, 826 wl_fixed_t dy, 827 wl_fixed_t dxUnaccel, 828 wl_fixed_t dyUnaccel) 829 { 830 _GLFWwindow* window = data; 831 832 if (window->cursorMode != GLFW_CURSOR_DISABLED) 833 return; 834 835 _glfwInputCursorPos(window, 836 window->virtualCursorPosX + wl_fixed_to_double(dxUnaccel), 837 window->virtualCursorPosY + wl_fixed_to_double(dyUnaccel)); 838 } 839 840 static const struct zwp_relative_pointer_v1_listener relativePointerListener = { 841 handleRelativeMotion 842 }; 843 844 static void handleLocked(void* data, 845 struct zwp_locked_pointer_v1* lockedPointer) 846 { 847 } 848 849 static void unlockPointer(_GLFWwindow* window) 850 { 851 struct zwp_relative_pointer_v1* relativePointer = 852 window->wl.pointerLock.relativePointer; 853 struct zwp_locked_pointer_v1* lockedPointer = 854 window->wl.pointerLock.lockedPointer; 855 856 zwp_relative_pointer_v1_destroy(relativePointer); 857 zwp_locked_pointer_v1_destroy(lockedPointer); 858 859 window->wl.pointerLock.relativePointer = NULL; 860 window->wl.pointerLock.lockedPointer = NULL; 861 } 862 863 static void lockPointer(_GLFWwindow* window); 864 865 static void handleUnlocked(void* data, 866 struct zwp_locked_pointer_v1* lockedPointer) 867 { 868 } 869 870 static const struct zwp_locked_pointer_v1_listener lockedPointerListener = { 871 handleLocked, 872 handleUnlocked 873 }; 874 875 static void lockPointer(_GLFWwindow* window) 876 { 877 struct zwp_relative_pointer_v1* relativePointer; 878 struct zwp_locked_pointer_v1* lockedPointer; 879 880 if (!_glfw.wl.relativePointerManager) 881 { 882 _glfwInputError(GLFW_PLATFORM_ERROR, 883 "Wayland: no relative pointer manager"); 884 return; 885 } 886 887 relativePointer = 888 zwp_relative_pointer_manager_v1_get_relative_pointer( 889 _glfw.wl.relativePointerManager, 890 _glfw.wl.pointer); 891 zwp_relative_pointer_v1_add_listener(relativePointer, 892 &relativePointerListener, 893 window); 894 895 lockedPointer = 896 zwp_pointer_constraints_v1_lock_pointer( 897 _glfw.wl.pointerConstraints, 898 window->wl.surface, 899 _glfw.wl.pointer, 900 NULL, 901 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); 902 zwp_locked_pointer_v1_add_listener(lockedPointer, 903 &lockedPointerListener, 904 window); 905 906 window->wl.pointerLock.relativePointer = relativePointer; 907 window->wl.pointerLock.lockedPointer = lockedPointer; 908 909 wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, 910 NULL, 0, 0); 911 } 912 913 static GLFWbool isPointerLocked(_GLFWwindow* window) 914 { 915 return window->wl.pointerLock.lockedPointer != NULL; 916 } 917 918 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) 919 { 920 struct wl_buffer* buffer; 921 struct wl_cursor* defaultCursor; 922 struct wl_cursor_image* image; 923 struct wl_surface* surface = _glfw.wl.cursorSurface; 924 925 if (!_glfw.wl.pointer) 926 return; 927 928 window->wl.currentCursor = cursor; 929 930 // If we're not in the correct window just save the cursor 931 // the next time the pointer enters the window the cursor will change 932 if (window != _glfw.wl.pointerFocus) 933 return; 934 935 // Unlock possible pointer lock if no longer disabled. 936 if (window->cursorMode != GLFW_CURSOR_DISABLED && isPointerLocked(window)) 937 unlockPointer(window); 938 939 if (window->cursorMode == GLFW_CURSOR_NORMAL) 940 { 941 if (cursor) 942 image = cursor->wl.image; 943 else 944 { 945 defaultCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, 946 "left_ptr"); 947 if (!defaultCursor) 948 { 949 _glfwInputError(GLFW_PLATFORM_ERROR, 950 "Wayland: Standard cursor not found"); 951 return; 952 } 953 image = defaultCursor->images[0]; 954 } 955 956 if (image) 957 { 958 buffer = wl_cursor_image_get_buffer(image); 959 if (!buffer) 960 return; 961 wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, 962 surface, 963 image->hotspot_x, 964 image->hotspot_y); 965 wl_surface_attach(surface, buffer, 0, 0); 966 wl_surface_damage(surface, 0, 0, 967 image->width, image->height); 968 wl_surface_commit(surface); 969 } 970 else 971 { 972 wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, 973 surface, 974 cursor->wl.xhot, 975 cursor->wl.yhot); 976 wl_surface_attach(surface, cursor->wl.buffer, 0, 0); 977 wl_surface_damage(surface, 0, 0, 978 cursor->wl.width, cursor->wl.height); 979 wl_surface_commit(surface); 980 } 981 } 982 else if (window->cursorMode == GLFW_CURSOR_DISABLED) 983 { 984 if (!isPointerLocked(window)) 985 lockPointer(window); 986 } 987 else if (window->cursorMode == GLFW_CURSOR_HIDDEN) 988 { 989 wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, 990 NULL, 0, 0); 991 } 992 } 993 994 void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) 995 { 996 // TODO 997 _glfwInputError(GLFW_PLATFORM_ERROR, 998 "Wayland: Clipboard setting not implemented yet"); 999 } 1000 1001 const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) 1002 { 1003 // TODO 1004 _glfwInputError(GLFW_PLATFORM_ERROR, 1005 "Wayland: Clipboard getting not implemented yet"); 1006 return NULL; 1007 } 1008 1009 void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) 1010 { 1011 if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_wayland_surface) 1012 return; 1013 1014 extensions[0] = "VK_KHR_surface"; 1015 extensions[1] = "VK_KHR_wayland_surface"; 1016 } 1017 1018 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, 1019 VkPhysicalDevice device, 1020 uint32_t queuefamily) 1021 { 1022 PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR = 1023 (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR) 1024 vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR"); 1025 if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR) 1026 { 1027 _glfwInputError(GLFW_API_UNAVAILABLE, 1028 "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension"); 1029 return VK_NULL_HANDLE; 1030 } 1031 1032 return vkGetPhysicalDeviceWaylandPresentationSupportKHR(device, 1033 queuefamily, 1034 _glfw.wl.display); 1035 } 1036 1037 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, 1038 _GLFWwindow* window, 1039 const VkAllocationCallbacks* allocator, 1040 VkSurfaceKHR* surface) 1041 { 1042 VkResult err; 1043 VkWaylandSurfaceCreateInfoKHR sci; 1044 PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR; 1045 1046 vkCreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR) 1047 vkGetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR"); 1048 if (!vkCreateWaylandSurfaceKHR) 1049 { 1050 _glfwInputError(GLFW_API_UNAVAILABLE, 1051 "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension"); 1052 return VK_ERROR_EXTENSION_NOT_PRESENT; 1053 } 1054 1055 memset(&sci, 0, sizeof(sci)); 1056 sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; 1057 sci.display = _glfw.wl.display; 1058 sci.surface = window->wl.surface; 1059 1060 err = vkCreateWaylandSurfaceKHR(instance, &sci, allocator, surface); 1061 if (err) 1062 { 1063 _glfwInputError(GLFW_PLATFORM_ERROR, 1064 "Wayland: Failed to create Vulkan surface: %s", 1065 _glfwGetVulkanResultString(err)); 1066 } 1067 1068 return err; 1069 } 1070 1071 1072 ////////////////////////////////////////////////////////////////////////// 1073 ////// GLFW native API ////// 1074 ////////////////////////////////////////////////////////////////////////// 1075 1076 GLFWAPI struct wl_display* glfwGetWaylandDisplay(void) 1077 { 1078 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 1079 return _glfw.wl.display; 1080 } 1081 1082 GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle) 1083 { 1084 _GLFWwindow* window = (_GLFWwindow*) handle; 1085 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 1086 return window->wl.surface; 1087 } 1088