ScrapExplorer - wl_window.c

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