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