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