Atlas - wl_window.c

Home / ext / glfw / src Lines: 1 | Size: 104532 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 { 1697 close(fd); 1698 return; 1699 } 1700 1701 keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context, 1702 mapStr, 1703 XKB_KEYMAP_FORMAT_TEXT_V1, 1704 0); 1705 munmap(mapStr, size); 1706 close(fd); 1707 1708 if (!keymap) 1709 { 1710 _glfwInputError(GLFW_PLATFORM_ERROR, 1711 "Wayland: Failed to compile keymap"); 1712 return; 1713 } 1714 1715 state = xkb_state_new(keymap); 1716 if (!state) 1717 { 1718 _glfwInputError(GLFW_PLATFORM_ERROR, 1719 "Wayland: Failed to create XKB state"); 1720 xkb_keymap_unref(keymap); 1721 return; 1722 } 1723 1724 // Look up the preferred locale, falling back to "C" as default. 1725 locale = getenv("LC_ALL"); 1726 if (!locale) 1727 locale = getenv("LC_CTYPE"); 1728 if (!locale) 1729 locale = getenv("LANG"); 1730 if (!locale) 1731 locale = "C"; 1732 1733 composeTable = 1734 xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale, 1735 XKB_COMPOSE_COMPILE_NO_FLAGS); 1736 if (composeTable) 1737 { 1738 composeState = 1739 xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS); 1740 xkb_compose_table_unref(composeTable); 1741 if (composeState) 1742 _glfw.wl.xkb.composeState = composeState; 1743 else 1744 _glfwInputError(GLFW_PLATFORM_ERROR, 1745 "Wayland: Failed to create XKB compose state"); 1746 } 1747 else 1748 { 1749 _glfwInputError(GLFW_PLATFORM_ERROR, 1750 "Wayland: Failed to create XKB compose table"); 1751 } 1752 1753 xkb_keymap_unref(_glfw.wl.xkb.keymap); 1754 xkb_state_unref(_glfw.wl.xkb.state); 1755 _glfw.wl.xkb.keymap = keymap; 1756 _glfw.wl.xkb.state = state; 1757 1758 _glfw.wl.xkb.controlIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control"); 1759 _glfw.wl.xkb.altIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1"); 1760 _glfw.wl.xkb.shiftIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift"); 1761 _glfw.wl.xkb.superIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4"); 1762 _glfw.wl.xkb.capsLockIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Lock"); 1763 _glfw.wl.xkb.numLockIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2"); 1764} 1765 1766static void keyboardHandleEnter(void* userData, 1767 struct wl_keyboard* keyboard, 1768 uint32_t serial, 1769 struct wl_surface* surface, 1770 struct wl_array* keys) 1771{ 1772 // Happens in the case we just destroyed the surface. 1773 if (!surface) 1774 return; 1775 1776 if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag) 1777 return; 1778 1779 _GLFWwindow* window = wl_surface_get_user_data(surface); 1780 if (surface != window->wl.surface) 1781 return; 1782 1783 _glfw.wl.serial = serial; 1784 _glfw.wl.keyboardFocus = window; 1785 _glfwInputWindowFocus(window, GLFW_TRUE); 1786} 1787 1788static void keyboardHandleLeave(void* userData, 1789 struct wl_keyboard* keyboard, 1790 uint32_t serial, 1791 struct wl_surface* surface) 1792{ 1793 _GLFWwindow* window = _glfw.wl.keyboardFocus; 1794 1795 if (!window) 1796 return; 1797 1798 struct itimerspec timer = {0}; 1799 timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL); 1800 1801 _glfw.wl.serial = serial; 1802 _glfw.wl.keyboardFocus = NULL; 1803 _glfwInputWindowFocus(window, GLFW_FALSE); 1804} 1805 1806static void keyboardHandleKey(void* userData, 1807 struct wl_keyboard* keyboard, 1808 uint32_t serial, 1809 uint32_t time, 1810 uint32_t scancode, 1811 uint32_t state) 1812{ 1813 _GLFWwindow* window = _glfw.wl.keyboardFocus; 1814 if (!window) 1815 return; 1816 1817 const int key = translateKey(scancode); 1818 const int action = 1819 state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE; 1820 1821 _glfw.wl.serial = serial; 1822 1823 struct itimerspec timer = {0}; 1824 1825 if (action == GLFW_PRESS) 1826 { 1827 const xkb_keycode_t keycode = scancode + 8; 1828 1829 if (xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, keycode) && 1830 _glfw.wl.keyRepeatRate > 0) 1831 { 1832 _glfw.wl.keyRepeatScancode = scancode; 1833 if (_glfw.wl.keyRepeatRate > 1) 1834 timer.it_interval.tv_nsec = 1000000000 / _glfw.wl.keyRepeatRate; 1835 else 1836 timer.it_interval.tv_sec = 1; 1837 1838 timer.it_value.tv_sec = _glfw.wl.keyRepeatDelay / 1000; 1839 timer.it_value.tv_nsec = (_glfw.wl.keyRepeatDelay % 1000) * 1000000; 1840 timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL); 1841 } 1842 } 1843 else if (scancode == _glfw.wl.keyRepeatScancode) 1844 { 1845 timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL); 1846 } 1847 1848 _glfwInputKey(window, key, scancode, action, _glfw.wl.xkb.modifiers); 1849 1850 if (action == GLFW_PRESS) 1851 inputText(window, scancode); 1852} 1853 1854static void keyboardHandleModifiers(void* userData, 1855 struct wl_keyboard* keyboard, 1856 uint32_t serial, 1857 uint32_t modsDepressed, 1858 uint32_t modsLatched, 1859 uint32_t modsLocked, 1860 uint32_t group) 1861{ 1862 _glfw.wl.serial = serial; 1863 1864 if (!_glfw.wl.xkb.keymap) 1865 return; 1866 1867 xkb_state_update_mask(_glfw.wl.xkb.state, 1868 modsDepressed, 1869 modsLatched, 1870 modsLocked, 1871 0, 1872 0, 1873 group); 1874 1875 _glfw.wl.xkb.modifiers = 0; 1876 1877 struct 1878 { 1879 xkb_mod_index_t index; 1880 unsigned int bit; 1881 } modifiers[] = 1882 { 1883 { _glfw.wl.xkb.controlIndex, GLFW_MOD_CONTROL }, 1884 { _glfw.wl.xkb.altIndex, GLFW_MOD_ALT }, 1885 { _glfw.wl.xkb.shiftIndex, GLFW_MOD_SHIFT }, 1886 { _glfw.wl.xkb.superIndex, GLFW_MOD_SUPER }, 1887 { _glfw.wl.xkb.capsLockIndex, GLFW_MOD_CAPS_LOCK }, 1888 { _glfw.wl.xkb.numLockIndex, GLFW_MOD_NUM_LOCK } 1889 }; 1890 1891 for (size_t i = 0; i < sizeof(modifiers) / sizeof(modifiers[0]); i++) 1892 { 1893 if (xkb_state_mod_index_is_active(_glfw.wl.xkb.state, 1894 modifiers[i].index, 1895 XKB_STATE_MODS_EFFECTIVE) == 1) 1896 { 1897 _glfw.wl.xkb.modifiers |= modifiers[i].bit; 1898 } 1899 } 1900} 1901 1902static void keyboardHandleRepeatInfo(void* userData, 1903 struct wl_keyboard* keyboard, 1904 int32_t rate, 1905 int32_t delay) 1906{ 1907 if (keyboard != _glfw.wl.keyboard) 1908 return; 1909 1910 _glfw.wl.keyRepeatRate = rate; 1911 _glfw.wl.keyRepeatDelay = delay; 1912} 1913 1914static const struct wl_keyboard_listener keyboardListener = 1915{ 1916 keyboardHandleKeymap, 1917 keyboardHandleEnter, 1918 keyboardHandleLeave, 1919 keyboardHandleKey, 1920 keyboardHandleModifiers, 1921 keyboardHandleRepeatInfo, 1922}; 1923 1924static void seatHandleCapabilities(void* userData, 1925 struct wl_seat* seat, 1926 enum wl_seat_capability caps) 1927{ 1928 if ((caps & WL_SEAT_CAPABILITY_POINTER) && !_glfw.wl.pointer) 1929 { 1930 _glfw.wl.pointer = wl_seat_get_pointer(seat); 1931 wl_pointer_add_listener(_glfw.wl.pointer, &pointerListener, NULL); 1932 } 1933 else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer) 1934 { 1935 wl_pointer_destroy(_glfw.wl.pointer); 1936 _glfw.wl.pointer = NULL; 1937 } 1938 1939 if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard) 1940 { 1941 _glfw.wl.keyboard = wl_seat_get_keyboard(seat); 1942 wl_keyboard_add_listener(_glfw.wl.keyboard, &keyboardListener, NULL); 1943 } 1944 else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard) 1945 { 1946 wl_keyboard_destroy(_glfw.wl.keyboard); 1947 _glfw.wl.keyboard = NULL; 1948 } 1949} 1950 1951static void seatHandleName(void* userData, 1952 struct wl_seat* seat, 1953 const char* name) 1954{ 1955} 1956 1957static const struct wl_seat_listener seatListener = 1958{ 1959 seatHandleCapabilities, 1960 seatHandleName, 1961}; 1962 1963static void dataOfferHandleOffer(void* userData, 1964 struct wl_data_offer* offer, 1965 const char* mimeType) 1966{ 1967 for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) 1968 { 1969 if (_glfw.wl.offers[i].offer == offer) 1970 { 1971 if (strcmp(mimeType, "text/plain;charset=utf-8") == 0) 1972 _glfw.wl.offers[i].text_plain_utf8 = GLFW_TRUE; 1973 else if (strcmp(mimeType, "text/uri-list") == 0) 1974 _glfw.wl.offers[i].text_uri_list = GLFW_TRUE; 1975 1976 break; 1977 } 1978 } 1979} 1980 1981static const struct wl_data_offer_listener dataOfferListener = 1982{ 1983 dataOfferHandleOffer 1984}; 1985 1986static void dataDeviceHandleDataOffer(void* userData, 1987 struct wl_data_device* device, 1988 struct wl_data_offer* offer) 1989{ 1990 _GLFWofferWayland* offers = 1991 _glfw_realloc(_glfw.wl.offers, 1992 sizeof(_GLFWofferWayland) * (_glfw.wl.offerCount + 1)); 1993 if (!offers) 1994 { 1995 _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); 1996 return; 1997 } 1998 1999 _glfw.wl.offers = offers; 2000 _glfw.wl.offerCount++; 2001 2002 _glfw.wl.offers[_glfw.wl.offerCount - 1] = (_GLFWofferWayland) { offer }; 2003 wl_data_offer_add_listener(offer, &dataOfferListener, NULL); 2004} 2005 2006static void dataDeviceHandleEnter(void* userData, 2007 struct wl_data_device* device, 2008 uint32_t serial, 2009 struct wl_surface* surface, 2010 wl_fixed_t x, 2011 wl_fixed_t y, 2012 struct wl_data_offer* offer) 2013{ 2014 if (_glfw.wl.dragOffer) 2015 { 2016 wl_data_offer_destroy(_glfw.wl.dragOffer); 2017 _glfw.wl.dragOffer = NULL; 2018 _glfw.wl.dragFocus = NULL; 2019 } 2020 2021 unsigned int i; 2022 2023 for (i = 0; i < _glfw.wl.offerCount; i++) 2024 { 2025 if (_glfw.wl.offers[i].offer == offer) 2026 break; 2027 } 2028 2029 if (i == _glfw.wl.offerCount) 2030 return; 2031 2032 if (surface && wl_proxy_get_tag((struct wl_proxy*) surface) == &_glfw.wl.tag) 2033 { 2034 _GLFWwindow* window = wl_surface_get_user_data(surface); 2035 if (window->wl.surface == surface) 2036 { 2037 if (_glfw.wl.offers[i].text_uri_list) 2038 { 2039 _glfw.wl.dragOffer = offer; 2040 _glfw.wl.dragFocus = window; 2041 _glfw.wl.dragSerial = serial; 2042 2043 wl_data_offer_accept(offer, serial, "text/uri-list"); 2044 } 2045 } 2046 } 2047 2048 if (!_glfw.wl.dragOffer) 2049 { 2050 wl_data_offer_accept(offer, serial, NULL); 2051 wl_data_offer_destroy(offer); 2052 } 2053 2054 _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1]; 2055 _glfw.wl.offerCount--; 2056} 2057 2058static void dataDeviceHandleLeave(void* userData, 2059 struct wl_data_device* device) 2060{ 2061 if (_glfw.wl.dragOffer) 2062 { 2063 wl_data_offer_destroy(_glfw.wl.dragOffer); 2064 _glfw.wl.dragOffer = NULL; 2065 _glfw.wl.dragFocus = NULL; 2066 } 2067} 2068 2069static void dataDeviceHandleMotion(void* userData, 2070 struct wl_data_device* device, 2071 uint32_t time, 2072 wl_fixed_t x, 2073 wl_fixed_t y) 2074{ 2075} 2076 2077static void dataDeviceHandleDrop(void* userData, 2078 struct wl_data_device* device) 2079{ 2080 if (!_glfw.wl.dragOffer) 2081 return; 2082 2083 char* string = readDataOfferAsString(_glfw.wl.dragOffer, "text/uri-list"); 2084 if (string) 2085 { 2086 int count; 2087 char** paths = _glfwParseUriList(string, &count); 2088 if (paths) 2089 { 2090 _glfwInputDrop(_glfw.wl.dragFocus, count, (const char**) paths); 2091 2092 for (int i = 0; i < count; i++) 2093 _glfw_free(paths[i]); 2094 2095 _glfw_free(paths); 2096 } 2097 2098 _glfw_free(string); 2099 } 2100} 2101 2102static void dataDeviceHandleSelection(void* userData, 2103 struct wl_data_device* device, 2104 struct wl_data_offer* offer) 2105{ 2106 if (_glfw.wl.selectionOffer) 2107 { 2108 wl_data_offer_destroy(_glfw.wl.selectionOffer); 2109 _glfw.wl.selectionOffer = NULL; 2110 } 2111 2112 for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) 2113 { 2114 if (_glfw.wl.offers[i].offer == offer) 2115 { 2116 if (_glfw.wl.offers[i].text_plain_utf8) 2117 _glfw.wl.selectionOffer = offer; 2118 else 2119 wl_data_offer_destroy(offer); 2120 2121 _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1]; 2122 _glfw.wl.offerCount--; 2123 break; 2124 } 2125 } 2126} 2127 2128const struct wl_data_device_listener dataDeviceListener = 2129{ 2130 dataDeviceHandleDataOffer, 2131 dataDeviceHandleEnter, 2132 dataDeviceHandleLeave, 2133 dataDeviceHandleMotion, 2134 dataDeviceHandleDrop, 2135 dataDeviceHandleSelection, 2136}; 2137 2138static void xdgActivationHandleDone(void* userData, 2139 struct xdg_activation_token_v1* activationToken, 2140 const char* token) 2141{ 2142 _GLFWwindow* window = userData; 2143 2144 if (activationToken != window->wl.activationToken) 2145 return; 2146 2147 xdg_activation_v1_activate(_glfw.wl.activationManager, token, window->wl.surface); 2148 xdg_activation_token_v1_destroy(window->wl.activationToken); 2149 window->wl.activationToken = NULL; 2150} 2151 2152static const struct xdg_activation_token_v1_listener xdgActivationListener = 2153{ 2154 xdgActivationHandleDone 2155}; 2156 2157void _glfwAddSeatListenerWayland(struct wl_seat* seat) 2158{ 2159 wl_seat_add_listener(seat, &seatListener, NULL); 2160} 2161 2162void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device) 2163{ 2164 wl_data_device_add_listener(device, &dataDeviceListener, NULL); 2165} 2166 2167 2168////////////////////////////////////////////////////////////////////////// 2169////// GLFW platform API ////// 2170////////////////////////////////////////////////////////////////////////// 2171 2172GLFWbool _glfwCreateWindowWayland(_GLFWwindow* window, 2173 const _GLFWwndconfig* wndconfig, 2174 const _GLFWctxconfig* ctxconfig, 2175 const _GLFWfbconfig* fbconfig) 2176{ 2177 if (!createNativeSurface(window, wndconfig, fbconfig)) 2178 return GLFW_FALSE; 2179 2180 if (ctxconfig->client != GLFW_NO_API) 2181 { 2182 if (ctxconfig->source == GLFW_EGL_CONTEXT_API || 2183 ctxconfig->source == GLFW_NATIVE_CONTEXT_API) 2184 { 2185 window->wl.egl.window = wl_egl_window_create(window->wl.surface, 2186 window->wl.fbWidth, 2187 window->wl.fbHeight); 2188 if (!window->wl.egl.window) 2189 { 2190 _glfwInputError(GLFW_PLATFORM_ERROR, 2191 "Wayland: Failed to create EGL window"); 2192 return GLFW_FALSE; 2193 } 2194 2195 if (!_glfwInitEGL()) 2196 return GLFW_FALSE; 2197 if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) 2198 return GLFW_FALSE; 2199 } 2200 else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) 2201 { 2202 if (!_glfwInitOSMesa()) 2203 return GLFW_FALSE; 2204 if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) 2205 return GLFW_FALSE; 2206 } 2207 2208 if (!_glfwRefreshContextAttribs(window, ctxconfig)) 2209 return GLFW_FALSE; 2210 } 2211 2212 if (wndconfig->mousePassthrough) 2213 _glfwSetWindowMousePassthroughWayland(window, GLFW_TRUE); 2214 2215 if (window->monitor || wndconfig->visible) 2216 { 2217 if (!createShellObjects(window)) 2218 return GLFW_FALSE; 2219 } 2220 2221 return GLFW_TRUE; 2222} 2223 2224void _glfwDestroyWindowWayland(_GLFWwindow* window) 2225{ 2226 if (window == _glfw.wl.pointerFocus) 2227 _glfw.wl.pointerFocus = NULL; 2228 2229 if (window == _glfw.wl.keyboardFocus) 2230 { 2231 struct itimerspec timer = {0}; 2232 timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL); 2233 2234 _glfw.wl.keyboardFocus = NULL; 2235 } 2236 2237 if (window->wl.fractionalScale) 2238 wp_fractional_scale_v1_destroy(window->wl.fractionalScale); 2239 2240 if (window->wl.scalingViewport) 2241 wp_viewport_destroy(window->wl.scalingViewport); 2242 2243 if (window->wl.activationToken) 2244 xdg_activation_token_v1_destroy(window->wl.activationToken); 2245 2246 if (window->wl.idleInhibitor) 2247 zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor); 2248 2249 if (window->wl.relativePointer) 2250 zwp_relative_pointer_v1_destroy(window->wl.relativePointer); 2251 2252 if (window->wl.lockedPointer) 2253 zwp_locked_pointer_v1_destroy(window->wl.lockedPointer); 2254 2255 if (window->wl.confinedPointer) 2256 zwp_confined_pointer_v1_destroy(window->wl.confinedPointer); 2257 2258 if (window->context.destroy) 2259 window->context.destroy(window); 2260 2261 destroyShellObjects(window); 2262 2263 if (window->wl.fallback.buffer) 2264 wl_buffer_destroy(window->wl.fallback.buffer); 2265 2266 if (window->wl.egl.window) 2267 wl_egl_window_destroy(window->wl.egl.window); 2268 2269 if (window->wl.surface) 2270 wl_surface_destroy(window->wl.surface); 2271 2272 _glfw_free(window->wl.appId); 2273 _glfw_free(window->wl.outputScales); 2274} 2275 2276void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title) 2277{ 2278 if (window->wl.libdecor.frame) 2279 libdecor_frame_set_title(window->wl.libdecor.frame, title); 2280 else if (window->wl.xdg.toplevel) 2281 xdg_toplevel_set_title(window->wl.xdg.toplevel, title); 2282} 2283 2284void _glfwSetWindowIconWayland(_GLFWwindow* window, 2285 int count, const GLFWimage* images) 2286{ 2287 _glfwInputError(GLFW_FEATURE_UNAVAILABLE, 2288 "Wayland: The platform does not support setting the window icon"); 2289} 2290 2291void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos) 2292{ 2293 // A Wayland client is not aware of its position, so just warn and leave it 2294 // as (0, 0) 2295 2296 _glfwInputError(GLFW_FEATURE_UNAVAILABLE, 2297 "Wayland: The platform does not provide the window position"); 2298} 2299 2300void _glfwSetWindowPosWayland(_GLFWwindow* window, int xpos, int ypos) 2301{ 2302 // A Wayland client can not set its position, so just warn 2303 2304 _glfwInputError(GLFW_FEATURE_UNAVAILABLE, 2305 "Wayland: The platform does not support setting the window position"); 2306} 2307 2308void _glfwGetWindowSizeWayland(_GLFWwindow* window, int* width, int* height) 2309{ 2310 if (width) 2311 *width = window->wl.width; 2312 if (height) 2313 *height = window->wl.height; 2314} 2315 2316void _glfwSetWindowSizeWayland(_GLFWwindow* window, int width, int height) 2317{ 2318 if (window->monitor) 2319 { 2320 // Video mode setting is not available on Wayland 2321 } 2322 else 2323 { 2324 if (!resizeWindow(window, width, height)) 2325 return; 2326 2327 if (window->wl.libdecor.frame) 2328 { 2329 struct libdecor_state* frameState = 2330 libdecor_state_new(window->wl.width, window->wl.height); 2331 libdecor_frame_commit(window->wl.libdecor.frame, frameState, NULL); 2332 libdecor_state_free(frameState); 2333 } 2334 2335 if (window->wl.visible) 2336 _glfwInputWindowDamage(window); 2337 } 2338} 2339 2340void _glfwSetWindowSizeLimitsWayland(_GLFWwindow* window, 2341 int minwidth, int minheight, 2342 int maxwidth, int maxheight) 2343{ 2344 if (window->wl.libdecor.frame) 2345 { 2346 if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) 2347 minwidth = minheight = 0; 2348 2349 if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE) 2350 maxwidth = maxheight = 0; 2351 2352 libdecor_frame_set_min_content_size(window->wl.libdecor.frame, 2353 minwidth, minheight); 2354 libdecor_frame_set_max_content_size(window->wl.libdecor.frame, 2355 maxwidth, maxheight); 2356 } 2357 else if (window->wl.xdg.toplevel) 2358 updateXdgSizeLimits(window); 2359} 2360 2361void _glfwSetWindowAspectRatioWayland(_GLFWwindow* window, int numer, int denom) 2362{ 2363 if (window->wl.maximized || window->wl.fullscreen) 2364 return; 2365 2366 int width = window->wl.width, height = window->wl.height; 2367 2368 if (numer != GLFW_DONT_CARE && denom != GLFW_DONT_CARE) 2369 { 2370 const float aspectRatio = (float) width / (float) height; 2371 const float targetRatio = (float) numer / (float) denom; 2372 if (aspectRatio < targetRatio) 2373 height /= targetRatio; 2374 else if (aspectRatio > targetRatio) 2375 width *= targetRatio; 2376 } 2377 2378 if (resizeWindow(window, width, height)) 2379 { 2380 if (window->wl.libdecor.frame) 2381 { 2382 struct libdecor_state* frameState = 2383 libdecor_state_new(window->wl.width, window->wl.height); 2384 libdecor_frame_commit(window->wl.libdecor.frame, frameState, NULL); 2385 libdecor_state_free(frameState); 2386 } 2387 2388 _glfwInputWindowSize(window, window->wl.width, window->wl.height); 2389 2390 if (window->wl.visible) 2391 _glfwInputWindowDamage(window); 2392 } 2393} 2394 2395void _glfwGetFramebufferSizeWayland(_GLFWwindow* window, int* width, int* height) 2396{ 2397 if (width) 2398 *width = window->wl.fbWidth; 2399 if (height) 2400 *height = window->wl.fbHeight; 2401} 2402 2403void _glfwGetWindowFrameSizeWayland(_GLFWwindow* window, 2404 int* left, int* top, 2405 int* right, int* bottom) 2406{ 2407 if (window->wl.fallback.decorations) 2408 { 2409 if (top) 2410 *top = GLFW_CAPTION_HEIGHT; 2411 if (left) 2412 *left = GLFW_BORDER_SIZE; 2413 if (right) 2414 *right = GLFW_BORDER_SIZE; 2415 if (bottom) 2416 *bottom = GLFW_BORDER_SIZE; 2417 } 2418} 2419 2420void _glfwGetWindowContentScaleWayland(_GLFWwindow* window, 2421 float* xscale, float* yscale) 2422{ 2423 if (window->wl.fractionalScale) 2424 { 2425 if (xscale) 2426 *xscale = (float) window->wl.scalingNumerator / 120.f; 2427 if (yscale) 2428 *yscale = (float) window->wl.scalingNumerator / 120.f; 2429 } 2430 else 2431 { 2432 if (xscale) 2433 *xscale = (float) window->wl.bufferScale; 2434 if (yscale) 2435 *yscale = (float) window->wl.bufferScale; 2436 } 2437} 2438 2439void _glfwIconifyWindowWayland(_GLFWwindow* window) 2440{ 2441 if (window->wl.libdecor.frame) 2442 libdecor_frame_set_minimized(window->wl.libdecor.frame); 2443 else if (window->wl.xdg.toplevel) 2444 xdg_toplevel_set_minimized(window->wl.xdg.toplevel); 2445} 2446 2447void _glfwRestoreWindowWayland(_GLFWwindow* window) 2448{ 2449 if (window->monitor) 2450 { 2451 // There is no way to unset minimized, or even to know if we are 2452 // minimized, so there is nothing to do in this case. 2453 } 2454 else 2455 { 2456 // We assume we are not minimized and act only on maximization 2457 2458 if (window->wl.maximized) 2459 { 2460 if (window->wl.libdecor.frame) 2461 libdecor_frame_unset_maximized(window->wl.libdecor.frame); 2462 else if (window->wl.xdg.toplevel) 2463 xdg_toplevel_unset_maximized(window->wl.xdg.toplevel); 2464 else 2465 window->wl.maximized = GLFW_FALSE; 2466 } 2467 } 2468} 2469 2470void _glfwMaximizeWindowWayland(_GLFWwindow* window) 2471{ 2472 if (window->wl.libdecor.frame) 2473 libdecor_frame_set_maximized(window->wl.libdecor.frame); 2474 else if (window->wl.xdg.toplevel) 2475 xdg_toplevel_set_maximized(window->wl.xdg.toplevel); 2476 else 2477 window->wl.maximized = GLFW_TRUE; 2478} 2479 2480void _glfwShowWindowWayland(_GLFWwindow* window) 2481{ 2482 if (!window->wl.libdecor.frame && !window->wl.xdg.toplevel) 2483 { 2484 // NOTE: The XDG surface and role are created here so command-line applications 2485 // with off-screen windows do not appear in for example the Unity dock 2486 createShellObjects(window); 2487 } 2488} 2489 2490void _glfwHideWindowWayland(_GLFWwindow* window) 2491{ 2492 if (window->wl.visible) 2493 { 2494 window->wl.visible = GLFW_FALSE; 2495 destroyShellObjects(window); 2496 2497 wl_surface_attach(window->wl.surface, NULL, 0, 0); 2498 wl_surface_commit(window->wl.surface); 2499 } 2500} 2501 2502void _glfwRequestWindowAttentionWayland(_GLFWwindow* window) 2503{ 2504 if (!_glfw.wl.activationManager) 2505 return; 2506 2507 // We're about to overwrite this with a new request 2508 if (window->wl.activationToken) 2509 xdg_activation_token_v1_destroy(window->wl.activationToken); 2510 2511 window->wl.activationToken = 2512 xdg_activation_v1_get_activation_token(_glfw.wl.activationManager); 2513 xdg_activation_token_v1_add_listener(window->wl.activationToken, 2514 &xdgActivationListener, 2515 window); 2516 2517 xdg_activation_token_v1_commit(window->wl.activationToken); 2518} 2519 2520void _glfwFocusWindowWayland(_GLFWwindow* window) 2521{ 2522 if (!_glfw.wl.activationManager) 2523 return; 2524 2525 if (window->wl.activationToken) 2526 xdg_activation_token_v1_destroy(window->wl.activationToken); 2527 2528 window->wl.activationToken = 2529 xdg_activation_v1_get_activation_token(_glfw.wl.activationManager); 2530 xdg_activation_token_v1_add_listener(window->wl.activationToken, 2531 &xdgActivationListener, 2532 window); 2533 2534 xdg_activation_token_v1_set_serial(window->wl.activationToken, 2535 _glfw.wl.serial, 2536 _glfw.wl.seat); 2537 2538 _GLFWwindow* requester = _glfw.wl.keyboardFocus; 2539 if (requester) 2540 { 2541 xdg_activation_token_v1_set_surface(window->wl.activationToken, 2542 requester->wl.surface); 2543 2544 if (requester->wl.appId) 2545 { 2546 xdg_activation_token_v1_set_app_id(window->wl.activationToken, 2547 requester->wl.appId); 2548 } 2549 } 2550 2551 xdg_activation_token_v1_commit(window->wl.activationToken); 2552} 2553 2554void _glfwSetWindowMonitorWayland(_GLFWwindow* window, 2555 _GLFWmonitor* monitor, 2556 int xpos, int ypos, 2557 int width, int height, 2558 int refreshRate) 2559{ 2560 if (window->monitor == monitor) 2561 { 2562 if (!monitor) 2563 _glfwSetWindowSizeWayland(window, width, height); 2564 2565 return; 2566 } 2567 2568 if (window->monitor) 2569 releaseMonitor(window); 2570 2571 _glfwInputWindowMonitor(window, monitor); 2572 2573 if (window->monitor) 2574 acquireMonitor(window); 2575 else 2576 _glfwSetWindowSizeWayland(window, width, height); 2577} 2578 2579GLFWbool _glfwWindowFocusedWayland(_GLFWwindow* window) 2580{ 2581 return _glfw.wl.keyboardFocus == window; 2582} 2583 2584GLFWbool _glfwWindowIconifiedWayland(_GLFWwindow* window) 2585{ 2586 // xdg-shell doesn’t give any way to request whether a surface is 2587 // iconified. 2588 return GLFW_FALSE; 2589} 2590 2591GLFWbool _glfwWindowVisibleWayland(_GLFWwindow* window) 2592{ 2593 return window->wl.visible; 2594} 2595 2596GLFWbool _glfwWindowMaximizedWayland(_GLFWwindow* window) 2597{ 2598 return window->wl.maximized; 2599} 2600 2601GLFWbool _glfwWindowHoveredWayland(_GLFWwindow* window) 2602{ 2603 return window->wl.hovered; 2604} 2605 2606GLFWbool _glfwFramebufferTransparentWayland(_GLFWwindow* window) 2607{ 2608 return window->wl.transparent; 2609} 2610 2611void _glfwSetWindowResizableWayland(_GLFWwindow* window, GLFWbool enabled) 2612{ 2613 if (window->wl.libdecor.frame) 2614 { 2615 if (enabled) 2616 { 2617 libdecor_frame_set_capabilities(window->wl.libdecor.frame, 2618 LIBDECOR_ACTION_RESIZE); 2619 } 2620 else 2621 { 2622 libdecor_frame_unset_capabilities(window->wl.libdecor.frame, 2623 LIBDECOR_ACTION_RESIZE); 2624 } 2625 } 2626 else if (window->wl.xdg.toplevel) 2627 updateXdgSizeLimits(window); 2628} 2629 2630void _glfwSetWindowDecoratedWayland(_GLFWwindow* window, GLFWbool enabled) 2631{ 2632 if (window->wl.libdecor.frame) 2633 { 2634 libdecor_frame_set_visibility(window->wl.libdecor.frame, enabled); 2635 } 2636 else if (window->wl.xdg.decoration) 2637 { 2638 uint32_t mode; 2639 2640 if (enabled) 2641 mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; 2642 else 2643 mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; 2644 2645 zxdg_toplevel_decoration_v1_set_mode(window->wl.xdg.decoration, mode); 2646 } 2647 else if (window->wl.xdg.toplevel) 2648 { 2649 if (enabled) 2650 createFallbackDecorations(window); 2651 else 2652 destroyFallbackDecorations(window); 2653 } 2654} 2655 2656void _glfwSetWindowFloatingWayland(_GLFWwindow* window, GLFWbool enabled) 2657{ 2658 _glfwInputError(GLFW_FEATURE_UNAVAILABLE, 2659 "Wayland: Platform does not support making a window floating"); 2660} 2661 2662void _glfwSetWindowMousePassthroughWayland(_GLFWwindow* window, GLFWbool enabled) 2663{ 2664 if (enabled) 2665 { 2666 struct wl_region* region = wl_compositor_create_region(_glfw.wl.compositor); 2667 wl_surface_set_input_region(window->wl.surface, region); 2668 wl_region_destroy(region); 2669 } 2670 else 2671 wl_surface_set_input_region(window->wl.surface, NULL); 2672} 2673 2674float _glfwGetWindowOpacityWayland(_GLFWwindow* window) 2675{ 2676 return 1.f; 2677} 2678 2679void _glfwSetWindowOpacityWayland(_GLFWwindow* window, float opacity) 2680{ 2681 _glfwInputError(GLFW_FEATURE_UNAVAILABLE, 2682 "Wayland: The platform does not support setting the window opacity"); 2683} 2684 2685void _glfwSetRawMouseMotionWayland(_GLFWwindow* window, GLFWbool enabled) 2686{ 2687 // This is handled in relativePointerHandleRelativeMotion 2688} 2689 2690GLFWbool _glfwRawMouseMotionSupportedWayland(void) 2691{ 2692 return GLFW_TRUE; 2693} 2694 2695void _glfwPollEventsWayland(void) 2696{ 2697 double timeout = 0.0; 2698 handleEvents(&timeout); 2699} 2700 2701void _glfwWaitEventsWayland(void) 2702{ 2703 handleEvents(NULL); 2704} 2705 2706void _glfwWaitEventsTimeoutWayland(double timeout) 2707{ 2708 handleEvents(&timeout); 2709} 2710 2711void _glfwPostEmptyEventWayland(void) 2712{ 2713 wl_display_sync(_glfw.wl.display); 2714 flushDisplay(); 2715} 2716 2717void _glfwGetCursorPosWayland(_GLFWwindow* window, double* xpos, double* ypos) 2718{ 2719 if (xpos) 2720 *xpos = window->wl.cursorPosX; 2721 if (ypos) 2722 *ypos = window->wl.cursorPosY; 2723} 2724 2725void _glfwSetCursorPosWayland(_GLFWwindow* window, double x, double y) 2726{ 2727 _glfwInputError(GLFW_FEATURE_UNAVAILABLE, 2728 "Wayland: The platform does not support setting the cursor position"); 2729} 2730 2731void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode) 2732{ 2733 _glfwSetCursorWayland(window, window->wl.currentCursor); 2734} 2735 2736const char* _glfwGetScancodeNameWayland(int scancode) 2737{ 2738 if (scancode < 0 || scancode > 255) 2739 { 2740 _glfwInputError(GLFW_INVALID_VALUE, 2741 "Wayland: Invalid scancode %i", 2742 scancode); 2743 return NULL; 2744 } 2745 2746 const int key = _glfw.wl.keycodes[scancode]; 2747 if (key == GLFW_KEY_UNKNOWN) 2748 return NULL; 2749 2750 const xkb_keycode_t keycode = scancode + 8; 2751 const xkb_layout_index_t layout = 2752 xkb_state_key_get_layout(_glfw.wl.xkb.state, keycode); 2753 if (layout == XKB_LAYOUT_INVALID) 2754 { 2755 _glfwInputError(GLFW_PLATFORM_ERROR, 2756 "Wayland: Failed to retrieve layout for key name"); 2757 return NULL; 2758 } 2759 2760 const xkb_keysym_t* keysyms = NULL; 2761 xkb_keymap_key_get_syms_by_level(_glfw.wl.xkb.keymap, 2762 keycode, 2763 layout, 2764 0, 2765 &keysyms); 2766 if (keysyms == NULL) 2767 { 2768 _glfwInputError(GLFW_PLATFORM_ERROR, 2769 "Wayland: Failed to retrieve keysym for key name"); 2770 return NULL; 2771 } 2772 2773 // WORKAROUND: xkb_keysym_to_utf8() requires the third parameter (size of the output buffer) 2774 // to be at least 7 (6 bytes + a null terminator), because it was written when UTF-8 2775 // sequences could be up to 6 bytes long. The _glfw.wl.keynames buffers are only 5 bytes 2776 // long, because UTF-8 sequences are now limited to 4 bytes and no codepoints were ever assigned 2777 // that needed more than that. To work around this, we first copy to a temporary buffer. 2778 // 2779 // See: https://github.com/xkbcommon/libxkbcommon/issues/418 2780 char temp_buffer[7]; 2781 const int bytes_written = xkb_keysym_to_utf8(keysyms[0], temp_buffer, sizeof(temp_buffer)); 2782 if (bytes_written <= 0 || bytes_written > 5) 2783 { 2784 _glfwInputError(GLFW_PLATFORM_ERROR, 2785 "Wayland: Failed to encode keysym as UTF-8"); 2786 return NULL; 2787 } 2788 memcpy(_glfw.wl.keynames[key], temp_buffer, bytes_written); 2789 2790 return _glfw.wl.keynames[key]; 2791} 2792 2793int _glfwGetKeyScancodeWayland(int key) 2794{ 2795 return _glfw.wl.scancodes[key]; 2796} 2797 2798GLFWbool _glfwCreateCursorWayland(_GLFWcursor* cursor, 2799 const GLFWimage* image, 2800 int xhot, int yhot) 2801{ 2802 cursor->wl.buffer = createShmBuffer(image); 2803 if (!cursor->wl.buffer) 2804 return GLFW_FALSE; 2805 2806 cursor->wl.width = image->width; 2807 cursor->wl.height = image->height; 2808 cursor->wl.xhot = xhot; 2809 cursor->wl.yhot = yhot; 2810 return GLFW_TRUE; 2811} 2812 2813GLFWbool _glfwCreateStandardCursorWayland(_GLFWcursor* cursor, int shape) 2814{ 2815 const char* name = NULL; 2816 2817 // Try the XDG names first 2818 switch (shape) 2819 { 2820 case GLFW_ARROW_CURSOR: 2821 name = "default"; 2822 break; 2823 case GLFW_IBEAM_CURSOR: 2824 name = "text"; 2825 break; 2826 case GLFW_CROSSHAIR_CURSOR: 2827 name = "crosshair"; 2828 break; 2829 case GLFW_POINTING_HAND_CURSOR: 2830 name = "pointer"; 2831 break; 2832 case GLFW_RESIZE_EW_CURSOR: 2833 name = "ew-resize"; 2834 break; 2835 case GLFW_RESIZE_NS_CURSOR: 2836 name = "ns-resize"; 2837 break; 2838 case GLFW_RESIZE_NWSE_CURSOR: 2839 name = "nwse-resize"; 2840 break; 2841 case GLFW_RESIZE_NESW_CURSOR: 2842 name = "nesw-resize"; 2843 break; 2844 case GLFW_RESIZE_ALL_CURSOR: 2845 name = "all-scroll"; 2846 break; 2847 case GLFW_NOT_ALLOWED_CURSOR: 2848 name = "not-allowed"; 2849 break; 2850 } 2851 2852 cursor->wl.cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name); 2853 2854 if (_glfw.wl.cursorThemeHiDPI) 2855 { 2856 cursor->wl.cursorHiDPI = 2857 wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, name); 2858 } 2859 2860 if (!cursor->wl.cursor) 2861 { 2862 // Fall back to the core X11 names 2863 switch (shape) 2864 { 2865 case GLFW_ARROW_CURSOR: 2866 name = "left_ptr"; 2867 break; 2868 case GLFW_IBEAM_CURSOR: 2869 name = "xterm"; 2870 break; 2871 case GLFW_CROSSHAIR_CURSOR: 2872 name = "crosshair"; 2873 break; 2874 case GLFW_POINTING_HAND_CURSOR: 2875 name = "hand2"; 2876 break; 2877 case GLFW_RESIZE_EW_CURSOR: 2878 name = "sb_h_double_arrow"; 2879 break; 2880 case GLFW_RESIZE_NS_CURSOR: 2881 name = "sb_v_double_arrow"; 2882 break; 2883 case GLFW_RESIZE_ALL_CURSOR: 2884 name = "fleur"; 2885 break; 2886 default: 2887 _glfwInputError(GLFW_CURSOR_UNAVAILABLE, 2888 "Wayland: Standard cursor shape unavailable"); 2889 return GLFW_FALSE; 2890 } 2891 2892 cursor->wl.cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name); 2893 if (!cursor->wl.cursor) 2894 { 2895 _glfwInputError(GLFW_CURSOR_UNAVAILABLE, 2896 "Wayland: Failed to create standard cursor \"%s\"", 2897 name); 2898 return GLFW_FALSE; 2899 } 2900 2901 if (_glfw.wl.cursorThemeHiDPI) 2902 { 2903 if (!cursor->wl.cursorHiDPI) 2904 { 2905 cursor->wl.cursorHiDPI = 2906 wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, name); 2907 } 2908 } 2909 } 2910 2911 return GLFW_TRUE; 2912} 2913 2914void _glfwDestroyCursorWayland(_GLFWcursor* cursor) 2915{ 2916 // If it's a standard cursor we don't need to do anything here 2917 if (cursor->wl.cursor) 2918 return; 2919 2920 if (cursor->wl.buffer) 2921 wl_buffer_destroy(cursor->wl.buffer); 2922} 2923 2924static void relativePointerHandleRelativeMotion(void* userData, 2925 struct zwp_relative_pointer_v1* pointer, 2926 uint32_t timeHi, 2927 uint32_t timeLo, 2928 wl_fixed_t dx, 2929 wl_fixed_t dy, 2930 wl_fixed_t dxUnaccel, 2931 wl_fixed_t dyUnaccel) 2932{ 2933 _GLFWwindow* window = userData; 2934 double xpos = window->virtualCursorPosX; 2935 double ypos = window->virtualCursorPosY; 2936 2937 if (window->cursorMode != GLFW_CURSOR_DISABLED) 2938 return; 2939 2940 if (window->rawMouseMotion) 2941 { 2942 xpos += wl_fixed_to_double(dxUnaccel); 2943 ypos += wl_fixed_to_double(dyUnaccel); 2944 } 2945 else 2946 { 2947 xpos += wl_fixed_to_double(dx); 2948 ypos += wl_fixed_to_double(dy); 2949 } 2950 2951 _glfwInputCursorPos(window, xpos, ypos); 2952} 2953 2954static const struct zwp_relative_pointer_v1_listener relativePointerListener = 2955{ 2956 relativePointerHandleRelativeMotion 2957}; 2958 2959static void lockedPointerHandleLocked(void* userData, 2960 struct zwp_locked_pointer_v1* lockedPointer) 2961{ 2962} 2963 2964static void lockedPointerHandleUnlocked(void* userData, 2965 struct zwp_locked_pointer_v1* lockedPointer) 2966{ 2967} 2968 2969static const struct zwp_locked_pointer_v1_listener lockedPointerListener = 2970{ 2971 lockedPointerHandleLocked, 2972 lockedPointerHandleUnlocked 2973}; 2974 2975static void lockPointer(_GLFWwindow* window) 2976{ 2977 if (!_glfw.wl.relativePointerManager) 2978 { 2979 _glfwInputError(GLFW_FEATURE_UNAVAILABLE, 2980 "Wayland: The compositor does not support relative pointer motion"); 2981 return; 2982 } 2983 2984 if (!_glfw.wl.pointerConstraints) 2985 { 2986 _glfwInputError(GLFW_FEATURE_UNAVAILABLE, 2987 "Wayland: The compositor does not support locking the pointer"); 2988 } 2989 2990 window->wl.relativePointer = 2991 zwp_relative_pointer_manager_v1_get_relative_pointer( 2992 _glfw.wl.relativePointerManager, 2993 _glfw.wl.pointer); 2994 zwp_relative_pointer_v1_add_listener(window->wl.relativePointer, 2995 &relativePointerListener, 2996 window); 2997 2998 window->wl.lockedPointer = 2999 zwp_pointer_constraints_v1_lock_pointer( 3000 _glfw.wl.pointerConstraints, 3001 window->wl.surface, 3002 _glfw.wl.pointer, 3003 NULL, 3004 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); 3005 zwp_locked_pointer_v1_add_listener(window->wl.lockedPointer, 3006 &lockedPointerListener, 3007 window); 3008} 3009 3010static void unlockPointer(_GLFWwindow* window) 3011{ 3012 zwp_relative_pointer_v1_destroy(window->wl.relativePointer); 3013 window->wl.relativePointer = NULL; 3014 3015 zwp_locked_pointer_v1_destroy(window->wl.lockedPointer); 3016 window->wl.lockedPointer = NULL; 3017} 3018 3019static void confinedPointerHandleConfined(void* userData, 3020 struct zwp_confined_pointer_v1* confinedPointer) 3021{ 3022} 3023 3024static void confinedPointerHandleUnconfined(void* userData, 3025 struct zwp_confined_pointer_v1* confinedPointer) 3026{ 3027} 3028 3029static const struct zwp_confined_pointer_v1_listener confinedPointerListener = 3030{ 3031 confinedPointerHandleConfined, 3032 confinedPointerHandleUnconfined 3033}; 3034 3035static void confinePointer(_GLFWwindow* window) 3036{ 3037 if (!_glfw.wl.pointerConstraints) 3038 { 3039 _glfwInputError(GLFW_FEATURE_UNAVAILABLE, 3040 "Wayland: The compositor does not support confining the pointer"); 3041 } 3042 3043 window->wl.confinedPointer = 3044 zwp_pointer_constraints_v1_confine_pointer( 3045 _glfw.wl.pointerConstraints, 3046 window->wl.surface, 3047 _glfw.wl.pointer, 3048 NULL, 3049 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); 3050 3051 zwp_confined_pointer_v1_add_listener(window->wl.confinedPointer, 3052 &confinedPointerListener, 3053 window); 3054} 3055 3056static void unconfinePointer(_GLFWwindow* window) 3057{ 3058 zwp_confined_pointer_v1_destroy(window->wl.confinedPointer); 3059 window->wl.confinedPointer = NULL; 3060} 3061 3062void _glfwSetCursorWayland(_GLFWwindow* window, _GLFWcursor* cursor) 3063{ 3064 if (!_glfw.wl.pointer) 3065 return; 3066 3067 window->wl.currentCursor = cursor; 3068 3069 // If we're not in the correct window just save the cursor 3070 // the next time the pointer enters the window the cursor will change 3071 if (!window->wl.hovered) 3072 return; 3073 3074 // Update pointer lock to match cursor mode 3075 if (window->cursorMode == GLFW_CURSOR_DISABLED) 3076 { 3077 if (window->wl.confinedPointer) 3078 unconfinePointer(window); 3079 if (!window->wl.lockedPointer) 3080 lockPointer(window); 3081 } 3082 else if (window->cursorMode == GLFW_CURSOR_CAPTURED) 3083 { 3084 if (window->wl.lockedPointer) 3085 unlockPointer(window); 3086 if (!window->wl.confinedPointer) 3087 confinePointer(window); 3088 } 3089 else if (window->cursorMode == GLFW_CURSOR_NORMAL || 3090 window->cursorMode == GLFW_CURSOR_HIDDEN) 3091 { 3092 if (window->wl.lockedPointer) 3093 unlockPointer(window); 3094 else if (window->wl.confinedPointer) 3095 unconfinePointer(window); 3096 } 3097 3098 if (window->cursorMode == GLFW_CURSOR_NORMAL || 3099 window->cursorMode == GLFW_CURSOR_CAPTURED) 3100 { 3101 if (cursor) 3102 setCursorImage(window, &cursor->wl); 3103 else 3104 { 3105 struct wl_cursor* defaultCursor = 3106 wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, "left_ptr"); 3107 if (!defaultCursor) 3108 { 3109 _glfwInputError(GLFW_PLATFORM_ERROR, 3110 "Wayland: Standard cursor not found"); 3111 return; 3112 } 3113 3114 struct wl_cursor* defaultCursorHiDPI = NULL; 3115 if (_glfw.wl.cursorThemeHiDPI) 3116 { 3117 defaultCursorHiDPI = 3118 wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, "left_ptr"); 3119 } 3120 3121 _GLFWcursorWayland cursorWayland = 3122 { 3123 defaultCursor, 3124 defaultCursorHiDPI, 3125 NULL, 3126 0, 0, 3127 0, 0, 3128 0 3129 }; 3130 3131 setCursorImage(window, &cursorWayland); 3132 } 3133 } 3134 else if (window->cursorMode == GLFW_CURSOR_HIDDEN || 3135 window->cursorMode == GLFW_CURSOR_DISABLED) 3136 { 3137 wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial, NULL, 0, 0); 3138 } 3139} 3140 3141static void dataSourceHandleTarget(void* userData, 3142 struct wl_data_source* source, 3143 const char* mimeType) 3144{ 3145 if (_glfw.wl.selectionSource != source) 3146 { 3147 _glfwInputError(GLFW_PLATFORM_ERROR, 3148 "Wayland: Unknown clipboard data source"); 3149 return; 3150 } 3151} 3152 3153static void dataSourceHandleSend(void* userData, 3154 struct wl_data_source* source, 3155 const char* mimeType, 3156 int fd) 3157{ 3158 // Ignore it if this is an outdated or invalid request 3159 if (_glfw.wl.selectionSource != source || 3160 strcmp(mimeType, "text/plain;charset=utf-8") != 0) 3161 { 3162 close(fd); 3163 return; 3164 } 3165 3166 char* string = _glfw.wl.clipboardString; 3167 size_t length = strlen(string); 3168 3169 while (length > 0) 3170 { 3171 const ssize_t result = write(fd, string, length); 3172 if (result == -1) 3173 { 3174 if (errno == EINTR) 3175 continue; 3176 3177 _glfwInputError(GLFW_PLATFORM_ERROR, 3178 "Wayland: Error while writing the clipboard: %s", 3179 strerror(errno)); 3180 break; 3181 } 3182 3183 length -= result; 3184 string += result; 3185 } 3186 3187 close(fd); 3188} 3189 3190static void dataSourceHandleCancelled(void* userData, 3191 struct wl_data_source* source) 3192{ 3193 wl_data_source_destroy(source); 3194 3195 if (_glfw.wl.selectionSource != source) 3196 return; 3197 3198 _glfw.wl.selectionSource = NULL; 3199} 3200 3201static const struct wl_data_source_listener dataSourceListener = 3202{ 3203 dataSourceHandleTarget, 3204 dataSourceHandleSend, 3205 dataSourceHandleCancelled, 3206}; 3207 3208void _glfwSetClipboardStringWayland(const char* string) 3209{ 3210 if (_glfw.wl.selectionSource) 3211 { 3212 wl_data_source_destroy(_glfw.wl.selectionSource); 3213 _glfw.wl.selectionSource = NULL; 3214 } 3215 3216 char* copy = _glfw_strdup(string); 3217 if (!copy) 3218 { 3219 _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); 3220 return; 3221 } 3222 3223 _glfw_free(_glfw.wl.clipboardString); 3224 _glfw.wl.clipboardString = copy; 3225 3226 _glfw.wl.selectionSource = 3227 wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager); 3228 if (!_glfw.wl.selectionSource) 3229 { 3230 _glfwInputError(GLFW_PLATFORM_ERROR, 3231 "Wayland: Failed to create clipboard data source"); 3232 return; 3233 } 3234 wl_data_source_add_listener(_glfw.wl.selectionSource, 3235 &dataSourceListener, 3236 NULL); 3237 wl_data_source_offer(_glfw.wl.selectionSource, "text/plain;charset=utf-8"); 3238 wl_data_device_set_selection(_glfw.wl.dataDevice, 3239 _glfw.wl.selectionSource, 3240 _glfw.wl.serial); 3241} 3242 3243const char* _glfwGetClipboardStringWayland(void) 3244{ 3245 if (!_glfw.wl.selectionOffer) 3246 { 3247 _glfwInputError(GLFW_FORMAT_UNAVAILABLE, 3248 "Wayland: No clipboard data available"); 3249 return NULL; 3250 } 3251 3252 if (_glfw.wl.selectionSource) 3253 return _glfw.wl.clipboardString; 3254 3255 _glfw_free(_glfw.wl.clipboardString); 3256 _glfw.wl.clipboardString = 3257 readDataOfferAsString(_glfw.wl.selectionOffer, "text/plain;charset=utf-8"); 3258 return _glfw.wl.clipboardString; 3259} 3260 3261EGLenum _glfwGetEGLPlatformWayland(EGLint** attribs) 3262{ 3263 if (_glfw.egl.EXT_platform_base && _glfw.egl.EXT_platform_wayland) 3264 return EGL_PLATFORM_WAYLAND_EXT; 3265 else 3266 return 0; 3267} 3268 3269EGLNativeDisplayType _glfwGetEGLNativeDisplayWayland(void) 3270{ 3271 return _glfw.wl.display; 3272} 3273 3274EGLNativeWindowType _glfwGetEGLNativeWindowWayland(_GLFWwindow* window) 3275{ 3276 return window->wl.egl.window; 3277} 3278 3279void _glfwGetRequiredInstanceExtensionsWayland(char** extensions) 3280{ 3281 if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_wayland_surface) 3282 return; 3283 3284 extensions[0] = "VK_KHR_surface"; 3285 extensions[1] = "VK_KHR_wayland_surface"; 3286} 3287 3288GLFWbool _glfwGetPhysicalDevicePresentationSupportWayland(VkInstance instance, 3289 VkPhysicalDevice device, 3290 uint32_t queuefamily) 3291{ 3292 PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR 3293 vkGetPhysicalDeviceWaylandPresentationSupportKHR = 3294 (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR) 3295 vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR"); 3296 if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR) 3297 { 3298 _glfwInputError(GLFW_API_UNAVAILABLE, 3299 "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension"); 3300 return VK_NULL_HANDLE; 3301 } 3302 3303 return vkGetPhysicalDeviceWaylandPresentationSupportKHR(device, 3304 queuefamily, 3305 _glfw.wl.display); 3306} 3307 3308VkResult _glfwCreateWindowSurfaceWayland(VkInstance instance, 3309 _GLFWwindow* window, 3310 const VkAllocationCallbacks* allocator, 3311 VkSurfaceKHR* surface) 3312{ 3313 VkResult err; 3314 VkWaylandSurfaceCreateInfoKHR sci; 3315 PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR; 3316 3317 vkCreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR) 3318 vkGetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR"); 3319 if (!vkCreateWaylandSurfaceKHR) 3320 { 3321 _glfwInputError(GLFW_API_UNAVAILABLE, 3322 "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension"); 3323 return VK_ERROR_EXTENSION_NOT_PRESENT; 3324 } 3325 3326 memset(&sci, 0, sizeof(sci)); 3327 sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; 3328 sci.display = _glfw.wl.display; 3329 sci.surface = window->wl.surface; 3330 3331 err = vkCreateWaylandSurfaceKHR(instance, &sci, allocator, surface); 3332 if (err) 3333 { 3334 _glfwInputError(GLFW_PLATFORM_ERROR, 3335 "Wayland: Failed to create Vulkan surface: %s", 3336 _glfwGetVulkanResultString(err)); 3337 } 3338 3339 return err; 3340} 3341 3342 3343////////////////////////////////////////////////////////////////////////// 3344////// GLFW native API ////// 3345////////////////////////////////////////////////////////////////////////// 3346 3347GLFWAPI struct wl_display* glfwGetWaylandDisplay(void) 3348{ 3349 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 3350 3351 if (_glfw.platform.platformID != GLFW_PLATFORM_WAYLAND) 3352 { 3353 _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, 3354 "Wayland: Platform not initialized"); 3355 return NULL; 3356 } 3357 3358 return _glfw.wl.display; 3359} 3360 3361GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle) 3362{ 3363 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 3364 3365 if (_glfw.platform.platformID != GLFW_PLATFORM_WAYLAND) 3366 { 3367 _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, 3368 "Wayland: Platform not initialized"); 3369 return NULL; 3370 } 3371 3372 _GLFWwindow* window = (_GLFWwindow*) handle; 3373 assert(window != NULL); 3374 3375 return window->wl.surface; 3376} 3377 3378#endif // _GLFW_WAYLAND 3379 3380
[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.