Atlas - wl_window.c

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