Atlas - SDL_waylandvideo.c
Home / ext / SDL / src / video / wayland Lines: 2 | Size: 65062 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21 22#include "SDL_internal.h" 23 24#ifdef SDL_VIDEO_DRIVER_WAYLAND 25 26#include "../../core/linux/SDL_system_theme.h" 27#include "../../core/linux/SDL_progressbar.h" 28#include "../../events/SDL_events_c.h" 29 30#include "SDL_waylandclipboard.h" 31#include "SDL_waylandcolor.h" 32#include "SDL_waylandevents_c.h" 33#include "SDL_waylandkeyboard.h" 34#include "SDL_waylandmessagebox.h" 35#include "SDL_waylandmouse.h" 36#include "SDL_waylandopengles.h" 37#include "SDL_waylandvideo.h" 38#include "SDL_waylandvulkan.h" 39#include "SDL_waylandwindow.h" 40 41#include <fcntl.h> 42#include <sys/types.h> 43#include <unistd.h> 44#include <xkbcommon/xkbcommon.h> 45 46#include <wayland-util.h> 47 48#include "alpha-modifier-v1-client-protocol.h" 49#include "cursor-shape-v1-client-protocol.h" 50#include "fractional-scale-v1-client-protocol.h" 51#include "frog-color-management-v1-client-protocol.h" 52#include "idle-inhibit-unstable-v1-client-protocol.h" 53#include "input-timestamps-unstable-v1-client-protocol.h" 54#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h" 55#include "pointer-constraints-unstable-v1-client-protocol.h" 56#include "primary-selection-unstable-v1-client-protocol.h" 57#include "relative-pointer-unstable-v1-client-protocol.h" 58#include "tablet-v2-client-protocol.h" 59#include "text-input-unstable-v3-client-protocol.h" 60#include "viewporter-client-protocol.h" 61#include "xdg-activation-v1-client-protocol.h" 62#include "xdg-decoration-unstable-v1-client-protocol.h" 63#include "xdg-dialog-v1-client-protocol.h" 64#include "xdg-foreign-unstable-v2-client-protocol.h" 65#include "xdg-output-unstable-v1-client-protocol.h" 66#include "xdg-shell-client-protocol.h" 67#include "xdg-toplevel-icon-v1-client-protocol.h" 68#include "color-management-v1-client-protocol.h" 69#include "pointer-warp-v1-client-protocol.h" 70#include "pointer-gestures-unstable-v1-client-protocol.h" 71 72#ifdef HAVE_LIBDECOR_H 73#include <libdecor.h> 74#endif 75 76#define WAYLANDVID_DRIVER_NAME "wayland" 77 78// Clamp core protocol versions on older versions of libwayland. 79#if SDL_WAYLAND_CHECK_VERSION(1, 22, 0) 80#define SDL_WL_COMPOSITOR_VERSION 6 81#else 82#define SDL_WL_COMPOSITOR_VERSION 4 83#endif 84 85#if SDL_WAYLAND_CHECK_VERSION(1, 24, 0) 86#define SDL_WL_SEAT_VERSION 10 87#elif SDL_WAYLAND_CHECK_VERSION(1, 22, 0) 88#define SDL_WL_SEAT_VERSION 9 89#elif SDL_WAYLAND_CHECK_VERSION(1, 21, 0) 90#define SDL_WL_SEAT_VERSION 8 91#else 92#define SDL_WL_SEAT_VERSION 5 93#endif 94 95#if SDL_WAYLAND_CHECK_VERSION(1, 20, 0) 96#define SDL_WL_OUTPUT_VERSION 4 97#else 98#define SDL_WL_OUTPUT_VERSION 3 99#endif 100 101#if SDL_WAYLAND_CHECK_VERSION(1, 24, 0) 102#define SDL_WL_SHM_VERSION 2 103#else 104#define SDL_WL_SHM_VERSION 1 105#endif 106 107// The SDL libwayland-client minimum is 1.18, which supports version 3. 108#define SDL_WL_DATA_DEVICE_VERSION 3 109 110// wl_fixes was introduced in 1.24.0 111#if SDL_WAYLAND_CHECK_VERSION(1, 24, 0) 112#define SDL_WL_FIXES_VERSION 1 113#endif 114 115#ifdef SDL_USE_LIBDBUS 116#include "../../core/linux/SDL_dbus.h" 117 118#define DISPLAY_INFO_NODE "org.gnome.Mutter.DisplayConfig" 119#define DISPLAY_INFO_PATH "/org/gnome/Mutter/DisplayConfig" 120#define DISPLAY_INFO_METHOD "GetCurrentState" 121#endif 122 123/* GNOME doesn't expose displays in any particular order, but we can find the 124 * primary display and its logical coordinates via a DBus method. 125 */ 126static bool Wayland_GetGNOMEPrimaryDisplayCoordinates(int *x, int *y) 127{ 128#ifdef SDL_USE_LIBDBUS 129 SDL_DBusContext *dbus = SDL_DBus_GetContext(); 130 if (dbus == NULL) { 131 return false; 132 } 133 DBusMessage *reply = NULL; 134 DBusMessageIter iter[3]; 135 DBusMessage *msg = dbus->message_new_method_call(DISPLAY_INFO_NODE, 136 DISPLAY_INFO_PATH, 137 DISPLAY_INFO_NODE, 138 DISPLAY_INFO_METHOD); 139 140 if (msg) { 141 reply = dbus->connection_send_with_reply_and_block(dbus->session_conn, msg, DBUS_TIMEOUT_USE_DEFAULT, NULL); 142 dbus->message_unref(msg); 143 } 144 145 if (reply) { 146 // Serial (don't care) 147 dbus->message_iter_init(reply, &iter[0]); 148 if (dbus->message_iter_get_arg_type(&iter[0]) != DBUS_TYPE_UINT32) { 149 goto error; 150 } 151 152 // Physical monitor array (don't care) 153 dbus->message_iter_next(&iter[0]); 154 if (dbus->message_iter_get_arg_type(&iter[0]) != DBUS_TYPE_ARRAY) { 155 goto error; 156 } 157 158 // Logical monitor array of structs 159 dbus->message_iter_next(&iter[0]); 160 if (dbus->message_iter_get_arg_type(&iter[0]) != DBUS_TYPE_ARRAY) { 161 goto error; 162 } 163 164 // First logical monitor struct 165 dbus->message_iter_recurse(&iter[0], &iter[1]); 166 if (dbus->message_iter_get_arg_type(&iter[1]) != DBUS_TYPE_STRUCT) { 167 goto error; 168 } 169 170 do { 171 int logical_x, logical_y; 172 dbus_bool_t primary; 173 174 // Logical X 175 dbus->message_iter_recurse(&iter[1], &iter[2]); 176 if (dbus->message_iter_get_arg_type(&iter[2]) != DBUS_TYPE_INT32) { 177 goto error; 178 } 179 dbus->message_iter_get_basic(&iter[2], &logical_x); 180 181 // Logical Y 182 dbus->message_iter_next(&iter[2]); 183 if (dbus->message_iter_get_arg_type(&iter[2]) != DBUS_TYPE_INT32) { 184 goto error; 185 } 186 dbus->message_iter_get_basic(&iter[2], &logical_y); 187 188 // Scale (don't care) 189 dbus->message_iter_next(&iter[2]); 190 if (dbus->message_iter_get_arg_type(&iter[2]) != DBUS_TYPE_DOUBLE) { 191 goto error; 192 } 193 194 // Transform (don't care) 195 dbus->message_iter_next(&iter[2]); 196 if (dbus->message_iter_get_arg_type(&iter[2]) != DBUS_TYPE_UINT32) { 197 goto error; 198 } 199 200 // Primary display boolean 201 dbus->message_iter_next(&iter[2]); 202 if (dbus->message_iter_get_arg_type(&iter[2]) != DBUS_TYPE_BOOLEAN) { 203 goto error; 204 } 205 dbus->message_iter_get_basic(&iter[2], &primary); 206 207 if (primary) { 208 *x = logical_x; 209 *y = logical_y; 210 211 // We found the primary display: success. 212 dbus->message_unref(reply); 213 return true; 214 } 215 } while (dbus->message_iter_next(&iter[1])); 216 } 217 218error: 219 if (reply) { 220 dbus->message_unref(reply); 221 } 222#endif 223 return false; 224} 225 226// Sort the list of displays into a deterministic order 227static int SDLCALL Wayland_DisplayPositionCompare(const void *a, const void *b) 228{ 229 const SDL_DisplayData *da = *(SDL_DisplayData **)a; 230 const SDL_DisplayData *db = *(SDL_DisplayData **)b; 231 232 const bool a_at_origin = da->x == 0 && da->y == 0; 233 const bool b_at_origin = db->x == 0 && db->y == 0; 234 235 // Sort the display at 0,0 to be beginning of the list, as that will be the fallback primary. 236 if (a_at_origin && !b_at_origin) { 237 return -1; 238 } 239 if (b_at_origin && !a_at_origin) { 240 return 1; 241 } 242 if (da->x < db->x) { 243 return -1; 244 } 245 if (da->x > db->x) { 246 return 1; 247 } 248 if (da->y < db->y) { 249 return -1; 250 } 251 if (da->y > db->y) { 252 return 1; 253 } 254 255 // If no position information is available, use the connector name. 256 if (da->wl_output_name && db->wl_output_name) { 257 return SDL_strcmp(da->wl_output_name, db->wl_output_name); 258 } 259 260 return 0; 261} 262 263/* Wayland doesn't have the native concept of a primary display, but there are clients that 264 * will base their resolution lists on, or automatically make themselves fullscreen on, the 265 * first listed output, which can lead to problems if the first listed output isn't 266 * necessarily the best display for this. This attempts to find a primary display, first by 267 * querying the GNOME DBus property, then trying to determine the 'best' display if that fails. 268 * If all displays are equal, the one at position 0,0 will become the primary. 269 * 270 * The primary is determined by the following criteria, in order: 271 * - Landscape is preferred over portrait 272 * - The highest native resolution 273 * - A higher HDR range is preferred 274 * - Higher refresh is preferred (ignoring small differences) 275 * - Lower scale values are preferred (larger display) 276 */ 277static int Wayland_GetPrimaryDisplay(SDL_VideoData *vid) 278{ 279 static const int REFRESH_DELTA = 4000; 280 281 // Query the DBus interface to see if the coordinates of the primary display are exposed. 282 int x, y; 283 if (Wayland_GetGNOMEPrimaryDisplayCoordinates(&x, &y)) { 284 for (int i = 0; i < vid->output_count; ++i) { 285 if (vid->output_list[i]->x == x && vid->output_list[i]->y == y) { 286 return i; 287 } 288 } 289 } 290 291 // Otherwise, choose the 'best' display. 292 int best_width = 0; 293 int best_height = 0; 294 double best_scale = 0.0; 295 float best_headroom = 0.0f; 296 int best_refresh = 0; 297 bool best_is_landscape = false; 298 int best_index = 0; 299 300 for (int i = 0; i < vid->output_count; ++i) { 301 const SDL_DisplayData *d = vid->output_list[i]; 302 const bool is_landscape = d->orientation != SDL_ORIENTATION_PORTRAIT && d->orientation != SDL_ORIENTATION_PORTRAIT_FLIPPED; 303 bool have_new_best = false; 304 305 if (!best_is_landscape && is_landscape) { // Favor landscape over portrait displays. 306 have_new_best = true; 307 } else if (!best_is_landscape || is_landscape) { // Ignore portrait displays if a landscape was already found. 308 if (d->pixel_width > best_width || d->pixel_height > best_height) { 309 have_new_best = true; 310 } else if (d->pixel_width == best_width && d->pixel_height == best_height) { 311 if (d->HDR.HDR_headroom > best_headroom) { // Favor a higher HDR luminance range 312 have_new_best = true; 313 } else if (d->HDR.HDR_headroom == best_headroom) { 314 if (d->refresh - best_refresh > REFRESH_DELTA) { // Favor a higher refresh rate, but ignore small differences (e.g. 59.97 vs 60.1) 315 have_new_best = true; 316 } else if (d->scale_factor < best_scale && SDL_abs(d->refresh - best_refresh) <= REFRESH_DELTA) { 317 // Prefer a lower scale display if the difference in refresh rate is small. 318 have_new_best = true; 319 } 320 } 321 } 322 } 323 324 if (have_new_best) { 325 best_width = d->pixel_width; 326 best_height = d->pixel_height; 327 best_scale = d->scale_factor; 328 best_headroom = d->HDR.HDR_headroom; 329 best_refresh = d->refresh; 330 best_is_landscape = is_landscape; 331 best_index = i; 332 } 333 } 334 335 return best_index; 336} 337 338static void Wayland_SortOutputsByPriorityHint(SDL_VideoData *vid) 339{ 340 const char *name_hint = SDL_GetHint(SDL_HINT_VIDEO_DISPLAY_PRIORITY); 341 342 if (name_hint) { 343 char *saveptr; 344 char *str = SDL_strdup(name_hint); 345 SDL_DisplayData **sorted_list = SDL_malloc(sizeof(SDL_DisplayData *) * vid->output_count); 346 347 if (str && sorted_list) { 348 int sorted_index = 0; 349 350 // Sort the requested displays to the front of the list. 351 const char *token = SDL_strtok_r(str, ",", &saveptr); 352 while (token) { 353 for (int i = 0; i < vid->output_count; ++i) { 354 SDL_DisplayData *d = vid->output_list[i]; 355 if (d && d->wl_output_name && SDL_strcmp(token, d->wl_output_name) == 0) { 356 sorted_list[sorted_index++] = d; 357 vid->output_list[i] = NULL; 358 break; 359 } 360 } 361 362 token = SDL_strtok_r(NULL, ",", &saveptr); 363 } 364 365 // Append the remaining outputs to the end of the list. 366 for (int i = 0; i < vid->output_count; ++i) { 367 if (vid->output_list[i]) { 368 sorted_list[sorted_index++] = vid->output_list[i]; 369 } 370 } 371 372 // Copy the sorted list to the output list. 373 SDL_memcpy(vid->output_list, sorted_list, sizeof(SDL_DisplayData *) * vid->output_count); 374 } 375 376 SDL_free(str); 377 SDL_free(sorted_list); 378 } 379} 380 381static void Wayland_SortOutputs(SDL_VideoData *vid) 382{ 383 // Sort by position or connector name, so the order of outputs is deterministic. 384 SDL_qsort(vid->output_list, vid->output_count, sizeof(SDL_DisplayData *), Wayland_DisplayPositionCompare); 385 386 // Find a suitable primary display and move it to the front of the list. 387 const int primary_index = Wayland_GetPrimaryDisplay(vid); 388 if (primary_index) { 389 SDL_DisplayData *primary = vid->output_list[primary_index]; 390 SDL_memmove(&vid->output_list[1], &vid->output_list[0], sizeof(SDL_DisplayData *) * primary_index); 391 vid->output_list[0] = primary; 392 } 393 394 // Apply the ordering hint, if specified. 395 Wayland_SortOutputsByPriorityHint(vid); 396} 397 398static void handle_wl_output_done(void *data, struct wl_output *output); 399 400// Initialization/Query functions 401static bool Wayland_VideoInit(SDL_VideoDevice *_this); 402static bool Wayland_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect); 403static void Wayland_VideoQuit(SDL_VideoDevice *_this); 404 405static const char *SDL_WAYLAND_surface_tag = "sdl-window"; 406static const char *SDL_WAYLAND_output_tag = "sdl-output"; 407 408void SDL_WAYLAND_register_surface(struct wl_surface *surface) 409{ 410 wl_proxy_set_tag((struct wl_proxy *)surface, &SDL_WAYLAND_surface_tag); 411} 412 413void SDL_WAYLAND_register_output(struct wl_output *output) 414{ 415 wl_proxy_set_tag((struct wl_proxy *)output, &SDL_WAYLAND_output_tag); 416} 417 418bool SDL_WAYLAND_own_surface(struct wl_surface *surface) 419{ 420 return wl_proxy_get_tag((struct wl_proxy *)surface) == &SDL_WAYLAND_surface_tag; 421} 422 423bool SDL_WAYLAND_own_output(struct wl_output *output) 424{ 425 return wl_proxy_get_tag((struct wl_proxy *)output) == &SDL_WAYLAND_output_tag; 426} 427 428/* External surfaces may have their own user data attached, the modification of which 429 * can cause problems with external toolkits. Instead, external windows are kept in 430 * their own list, and a search is conducted to find a matching surface. 431 */ 432static struct wl_list external_window_list; 433 434void Wayland_AddWindowDataToExternalList(SDL_WindowData *data) 435{ 436 WAYLAND_wl_list_insert(&external_window_list, &data->external_window_list_link); 437} 438 439void Wayland_RemoveWindowDataFromExternalList(SDL_WindowData *data) 440{ 441 WAYLAND_wl_list_remove(&data->external_window_list_link); 442} 443 444SDL_WindowData *Wayland_GetWindowDataForOwnedSurface(struct wl_surface *surface) 445{ 446 if (SDL_WAYLAND_own_surface(surface)) { 447 return (SDL_WindowData *)wl_surface_get_user_data(surface); 448 } else if (!WAYLAND_wl_list_empty(&external_window_list)) { 449 SDL_WindowData *p; 450 wl_list_for_each (p, &external_window_list, external_window_list_link) { 451 if (p->surface == surface) { 452 return p; 453 } 454 } 455 } 456 457 return NULL; 458} 459 460struct wl_event_queue *Wayland_DisplayCreateQueue(struct wl_display *display, const char *name) 461{ 462#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC 463 if (WAYLAND_wl_display_create_queue_with_name) { 464 return WAYLAND_wl_display_create_queue_with_name(display, name); 465 } 466#elif SDL_WAYLAND_CHECK_VERSION(1, 23, 0) 467 return WAYLAND_wl_display_create_queue_with_name(display, name); 468#endif 469 return WAYLAND_wl_display_create_queue(display); 470} 471 472static void Wayland_DeleteDevice(SDL_VideoDevice *device) 473{ 474 SDL_VideoData *data = device->internal; 475 if (data->display && !data->display_externally_owned) { 476 WAYLAND_wl_display_disconnect(data->display); 477 SDL_ClearProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER); 478 } 479 SDL_free(data); 480 SDL_free(device); 481 SDL_WAYLAND_UnloadSymbols(); 482} 483 484typedef struct 485{ 486 bool has_fifo_v1; 487 struct wl_fixes *wl_fixes; 488} SDL_WaylandPreferredData; 489 490static void wayland_preferred_check_handle_global(void *data, struct wl_registry *registry, uint32_t id, 491 const char *interface, uint32_t version) 492{ 493 SDL_WaylandPreferredData *d = data; 494 495 if (SDL_strcmp(interface, "wp_fifo_manager_v1") == 0) { 496 d->has_fifo_v1 = true; 497 } 498#ifdef SDL_WL_FIXES_VERSION 499 else if (SDL_strcmp(interface, "wl_fixes") == 0) { 500 d->wl_fixes = wl_registry_bind(registry, id, &wl_fixes_interface, SDL_min(SDL_WL_FIXES_VERSION, version)); 501 } 502#endif 503} 504 505static void wayland_preferred_check_remove_global(void *data, struct wl_registry *registry, uint32_t id) 506{ 507 // No need to do anything here. 508} 509 510static const struct wl_registry_listener preferred_registry_listener = { 511 wayland_preferred_check_handle_global, 512 wayland_preferred_check_remove_global 513}; 514 515static bool Wayland_IsPreferred(struct wl_display *display) 516{ 517 struct wl_registry *registry = wl_display_get_registry(display); 518 SDL_WaylandPreferredData preferred_data = { 0 }; 519 520 if (!registry) { 521 SDL_SetError("Failed to get the Wayland registry"); 522 return false; 523 } 524 525 wl_registry_add_listener(registry, &preferred_registry_listener, &preferred_data); 526 527 WAYLAND_wl_display_roundtrip(display); 528 529 if (preferred_data.wl_fixes) { 530 wl_fixes_destroy_registry(preferred_data.wl_fixes, registry); 531 wl_fixes_destroy(preferred_data.wl_fixes); 532 } 533 wl_registry_destroy(registry); 534 535 if (!preferred_data.has_fifo_v1) { 536 SDL_LogInfo(SDL_LOG_CATEGORY_VIDEO, "This compositor lacks support for the fifo-v1 protocol; falling back to XWayland for GPU performance reasons (set SDL_VIDEO_DRIVER=wayland to override)"); 537 } 538 return preferred_data.has_fifo_v1; 539} 540 541static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols) 542{ 543 SDL_VideoDevice *device; 544 SDL_VideoData *data; 545 struct wl_display *display = SDL_GetPointerProperty(SDL_GetGlobalProperties(), 546 SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, NULL); 547 bool display_is_external = !!display; 548 549 // Are we trying to connect to or are currently in a Wayland session? 550 if (!SDL_getenv("WAYLAND_DISPLAY")) { 551 const char *session = SDL_getenv("XDG_SESSION_TYPE"); 552 if (session && SDL_strcasecmp(session, "wayland") != 0) { 553 return NULL; 554 } 555 } 556 557 if (!SDL_WAYLAND_LoadSymbols()) { 558 return NULL; 559 } 560 561 if (!display) { 562 display = WAYLAND_wl_display_connect(NULL); 563 if (!display) { 564 SDL_WAYLAND_UnloadSymbols(); 565 return NULL; 566 } 567 } 568 569 /* 570 * If we are checking for preferred Wayland, then let's query for 571 * fifo-v1's existence, so we don't regress GPU-bound performance 572 * and frame-pacing by default due to swapchain starvation. 573 */ 574 if (require_preferred_protocols && !Wayland_IsPreferred(display)) { 575 if (!display_is_external) { 576 WAYLAND_wl_display_disconnect(display); 577 } 578 SDL_WAYLAND_UnloadSymbols(); 579 return NULL; 580 } 581 582 data = SDL_calloc(1, sizeof(*data)); 583 if (!data) { 584 if (!display_is_external) { 585 WAYLAND_wl_display_disconnect(display); 586 } 587 SDL_WAYLAND_UnloadSymbols(); 588 return NULL; 589 } 590 591 data->initializing = true; 592 data->display = display; 593 data->display_externally_owned = display_is_external; 594 data->scale_to_display_enabled = SDL_GetHintBoolean(SDL_HINT_VIDEO_WAYLAND_SCALE_TO_DISPLAY, false); 595 WAYLAND_wl_list_init(&data->seat_list); 596 WAYLAND_wl_list_init(&external_window_list); 597 598 // Initialize all variables that we clean on shutdown 599 device = SDL_calloc(1, sizeof(SDL_VideoDevice)); 600 if (!device) { 601 SDL_free(data); 602 if (!display_is_external) { 603 WAYLAND_wl_display_disconnect(display); 604 } 605 SDL_WAYLAND_UnloadSymbols(); 606 return NULL; 607 } 608 609 if (!display_is_external) { 610 SDL_SetPointerProperty(SDL_GetGlobalProperties(), 611 SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, display); 612 } 613 614 device->internal = data; 615 616 // Set the function pointers 617 device->VideoInit = Wayland_VideoInit; 618 device->VideoQuit = Wayland_VideoQuit; 619 device->GetDisplayBounds = Wayland_GetDisplayBounds; 620 device->SuspendScreenSaver = Wayland_SuspendScreenSaver; 621 622 device->PumpEvents = Wayland_PumpEvents; 623 device->WaitEventTimeout = Wayland_WaitEventTimeout; 624 device->SendWakeupEvent = Wayland_SendWakeupEvent; 625 626#ifdef SDL_VIDEO_OPENGL_EGL 627 device->GL_SwapWindow = Wayland_GLES_SwapWindow; 628 device->GL_GetSwapInterval = Wayland_GLES_GetSwapInterval; 629 device->GL_SetSwapInterval = Wayland_GLES_SetSwapInterval; 630 device->GL_MakeCurrent = Wayland_GLES_MakeCurrent; 631 device->GL_CreateContext = Wayland_GLES_CreateContext; 632 device->GL_LoadLibrary = Wayland_GLES_LoadLibrary; 633 device->GL_UnloadLibrary = Wayland_GLES_UnloadLibrary; 634 device->GL_GetProcAddress = Wayland_GLES_GetProcAddress; 635 device->GL_DestroyContext = Wayland_GLES_DestroyContext; 636 device->GL_GetEGLSurface = Wayland_GLES_GetEGLSurface; 637#endif 638 639 device->CreateSDLWindow = Wayland_CreateWindow; 640 device->ShowWindow = Wayland_ShowWindow; 641 device->HideWindow = Wayland_HideWindow; 642 device->RaiseWindow = Wayland_RaiseWindow; 643 device->SetWindowFullscreen = Wayland_SetWindowFullscreen; 644 device->MaximizeWindow = Wayland_MaximizeWindow; 645 device->MinimizeWindow = Wayland_MinimizeWindow; 646 device->SetWindowMouseRect = Wayland_SetWindowMouseRect; 647 device->SetWindowMouseGrab = Wayland_SetWindowMouseGrab; 648 device->SetWindowKeyboardGrab = Wayland_SetWindowKeyboardGrab; 649 device->RestoreWindow = Wayland_RestoreWindow; 650 device->SetWindowBordered = Wayland_SetWindowBordered; 651 device->SetWindowResizable = Wayland_SetWindowResizable; 652 device->SetWindowPosition = Wayland_SetWindowPosition; 653 device->SetWindowSize = Wayland_SetWindowSize; 654 device->SetWindowMinimumSize = Wayland_SetWindowMinimumSize; 655 device->SetWindowMaximumSize = Wayland_SetWindowMaximumSize; 656 device->SetWindowParent = Wayland_SetWindowParent; 657 device->SetWindowModal = Wayland_SetWindowModal; 658 device->SetWindowOpacity = Wayland_SetWindowOpacity; 659 device->SetWindowTitle = Wayland_SetWindowTitle; 660 device->SetWindowIcon = Wayland_SetWindowIcon; 661 device->GetWindowSizeInPixels = Wayland_GetWindowSizeInPixels; 662 device->GetWindowContentScale = Wayland_GetWindowContentScale; 663 device->GetWindowICCProfile = Wayland_GetWindowICCProfile; 664 device->GetDisplayForWindow = Wayland_GetDisplayForWindow; 665 device->DestroyWindow = Wayland_DestroyWindow; 666 device->SetWindowHitTest = Wayland_SetWindowHitTest; 667 device->FlashWindow = Wayland_FlashWindow; 668#ifdef SDL_USE_LIBDBUS 669 device->ApplyWindowProgress = DBUS_ApplyWindowProgress; 670#endif // SDL_USE_LIBDBUS 671 device->HasScreenKeyboardSupport = Wayland_HasScreenKeyboardSupport; 672 device->ShowWindowSystemMenu = Wayland_ShowWindowSystemMenu; 673 device->SyncWindow = Wayland_SyncWindow; 674 device->SetWindowFocusable = Wayland_SetWindowFocusable; 675 device->ReconfigureWindow = Wayland_ReconfigureWindow; 676 677#ifdef SDL_USE_LIBDBUS 678 if (SDL_SystemTheme_Init()) 679 device->system_theme = SDL_SystemTheme_Get(); 680#endif 681 682 device->GetTextMimeTypes = Wayland_GetTextMimeTypes; 683 device->SetClipboardData = Wayland_SetClipboardData; 684 device->GetClipboardData = Wayland_GetClipboardData; 685 device->HasClipboardData = Wayland_HasClipboardData; 686 device->StartTextInput = Wayland_StartTextInput; 687 device->StopTextInput = Wayland_StopTextInput; 688 device->UpdateTextInputArea = Wayland_UpdateTextInputArea; 689 690#ifdef SDL_VIDEO_VULKAN 691 device->Vulkan_LoadLibrary = Wayland_Vulkan_LoadLibrary; 692 device->Vulkan_UnloadLibrary = Wayland_Vulkan_UnloadLibrary; 693 device->Vulkan_GetInstanceExtensions = Wayland_Vulkan_GetInstanceExtensions; 694 device->Vulkan_CreateSurface = Wayland_Vulkan_CreateSurface; 695 device->Vulkan_DestroySurface = Wayland_Vulkan_DestroySurface; 696 device->Vulkan_GetPresentationSupport = Wayland_Vulkan_GetPresentationSupport; 697#endif 698 699 device->free = Wayland_DeleteDevice; 700 701 device->device_caps = VIDEO_DEVICE_CAPS_MODE_SWITCHING_EMULATED | 702 VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT | 703 VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS | 704 VIDEO_DEVICE_CAPS_SENDS_DISPLAY_CHANGES | 705 VIDEO_DEVICE_CAPS_SENDS_HDR_CHANGES; 706 707 return device; 708} 709 710static SDL_VideoDevice *Wayland_Preferred_CreateDevice(void) 711{ 712 return Wayland_CreateDevice(true); 713} 714 715static SDL_VideoDevice *Wayland_Fallback_CreateDevice(void) 716{ 717 return Wayland_CreateDevice(false); 718} 719 720VideoBootStrap Wayland_preferred_bootstrap = { 721 WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver", 722 Wayland_Preferred_CreateDevice, 723 Wayland_ShowMessageBox, 724 true 725}; 726 727VideoBootStrap Wayland_bootstrap = { 728 WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver", 729 Wayland_Fallback_CreateDevice, 730 Wayland_ShowMessageBox, 731 false 732}; 733 734static void handle_xdg_output_logical_position(void *data, struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) 735{ 736 SDL_DisplayData *internal = (SDL_DisplayData *)data; 737 738 internal->x = x; 739 internal->y = y; 740 internal->has_logical_position = true; 741} 742 743static void handle_xdg_output_logical_size(void *data, struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) 744{ 745 SDL_DisplayData *internal = (SDL_DisplayData *)data; 746 747 internal->logical_width = width; 748 internal->logical_height = height; 749 internal->has_logical_size = true; 750} 751 752static void handle_xdg_output_done(void *data, struct zxdg_output_v1 *xdg_output) 753{ 754 SDL_DisplayData *internal = data; 755 756 /* 757 * xdg-output.done events are deprecated and only apply below version 3 of the protocol. 758 * A wl-output.done event will be emitted in version 3 or higher. 759 */ 760 if (zxdg_output_v1_get_version(internal->xdg_output) < 3) { 761 handle_wl_output_done(data, internal->output); 762 } 763} 764 765static void handle_xdg_output_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) 766{ 767 SDL_DisplayData *internal = (SDL_DisplayData *)data; 768 769 // Deprecated as of wl_output v4. 770 if (wl_output_get_version(internal->output) < WL_OUTPUT_NAME_SINCE_VERSION && 771 internal->display == 0) { 772 SDL_free(internal->wl_output_name); 773 internal->wl_output_name = SDL_strdup(name); 774 } 775} 776 777static void handle_xdg_output_description(void *data, struct zxdg_output_v1 *xdg_output, const char *description) 778{ 779 SDL_DisplayData *internal = (SDL_DisplayData *)data; 780 781 // Deprecated as of wl_output v4. 782 if (wl_output_get_version(internal->output) < WL_OUTPUT_DESCRIPTION_SINCE_VERSION && 783 internal->display == 0) { 784 // xdg-output descriptions, if available, supersede wl-output model names. 785 SDL_free(internal->placeholder.name); 786 internal->placeholder.name = SDL_strdup(description); 787 } 788} 789 790static const struct zxdg_output_v1_listener xdg_output_listener = { 791 handle_xdg_output_logical_position, 792 handle_xdg_output_logical_size, 793 handle_xdg_output_done, 794 handle_xdg_output_name, 795 handle_xdg_output_description, 796}; 797 798static void AddEmulatedModes(SDL_DisplayData *dispdata, int native_width, int native_height) 799{ 800 struct EmulatedMode 801 { 802 int w; 803 int h; 804 }; 805 806 // Resolution lists courtesy of XWayland 807 const struct EmulatedMode mode_list[] = { 808 // 16:9 (1.77) 809 { 7680, 4320 }, 810 { 6144, 3160 }, 811 { 5120, 2880 }, 812 { 4096, 2304 }, 813 { 3840, 2160 }, 814 { 3200, 1800 }, 815 { 2880, 1620 }, 816 { 2560, 1440 }, 817 { 2048, 1152 }, 818 { 1920, 1080 }, 819 { 1600, 900 }, 820 { 1368, 768 }, 821 { 1280, 720 }, 822 { 864, 486 }, 823 824 // 16:10 (1.6) 825 { 2560, 1600 }, 826 { 1920, 1200 }, 827 { 1680, 1050 }, 828 { 1440, 900 }, 829 { 1280, 800 }, 830 831 // 3:2 (1.5) 832 { 720, 480 }, 833 834 // 4:3 (1.33) 835 { 2048, 1536 }, 836 { 1920, 1440 }, 837 { 1600, 1200 }, 838 { 1440, 1080 }, 839 { 1400, 1050 }, 840 { 1280, 1024 }, 841 { 1280, 960 }, 842 { 1152, 864 }, 843 { 1024, 768 }, 844 { 800, 600 }, 845 { 640, 480 } 846 }; 847 848 int i; 849 SDL_DisplayMode mode; 850 SDL_VideoDisplay *dpy = dispdata->display ? SDL_GetVideoDisplay(dispdata->display) : &dispdata->placeholder; 851 const bool rot_90 = native_width < native_height; // Reverse width/height for portrait displays. 852 853 for (i = 0; i < SDL_arraysize(mode_list); ++i) { 854 SDL_zero(mode); 855 mode.format = dpy->desktop_mode.format; 856 mode.refresh_rate_numerator = dpy->desktop_mode.refresh_rate_numerator; 857 mode.refresh_rate_denominator = dpy->desktop_mode.refresh_rate_denominator; 858 859 if (rot_90) { 860 mode.w = mode_list[i].h; 861 mode.h = mode_list[i].w; 862 } else { 863 mode.w = mode_list[i].w; 864 mode.h = mode_list[i].h; 865 } 866 867 // Only add modes that are smaller than the native mode. 868 if ((mode.w < native_width && mode.h < native_height) || 869 (mode.w < native_width && mode.h == native_height) || 870 (mode.w == native_width && mode.h < native_height)) { 871 SDL_AddFullscreenDisplayMode(dpy, &mode); 872 } 873 } 874} 875 876static void handle_wl_output_geometry(void *data, struct wl_output *output, int x, int y, 877 int physical_width, int physical_height, int subpixel, 878 const char *make, const char *model, int transform) 879{ 880 SDL_DisplayData *internal = (SDL_DisplayData *)data; 881 882 // Apply the change from wl-output only if xdg-output is not supported 883 if (!internal->has_logical_position) { 884 internal->x = x; 885 internal->y = y; 886 } 887 internal->physical_width_mm = physical_width; 888 internal->physical_height_mm = physical_height; 889 890 // The model is only used for the output name if wl_output or xdg-output haven't provided a description. 891 if (internal->display == 0 && !internal->placeholder.name) { 892 internal->placeholder.name = SDL_strdup(model); 893 } 894 895 internal->transform = transform; 896#define TF_CASE(in, out) \ 897 case WL_OUTPUT_TRANSFORM_##in: \ 898 internal->orientation = SDL_ORIENTATION_##out; \ 899 break; 900 if (internal->physical_width_mm >= internal->physical_height_mm) { 901 switch (transform) { 902 TF_CASE(NORMAL, LANDSCAPE) 903 TF_CASE(90, PORTRAIT) 904 TF_CASE(180, LANDSCAPE_FLIPPED) 905 TF_CASE(270, PORTRAIT_FLIPPED) 906 TF_CASE(FLIPPED, LANDSCAPE_FLIPPED) 907 TF_CASE(FLIPPED_90, PORTRAIT_FLIPPED) 908 TF_CASE(FLIPPED_180, LANDSCAPE) 909 TF_CASE(FLIPPED_270, PORTRAIT) 910 } 911 } else { 912 switch (transform) { 913 TF_CASE(NORMAL, PORTRAIT) 914 TF_CASE(90, LANDSCAPE) 915 TF_CASE(180, PORTRAIT_FLIPPED) 916 TF_CASE(270, LANDSCAPE_FLIPPED) 917 TF_CASE(FLIPPED, PORTRAIT_FLIPPED) 918 TF_CASE(FLIPPED_90, LANDSCAPE_FLIPPED) 919 TF_CASE(FLIPPED_180, PORTRAIT) 920 TF_CASE(FLIPPED_270, LANDSCAPE) 921 } 922 } 923#undef TF_CASE 924} 925 926static void handle_wl_output_mode(void *data, struct wl_output *output, uint32_t flags, 927 int width, int height, int refresh) 928{ 929 SDL_DisplayData *internal = (SDL_DisplayData *)data; 930 931 if (flags & WL_OUTPUT_MODE_CURRENT) { 932 internal->pixel_width = width; 933 internal->pixel_height = height; 934 935 /* 936 * Don't rotate this yet, wl-output coordinates are transformed in 937 * handle_done and xdg-output coordinates are pre-transformed. 938 */ 939 if (!internal->has_logical_size) { 940 internal->logical_width = width; 941 internal->logical_height = height; 942 } 943 944 internal->refresh = refresh; 945 } 946} 947 948static void handle_wl_output_done(void *data, struct wl_output *output) 949{ 950 const bool mode_emulation_enabled = SDL_GetHintBoolean(SDL_HINT_VIDEO_WAYLAND_MODE_EMULATION, true); 951 SDL_DisplayData *internal = (SDL_DisplayData *)data; 952 SDL_VideoData *video = internal->videodata; 953 SDL_DisplayMode native_mode, desktop_mode; 954 955 /* 956 * When using xdg-output, two wl-output.done events will be emitted: 957 * one at the completion of wl-display and one at the completion of xdg-output. 958 * 959 * All required events must be received before proceeding. 960 */ 961 const int event_await_count = 1 + (internal->xdg_output != NULL); 962 963 internal->wl_output_done_count = SDL_min(internal->wl_output_done_count + 1, event_await_count + 1); 964 965 if (internal->wl_output_done_count < event_await_count) { 966 return; 967 } 968 969 // If the display was already created, reset and rebuild the mode list. 970 SDL_VideoDisplay *dpy = SDL_GetVideoDisplay(internal->display); 971 if (dpy) { 972 SDL_ResetFullscreenDisplayModes(dpy); 973 } 974 975 // The native display resolution 976 SDL_zero(native_mode); 977 native_mode.format = SDL_PIXELFORMAT_XRGB8888; 978 979 // Transform the pixel values, if necessary. 980 if (internal->transform & WL_OUTPUT_TRANSFORM_90) { 981 native_mode.w = internal->pixel_height; 982 native_mode.h = internal->pixel_width; 983 } else { 984 native_mode.w = internal->pixel_width; 985 native_mode.h = internal->pixel_height; 986 } 987 native_mode.refresh_rate_numerator = internal->refresh; 988 native_mode.refresh_rate_denominator = 1000; 989 990 if (internal->has_logical_size) { // If xdg-output is present... 991 if (native_mode.w != internal->logical_width || native_mode.h != internal->logical_height) { 992 // ...and the compositor scales the logical viewport... 993 if (video->viewporter) { 994 // ...and viewports are supported, calculate the true scale of the output. 995 internal->scale_factor = (double)native_mode.w / (double)internal->logical_width; 996 } else { 997 // ...otherwise, the 'native' pixel values are a multiple of the logical screen size. 998 internal->pixel_width = internal->logical_width * (int)internal->scale_factor; 999 internal->pixel_height = internal->logical_height * (int)internal->scale_factor; 1000 } 1001 } else { 1002 /* ...and the output viewport is not scaled in the global compositing 1003 * space, the output dimensions need to be divided by the scale factor. 1004 */ 1005 internal->logical_width /= (int)internal->scale_factor; 1006 internal->logical_height /= (int)internal->scale_factor; 1007 } 1008 } else { 1009 /* Calculate the points from the pixel values, if xdg-output isn't present. 1010 * Use the native mode pixel values since they are pre-transformed. 1011 */ 1012 internal->logical_width = native_mode.w / (int)internal->scale_factor; 1013 internal->logical_height = native_mode.h / (int)internal->scale_factor; 1014 } 1015 1016 // The scaled desktop mode 1017 SDL_zero(desktop_mode); 1018 desktop_mode.format = SDL_PIXELFORMAT_XRGB8888; 1019 1020 if (!video->scale_to_display_enabled) { 1021 desktop_mode.w = internal->logical_width; 1022 desktop_mode.h = internal->logical_height; 1023 desktop_mode.pixel_density = (float)internal->scale_factor; 1024 } else { 1025 desktop_mode.w = native_mode.w; 1026 desktop_mode.h = native_mode.h; 1027 desktop_mode.pixel_density = 1.0f; 1028 } 1029 1030 desktop_mode.refresh_rate_numerator = internal->refresh; 1031 desktop_mode.refresh_rate_denominator = 1000; 1032 1033 if (internal->display > 0) { 1034 dpy = SDL_GetVideoDisplay(internal->display); 1035 } else { 1036 dpy = &internal->placeholder; 1037 } 1038 1039 if (video->scale_to_display_enabled) { 1040 SDL_SetDisplayContentScale(dpy, (float)internal->scale_factor); 1041 } 1042 1043 // Set the desktop display mode. 1044 SDL_SetDesktopDisplayMode(dpy, &desktop_mode); 1045 1046 // Expose the unscaled, native resolution if the scale is 1.0 or viewports are available... 1047 if (internal->scale_factor == 1.0 || video->viewporter) { 1048 SDL_AddFullscreenDisplayMode(dpy, &native_mode); 1049 if (native_mode.w != desktop_mode.w || 1050 native_mode.h != desktop_mode.h) { 1051 SDL_AddFullscreenDisplayMode(dpy, &desktop_mode); 1052 } 1053 } else { 1054 // ...otherwise expose the integer scaled variants of the desktop resolution down to 1. 1055 int i; 1056 1057 desktop_mode.pixel_density = 1.0f; 1058 1059 for (i = (int)internal->scale_factor; i > 0; --i) { 1060 desktop_mode.w = internal->logical_width * i; 1061 desktop_mode.h = internal->logical_height * i; 1062 SDL_AddFullscreenDisplayMode(dpy, &desktop_mode); 1063 } 1064 } 1065 1066 // Add emulated modes if wp_viewporter is supported and mode emulation is enabled. 1067 if (video->viewporter && mode_emulation_enabled) { 1068 // The transformed display pixel width/height must be used here. 1069 AddEmulatedModes(internal, native_mode.w, native_mode.h); 1070 } 1071 1072 if (internal->display == 0) { 1073 // First time getting display info, initialize the VideoDisplay 1074 if (internal->physical_width_mm >= internal->physical_height_mm) { 1075 internal->placeholder.natural_orientation = SDL_ORIENTATION_LANDSCAPE; 1076 } else { 1077 internal->placeholder.natural_orientation = SDL_ORIENTATION_PORTRAIT; 1078 } 1079 internal->placeholder.current_orientation = internal->orientation; 1080 internal->placeholder.internal = internal; 1081 1082 internal->placeholder.props = SDL_CreateProperties(); 1083 SDL_SetPointerProperty(internal->placeholder.props, SDL_PROP_DISPLAY_WAYLAND_WL_OUTPUT_POINTER, internal->output); 1084 1085 // During initialization, the displays will be added after enumeration is complete. 1086 if (!video->initializing) { 1087 if (video->wp_color_manager_v1) { 1088 Wayland_GetColorInfoForOutput(internal, false); 1089 } 1090 internal->display = SDL_AddVideoDisplay(&internal->placeholder, true); 1091 SDL_free(internal->placeholder.name); 1092 SDL_zero(internal->placeholder); 1093 } 1094 } else { 1095 SDL_SendDisplayEvent(dpy, SDL_EVENT_DISPLAY_ORIENTATION, internal->orientation, 0); 1096 } 1097} 1098 1099static void handle_wl_output_scale(void *data, struct wl_output *output, int32_t factor) 1100{ 1101 SDL_DisplayData *internal = (SDL_DisplayData *)data; 1102 internal->scale_factor = factor; 1103} 1104 1105static void handle_wl_output_name(void *data, struct wl_output *wl_output, const char *name) 1106{ 1107 SDL_DisplayData *internal = (SDL_DisplayData *)data; 1108 1109 SDL_free(internal->wl_output_name); 1110 internal->wl_output_name = SDL_strdup(name); 1111} 1112 1113static void handle_wl_output_description(void *data, struct wl_output *wl_output, const char *description) 1114{ 1115 SDL_DisplayData *internal = (SDL_DisplayData *)data; 1116 1117 if (internal->display == 0) { 1118 // The description, if available, supersedes the model name. 1119 SDL_free(internal->placeholder.name); 1120 internal->placeholder.name = SDL_strdup(description); 1121 } 1122} 1123 1124static const struct wl_output_listener output_listener = { 1125 handle_wl_output_geometry, // Version 1 1126 handle_wl_output_mode, // Version 1 1127 handle_wl_output_done, // Version 2 1128 handle_wl_output_scale, // Version 2 1129 handle_wl_output_name, // Version 4 1130 handle_wl_output_description // Version 4 1131}; 1132 1133static void handle_output_image_description_changed(void *data, 1134 struct wp_color_management_output_v1 *wp_color_management_output_v1) 1135{ 1136 SDL_DisplayData *display = (SDL_DisplayData *)data; 1137 Wayland_GetColorInfoForOutput(display, false); 1138} 1139 1140static const struct wp_color_management_output_v1_listener wp_color_management_output_listener = { 1141 handle_output_image_description_changed 1142}; 1143 1144static bool Wayland_add_display(SDL_VideoData *d, uint32_t id, uint32_t version) 1145{ 1146 struct wl_output *output; 1147 SDL_DisplayData *data; 1148 1149 output = wl_registry_bind(d->registry, id, &wl_output_interface, version); 1150 if (!output) { 1151 return SDL_SetError("Failed to retrieve output."); 1152 } 1153 data = (SDL_DisplayData *)SDL_calloc(1, sizeof(*data)); 1154 data->videodata = d; 1155 data->output = output; 1156 data->registry_id = id; 1157 data->scale_factor = 1.0f; 1158 1159 wl_output_add_listener(output, &output_listener, data); 1160 SDL_WAYLAND_register_output(output); 1161 1162 // Keep a list of outputs for sorting and deferred protocol initialization. 1163 if (d->output_count == d->output_max) { 1164 d->output_max += 4; 1165 d->output_list = SDL_realloc(d->output_list, sizeof(SDL_DisplayData *) * d->output_max); 1166 } 1167 d->output_list[d->output_count++] = data; 1168 1169 if (data->videodata->xdg_output_manager) { 1170 data->xdg_output = zxdg_output_manager_v1_get_xdg_output(data->videodata->xdg_output_manager, output); 1171 zxdg_output_v1_add_listener(data->xdg_output, &xdg_output_listener, data); 1172 } 1173 if (data->videodata->wp_color_manager_v1) { 1174 data->wp_color_management_output = wp_color_manager_v1_get_output(data->videodata->wp_color_manager_v1, output); 1175 wp_color_management_output_v1_add_listener(data->wp_color_management_output, &wp_color_management_output_listener, data); 1176 1177 // If not initializing, this will be queried synchronously in wl_output.done. 1178 if (d->initializing) { 1179 Wayland_GetColorInfoForOutput(data, true); 1180 } 1181 } 1182 return true; 1183} 1184 1185static void Wayland_free_display(SDL_VideoDisplay *display, bool send_event) 1186{ 1187 if (display) { 1188 SDL_DisplayData *display_data = display->internal; 1189 1190 /* A preceding surface leave event is not guaranteed when an output is removed, 1191 * so ensure that no window continues to hold a reference to a removed output. 1192 */ 1193 for (SDL_Window *window = SDL_GetVideoDevice()->windows; window; window = window->next) { 1194 Wayland_RemoveOutputFromWindow(window->internal, display_data); 1195 } 1196 1197 SDL_free(display_data->wl_output_name); 1198 1199 if (display_data->wp_color_management_output) { 1200 Wayland_FreeColorInfoState(display_data->color_info_state); 1201 wp_color_management_output_v1_destroy(display_data->wp_color_management_output); 1202 } 1203 1204 if (display_data->xdg_output) { 1205 zxdg_output_v1_destroy(display_data->xdg_output); 1206 } 1207 1208 if (wl_output_get_version(display_data->output) >= WL_OUTPUT_RELEASE_SINCE_VERSION) { 1209 wl_output_release(display_data->output); 1210 } else { 1211 wl_output_destroy(display_data->output); 1212 } 1213 1214 SDL_DelVideoDisplay(display->id, send_event); 1215 } 1216} 1217 1218static void Wayland_FinalizeDisplays(SDL_VideoData *vid) 1219{ 1220 Wayland_SortOutputs(vid); 1221 for(int i = 0; i < vid->output_count; ++i) { 1222 SDL_DisplayData *d = vid->output_list[i]; 1223 d->display = SDL_AddVideoDisplay(&d->placeholder, false); 1224 SDL_free(d->placeholder.name); 1225 SDL_zero(d->placeholder); 1226 } 1227} 1228 1229static void Wayland_init_xdg_output(SDL_VideoData *d) 1230{ 1231 for (int i = 0; i < d->output_count; ++i) { 1232 SDL_DisplayData *disp = d->output_list[i]; 1233 disp->xdg_output = zxdg_output_manager_v1_get_xdg_output(disp->videodata->xdg_output_manager, disp->output); 1234 zxdg_output_v1_add_listener(disp->xdg_output, &xdg_output_listener, disp); 1235 } 1236} 1237 1238static void Wayland_InitColorManager(SDL_VideoData *d) 1239{ 1240 for (int i = 0; i < d->output_count; ++i) { 1241 SDL_DisplayData *disp = d->output_list[i]; 1242 disp->wp_color_management_output = wp_color_manager_v1_get_output(disp->videodata->wp_color_manager_v1, disp->output); 1243 wp_color_management_output_v1_add_listener(disp->wp_color_management_output, &wp_color_management_output_listener, disp); 1244 Wayland_GetColorInfoForOutput(disp, true); 1245 } 1246} 1247 1248static void handle_xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg, uint32_t serial) 1249{ 1250 xdg_wm_base_pong(xdg, serial); 1251} 1252 1253static const struct xdg_wm_base_listener _xdg_wm_base_listener = { 1254 handle_xdg_wm_base_ping 1255}; 1256 1257#ifdef HAVE_LIBDECOR_H 1258static void libdecor_error(struct libdecor *context, 1259 enum libdecor_error error, 1260 const char *message) 1261{ 1262 SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "libdecor error (%d): %s", error, message); 1263} 1264 1265static struct libdecor_interface libdecor_interface = { 1266 libdecor_error 1267}; 1268#endif 1269 1270static void handle_registry_global(void *data, struct wl_registry *registry, uint32_t id, 1271 const char *interface, uint32_t version) 1272{ 1273 SDL_VideoData *d = data; 1274 1275 // printf("WAYLAND INTERFACE: %s\n", interface); 1276 1277 if (SDL_strcmp(interface, "wl_compositor") == 0) { 1278 d->compositor = wl_registry_bind(d->registry, id, &wl_compositor_interface, SDL_min(SDL_WL_COMPOSITOR_VERSION, version)); 1279 } else if (SDL_strcmp(interface, "wl_output") == 0) { 1280 Wayland_add_display(d, id, SDL_min(version, SDL_WL_OUTPUT_VERSION)); 1281 } else if (SDL_strcmp(interface, "wl_seat") == 0) { 1282 struct wl_seat *seat = wl_registry_bind(d->registry, id, &wl_seat_interface, SDL_min(SDL_WL_SEAT_VERSION, version)); 1283 Wayland_DisplayCreateSeat(d, seat, id); 1284 } else if (SDL_strcmp(interface, "xdg_wm_base") == 0) { 1285 d->shell.xdg = wl_registry_bind(d->registry, id, &xdg_wm_base_interface, SDL_min(version, 7)); 1286 xdg_wm_base_add_listener(d->shell.xdg, &_xdg_wm_base_listener, NULL); 1287 } else if (SDL_strcmp(interface, "wl_shm") == 0) { 1288 d->shm = wl_registry_bind(registry, id, &wl_shm_interface, SDL_min(SDL_WL_SHM_VERSION, version)); 1289 } else if (SDL_strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) { 1290 d->relative_pointer_manager = wl_registry_bind(d->registry, id, &zwp_relative_pointer_manager_v1_interface, 1); 1291 } else if (SDL_strcmp(interface, "zwp_pointer_constraints_v1") == 0) { 1292 d->pointer_constraints = wl_registry_bind(d->registry, id, &zwp_pointer_constraints_v1_interface, 1); 1293 } else if (SDL_strcmp(interface, "zwp_keyboard_shortcuts_inhibit_manager_v1") == 0) { 1294 d->key_inhibitor_manager = wl_registry_bind(d->registry, id, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1); 1295 } else if (SDL_strcmp(interface, "zwp_idle_inhibit_manager_v1") == 0) { 1296 d->idle_inhibit_manager = wl_registry_bind(d->registry, id, &zwp_idle_inhibit_manager_v1_interface, 1); 1297 } else if (SDL_strcmp(interface, "xdg_activation_v1") == 0) { 1298 d->activation_manager = wl_registry_bind(d->registry, id, &xdg_activation_v1_interface, 1); 1299 } else if (SDL_strcmp(interface, "zwp_text_input_manager_v3") == 0) { 1300 d->text_input_manager = wl_registry_bind(d->registry, id, &zwp_text_input_manager_v3_interface, 1); 1301 Wayland_DisplayInitTextInputManager(d, id); 1302 } else if (SDL_strcmp(interface, "wl_data_device_manager") == 0) { 1303 d->data_device_manager = wl_registry_bind(d->registry, id, &wl_data_device_manager_interface, SDL_min(3, version)); 1304 Wayland_DisplayInitDataDeviceManager(d); 1305 } else if (SDL_strcmp(interface, "zwp_primary_selection_device_manager_v1") == 0) { 1306 d->primary_selection_device_manager = wl_registry_bind(d->registry, id, &zwp_primary_selection_device_manager_v1_interface, 1); 1307 Wayland_DisplayInitPrimarySelectionDeviceManager(d); 1308 } else if (SDL_strcmp(interface, "zxdg_decoration_manager_v1") == 0) { 1309 d->decoration_manager = wl_registry_bind(d->registry, id, &zxdg_decoration_manager_v1_interface, 1); 1310 } else if (SDL_strcmp(interface, "zwp_tablet_manager_v2") == 0) { 1311 d->tablet_manager = wl_registry_bind(d->registry, id, &zwp_tablet_manager_v2_interface, 1); 1312 Wayland_DisplayInitTabletManager(d); 1313 } else if (SDL_strcmp(interface, "zxdg_output_manager_v1") == 0) { 1314 version = SDL_min(version, 3); // Versions 1 through 3 are supported. 1315 d->xdg_output_manager = wl_registry_bind(d->registry, id, &zxdg_output_manager_v1_interface, version); 1316 Wayland_init_xdg_output(d); 1317 } else if (SDL_strcmp(interface, "wp_viewporter") == 0) { 1318 d->viewporter = wl_registry_bind(d->registry, id, &wp_viewporter_interface, 1); 1319 } else if (SDL_strcmp(interface, "wp_fractional_scale_manager_v1") == 0) { 1320 d->fractional_scale_manager = wl_registry_bind(d->registry, id, &wp_fractional_scale_manager_v1_interface, 1); 1321 } else if (SDL_strcmp(interface, "zwp_input_timestamps_manager_v1") == 0) { 1322 d->input_timestamps_manager = wl_registry_bind(d->registry, id, &zwp_input_timestamps_manager_v1_interface, 1); 1323 Wayland_DisplayInitInputTimestampManager(d); 1324 } else if (SDL_strcmp(interface, "wp_cursor_shape_manager_v1") == 0) { 1325 d->cursor_shape_manager = wl_registry_bind(d->registry, id, &wp_cursor_shape_manager_v1_interface, 1); 1326 Wayland_DisplayInitCursorShapeManager(d); 1327 } else if (SDL_strcmp(interface, "zxdg_exporter_v2") == 0) { 1328 d->zxdg_exporter_v2 = wl_registry_bind(d->registry, id, &zxdg_exporter_v2_interface, 1); 1329 } else if (SDL_strcmp(interface, "xdg_wm_dialog_v1") == 0) { 1330 d->xdg_wm_dialog_v1 = wl_registry_bind(d->registry, id, &xdg_wm_dialog_v1_interface, 1); 1331 } else if (SDL_strcmp(interface, "wp_alpha_modifier_v1") == 0) { 1332 d->wp_alpha_modifier_v1 = wl_registry_bind(d->registry, id, &wp_alpha_modifier_v1_interface, 1); 1333 } else if (SDL_strcmp(interface, "xdg_toplevel_icon_manager_v1") == 0) { 1334 d->xdg_toplevel_icon_manager_v1 = wl_registry_bind(d->registry, id, &xdg_toplevel_icon_manager_v1_interface, 1); 1335 } else if (SDL_strcmp(interface, "frog_color_management_factory_v1") == 0) { 1336 d->frog_color_management_factory_v1 = wl_registry_bind(d->registry, id, &frog_color_management_factory_v1_interface, 1); 1337 } else if (SDL_strcmp(interface, "wp_color_manager_v1") == 0) { 1338 d->wp_color_manager_v1 = wl_registry_bind(d->registry, id, &wp_color_manager_v1_interface, SDL_min(version, 2)); 1339 Wayland_InitColorManager(d); 1340 } else if (SDL_strcmp(interface, "wp_pointer_warp_v1") == 0) { 1341 d->wp_pointer_warp_v1 = wl_registry_bind(d->registry, id, &wp_pointer_warp_v1_interface, 1); 1342 } else if (SDL_strcmp(interface, "zwp_pointer_gestures_v1") == 0) { 1343 d->zwp_pointer_gestures = wl_registry_bind(d->registry, id, &zwp_pointer_gestures_v1_interface, SDL_min(version, 3)); 1344 Wayland_DisplayInitPointerGestureManager(d); 1345 } 1346#ifdef SDL_WL_FIXES_VERSION 1347 else if (SDL_strcmp(interface, "wl_fixes") == 0) { 1348 d->wl_fixes = wl_registry_bind(d->registry, id, &wl_fixes_interface, SDL_min(SDL_WL_FIXES_VERSION, version)); 1349 } 1350#endif 1351} 1352 1353static void handle_registry_remove_global(void *data, struct wl_registry *registry, uint32_t id) 1354{ 1355 SDL_VideoData *d = data; 1356 1357 // We don't get an interface, just an ID, so check outputs and seats. 1358 for (int i = 0; i < d->output_count; ++i) { 1359 SDL_DisplayData *disp = d->output_list[i]; 1360 if (disp->registry_id == id) { 1361 Wayland_free_display(SDL_GetVideoDisplay(disp->display), true); 1362 1363 if (i < d->output_count) { 1364 SDL_memmove(&d->output_list[i], &d->output_list[i + 1], sizeof(SDL_DisplayData *) * (d->output_count - i - 1)); 1365 } 1366 1367 d->output_count--; 1368 return; 1369 } 1370 } 1371 1372 struct SDL_WaylandSeat *seat, *temp; 1373 wl_list_for_each_safe (seat, temp, &d->seat_list, link) 1374 { 1375 if (seat->registry_id == id) { 1376 if (seat->keyboard.wl_keyboard) { 1377 SDL_RemoveKeyboard(seat->keyboard.sdl_id); 1378 } 1379 if (seat->pointer.wl_pointer) { 1380 SDL_RemoveMouse(seat->pointer.sdl_id); 1381 } 1382 Wayland_SeatDestroy(seat, false); 1383 } 1384 } 1385} 1386 1387static const struct wl_registry_listener registry_listener = { 1388 handle_registry_global, 1389 handle_registry_remove_global 1390}; 1391 1392#ifdef HAVE_LIBDECOR_H 1393static bool should_use_libdecor(SDL_VideoData *data, bool ignore_xdg) 1394{ 1395 if (!SDL_WAYLAND_HAVE_WAYLAND_LIBDECOR) { 1396 return false; 1397 } 1398 1399 if (!SDL_GetHintBoolean(SDL_HINT_VIDEO_WAYLAND_ALLOW_LIBDECOR, true)) { 1400 return false; 1401 } 1402 1403 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_WAYLAND_PREFER_LIBDECOR, false)) { 1404 return true; 1405 } 1406 1407 if (ignore_xdg) { 1408 return true; 1409 } 1410 1411 if (data->decoration_manager) { 1412 return false; 1413 } 1414 1415 return true; 1416} 1417#endif 1418 1419bool Wayland_LoadLibdecor(SDL_VideoData *data, bool ignore_xdg) 1420{ 1421#ifdef HAVE_LIBDECOR_H 1422 if (data->shell.libdecor != NULL) { 1423 return true; // Already loaded! 1424 } 1425 if (should_use_libdecor(data, ignore_xdg)) { 1426 data->shell.libdecor = libdecor_new(data->display, &libdecor_interface); 1427 return data->shell.libdecor != NULL; 1428 } 1429#endif 1430 return false; 1431} 1432 1433bool Wayland_VideoInit(SDL_VideoDevice *_this) 1434{ 1435 SDL_VideoData *data = _this->internal; 1436 1437 data->xkb_context = WAYLAND_xkb_context_new(0); 1438 if (!data->xkb_context) { 1439 return SDL_SetError("Failed to create XKB context"); 1440 } 1441 1442 data->registry = wl_display_get_registry(data->display); 1443 if (!data->registry) { 1444 return SDL_SetError("Failed to get the Wayland registry"); 1445 } 1446 1447 wl_registry_add_listener(data->registry, ®istry_listener, data); 1448 1449 // First roundtrip to receive all registry objects. 1450 WAYLAND_wl_display_roundtrip(data->display); 1451 1452 // Require viewports and xdg-output for display scaling. 1453 if (data->scale_to_display_enabled) { 1454 if (!data->viewporter) { 1455 SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "wayland: Display scaling requires the missing 'wp_viewporter' protocol: disabling"); 1456 data->scale_to_display_enabled = false; 1457 } 1458 if (!data->xdg_output_manager) { 1459 SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "wayland: Display scaling requires the missing 'zxdg_output_manager_v1' protocol: disabling"); 1460 data->scale_to_display_enabled = false; 1461 } 1462 } 1463 1464 // Now that we have all the protocols, load libdecor if applicable 1465 Wayland_LoadLibdecor(data, false); 1466 1467 // Second roundtrip to receive all output events. 1468 WAYLAND_wl_display_roundtrip(data->display); 1469 1470 Wayland_FinalizeDisplays(data); 1471 1472 Wayland_InitMouse(data); 1473 Wayland_InitKeyboard(_this); 1474 1475 if (data->primary_selection_device_manager) { 1476 _this->SetPrimarySelectionText = Wayland_SetPrimarySelectionText; 1477 _this->GetPrimarySelectionText = Wayland_GetPrimarySelectionText; 1478 _this->HasPrimarySelectionText = Wayland_HasPrimarySelectionText; 1479 } 1480 1481 data->initializing = false; 1482 1483 return true; 1484} 1485 1486static bool Wayland_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect) 1487{ 1488 SDL_VideoData *viddata = _this->internal; 1489 SDL_DisplayData *internal = display->internal; 1490 rect->x = internal->x; 1491 rect->y = internal->y; 1492 1493 // When an emulated, exclusive fullscreen window has focus, treat the mode dimensions as the display bounds. 1494 if (display->fullscreen_window && 1495 display->fullscreen_window->fullscreen_exclusive && 1496 display->fullscreen_window->internal->active && 1497 display->fullscreen_window->current_fullscreen_mode.w != 0 && 1498 display->fullscreen_window->current_fullscreen_mode.h != 0) { 1499 rect->w = display->fullscreen_window->current_fullscreen_mode.w; 1500 rect->h = display->fullscreen_window->current_fullscreen_mode.h; 1501 } else { 1502 if (!viddata->scale_to_display_enabled) { 1503 rect->w = display->current_mode->w; 1504 rect->h = display->current_mode->h; 1505 } else if (internal->transform & WL_OUTPUT_TRANSFORM_90) { 1506 rect->w = internal->pixel_height; 1507 rect->h = internal->pixel_width; 1508 } else { 1509 rect->w = internal->pixel_width; 1510 rect->h = internal->pixel_height; 1511 } 1512 } 1513 return true; 1514} 1515 1516static void Wayland_VideoCleanup(SDL_VideoDevice *_this) 1517{ 1518 SDL_VideoData *data = _this->internal; 1519 SDL_WaylandSeat *seat, *tmp; 1520 1521 for (int i = _this->num_displays - 1; i >= 0; --i) { 1522 SDL_VideoDisplay *display = _this->displays[i]; 1523 Wayland_free_display(display, false); 1524 } 1525 SDL_free(data->output_list); 1526 1527 wl_list_for_each_safe (seat, tmp, &data->seat_list, link) { 1528 Wayland_SeatDestroy(seat, true); 1529 } 1530 1531 Wayland_FiniMouse(data); 1532 1533 if (data->pointer_constraints) { 1534 zwp_pointer_constraints_v1_destroy(data->pointer_constraints); 1535 data->pointer_constraints = NULL; 1536 } 1537 1538 if (data->relative_pointer_manager) { 1539 zwp_relative_pointer_manager_v1_destroy(data->relative_pointer_manager); 1540 data->relative_pointer_manager = NULL; 1541 } 1542 1543 if (data->activation_manager) { 1544 xdg_activation_v1_destroy(data->activation_manager); 1545 data->activation_manager = NULL; 1546 } 1547 1548 if (data->idle_inhibit_manager) { 1549 zwp_idle_inhibit_manager_v1_destroy(data->idle_inhibit_manager); 1550 data->idle_inhibit_manager = NULL; 1551 } 1552 1553 if (data->key_inhibitor_manager) { 1554 zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(data->key_inhibitor_manager); 1555 data->key_inhibitor_manager = NULL; 1556 } 1557 1558 Wayland_QuitKeyboard(_this); 1559 1560 if (data->text_input_manager) { 1561 zwp_text_input_manager_v3_destroy(data->text_input_manager); 1562 data->text_input_manager = NULL; 1563 } 1564 1565 if (data->xkb_context) { 1566 WAYLAND_xkb_context_unref(data->xkb_context); 1567 data->xkb_context = NULL; 1568 } 1569 1570 if (data->tablet_manager) { 1571 zwp_tablet_manager_v2_destroy((struct zwp_tablet_manager_v2 *)data->tablet_manager); 1572 data->tablet_manager = NULL; 1573 } 1574 1575 if (data->data_device_manager) { 1576 wl_data_device_manager_destroy(data->data_device_manager); 1577 data->data_device_manager = NULL; 1578 } 1579 1580 if (data->shm) { 1581 if (wl_shm_get_version(data->shm) >= WL_SHM_RELEASE_SINCE_VERSION) { 1582 wl_shm_release(data->shm); 1583 } else { 1584 wl_shm_destroy(data->shm); 1585 } 1586 data->shm = NULL; 1587 } 1588 1589 if (data->shell.xdg) { 1590 xdg_wm_base_destroy(data->shell.xdg); 1591 data->shell.xdg = NULL; 1592 } 1593 1594 if (data->decoration_manager) { 1595 zxdg_decoration_manager_v1_destroy(data->decoration_manager); 1596 data->decoration_manager = NULL; 1597 } 1598 1599 if (data->xdg_output_manager) { 1600 zxdg_output_manager_v1_destroy(data->xdg_output_manager); 1601 data->xdg_output_manager = NULL; 1602 } 1603 1604 if (data->viewporter) { 1605 wp_viewporter_destroy(data->viewporter); 1606 data->viewporter = NULL; 1607 } 1608 1609 if (data->primary_selection_device_manager) { 1610 zwp_primary_selection_device_manager_v1_destroy(data->primary_selection_device_manager); 1611 data->primary_selection_device_manager = NULL; 1612 } 1613 1614 if (data->fractional_scale_manager) { 1615 wp_fractional_scale_manager_v1_destroy(data->fractional_scale_manager); 1616 data->fractional_scale_manager = NULL; 1617 } 1618 1619 if (data->input_timestamps_manager) { 1620 zwp_input_timestamps_manager_v1_destroy(data->input_timestamps_manager); 1621 data->input_timestamps_manager = NULL; 1622 } 1623 1624 if (data->cursor_shape_manager) { 1625 wp_cursor_shape_manager_v1_destroy(data->cursor_shape_manager); 1626 data->cursor_shape_manager = NULL; 1627 } 1628 1629 if (data->zxdg_exporter_v2) { 1630 zxdg_exporter_v2_destroy(data->zxdg_exporter_v2); 1631 data->zxdg_exporter_v2 = NULL; 1632 } 1633 1634 if (data->xdg_wm_dialog_v1) { 1635 xdg_wm_dialog_v1_destroy(data->xdg_wm_dialog_v1); 1636 data->xdg_wm_dialog_v1 = NULL; 1637 } 1638 1639 if (data->wp_alpha_modifier_v1) { 1640 wp_alpha_modifier_v1_destroy(data->wp_alpha_modifier_v1); 1641 data->wp_alpha_modifier_v1 = NULL; 1642 } 1643 1644 if (data->xdg_toplevel_icon_manager_v1) { 1645 xdg_toplevel_icon_manager_v1_destroy(data->xdg_toplevel_icon_manager_v1); 1646 data->xdg_toplevel_icon_manager_v1 = NULL; 1647 } 1648 1649 if (data->frog_color_management_factory_v1) { 1650 frog_color_management_factory_v1_destroy(data->frog_color_management_factory_v1); 1651 data->frog_color_management_factory_v1 = NULL; 1652 } 1653 1654 if (data->wp_color_manager_v1) { 1655 wp_color_manager_v1_destroy(data->wp_color_manager_v1); 1656 data->wp_color_manager_v1 = NULL; 1657 } 1658 1659 if (data->wp_pointer_warp_v1) { 1660 wp_pointer_warp_v1_destroy(data->wp_pointer_warp_v1); 1661 data->wp_pointer_warp_v1 = NULL; 1662 } 1663 1664 if (data->zwp_pointer_gestures) { 1665 if (zwp_pointer_gestures_v1_get_version(data->zwp_pointer_gestures) >= ZWP_POINTER_GESTURES_V1_RELEASE_SINCE_VERSION) { 1666 zwp_pointer_gestures_v1_release(data->zwp_pointer_gestures); 1667 } else { 1668 zwp_pointer_gestures_v1_destroy(data->zwp_pointer_gestures); 1669 } 1670 data->zwp_pointer_gestures = NULL; 1671 } 1672 1673 if (data->compositor) { 1674 wl_compositor_destroy(data->compositor); 1675 data->compositor = NULL; 1676 } 1677 1678 if (data->registry) { 1679 if (data->wl_fixes) { 1680 wl_fixes_destroy_registry(data->wl_fixes, data->registry); 1681 wl_fixes_destroy(data->wl_fixes); 1682 data->wl_fixes = NULL; 1683 } 1684 wl_registry_destroy(data->registry); 1685 data->registry = NULL; 1686 } 1687} 1688 1689static bool Wayland_VideoReconnect(SDL_VideoDevice *_this) 1690{ 1691#if 0 // TODO RECONNECT: Uncomment all when https://invent.kde.org/plasma/kwin/-/wikis/Restarting is completed 1692 SDL_VideoData *data = _this->internal; 1693 1694 SDL_Window *window = NULL; 1695 1696 SDL_GLContext current_ctx = SDL_GL_GetCurrentContext(); 1697 SDL_Window *current_window = SDL_GL_GetCurrentWindow(); 1698 1699 SDL_GL_MakeCurrent(NULL, NULL); 1700 Wayland_VideoCleanup(_this); 1701 1702 SDL_ResetKeyboard(); 1703 SDL_ResetMouse(); 1704 if (WAYLAND_wl_display_reconnect(data->display) < 0) { 1705 return false; 1706 } 1707 1708 Wayland_VideoInit(_this); 1709 1710 window = _this->windows; 1711 while (window) { 1712 /* We're going to cheat _just_ for a second and strip the OpenGL flag. 1713 * The Wayland driver actually forces it in CreateWindow, and 1714 * RecreateWindow does a bunch of unloading/loading of libGL, so just 1715 * strip the flag so RecreateWindow doesn't mess with the GL context, 1716 * and CreateWindow will add it right back! 1717 * -flibit 1718 */ 1719 window->flags &= ~SDL_WINDOW_OPENGL; 1720 1721 SDL_RecreateWindow(window, window->flags); 1722 window = window->next; 1723 } 1724 1725 Wayland_RecreateCursors(); 1726 1727 if (current_window && current_ctx) { 1728 SDL_GL_MakeCurrent (current_window, current_ctx); 1729 } 1730 return true; 1731#else 1732 return false; 1733#endif // 0 1734} 1735 1736bool Wayland_HandleDisplayDisconnected(SDL_VideoDevice *_this) 1737{ 1738 SDL_VideoData *video_data = _this->internal; 1739 1740 /* Something has failed with the Wayland connection -- for example, 1741 * the compositor may have shut down and closed its end of the socket, 1742 * or there is a library-specific error. 1743 * 1744 * Try to recover once, then quit. 1745 */ 1746 if (video_data->display_disconnected) { 1747 return false; 1748 } 1749 1750 if (Wayland_VideoReconnect(_this)) { 1751 return true; 1752 } 1753 1754 video_data->display_disconnected = true; 1755 SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Wayland display connection closed by server (fatal)"); 1756 1757 // Only send a single quit message, as application shutdown might call SDL_PumpEvents(). 1758 SDL_SendQuit(); 1759 1760 return false; 1761} 1762 1763void Wayland_VideoQuit(SDL_VideoDevice *_this) 1764{ 1765 Wayland_VideoCleanup(_this); 1766 1767#ifdef HAVE_LIBDECOR_H 1768 SDL_VideoData *data = _this->internal; 1769 if (data->shell.libdecor) { 1770 libdecor_unref(data->shell.libdecor); 1771 data->shell.libdecor = NULL; 1772 } 1773#endif 1774} 1775 1776#endif // SDL_VIDEO_DRIVER_WAYLAND 1777[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.