Atlas - SDL_video.c
Home / ext / SDL / src / video Lines: 1 | Size: 196810 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 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// The high-level video driver subsystem 25 26#include "SDL_sysvideo.h" 27#include "SDL_clipboard_c.h" 28#include "SDL_egl_c.h" 29#include "SDL_surface_c.h" 30#include "SDL_pixels_c.h" 31#include "SDL_rect_c.h" 32#include "SDL_video_c.h" 33#include "../events/SDL_events_c.h" 34#include "../SDL_hints_c.h" 35#include "../SDL_properties_c.h" 36#include "../timer/SDL_timer_c.h" 37#include "../camera/SDL_camera_c.h" 38#include "../render/SDL_sysrender.h" 39#include "../main/SDL_main_callbacks.h" 40 41#ifdef SDL_VIDEO_OPENGL 42#include <SDL3/SDL_opengl.h> 43#endif // SDL_VIDEO_OPENGL 44 45#if defined(SDL_VIDEO_OPENGL_ES) && !defined(SDL_VIDEO_OPENGL) 46#include <SDL3/SDL_opengles.h> 47#endif // SDL_VIDEO_OPENGL_ES && !SDL_VIDEO_OPENGL 48 49// GL and GLES2 headers conflict on Linux 32 bits 50#if defined(SDL_VIDEO_OPENGL_ES2) && !defined(SDL_VIDEO_OPENGL) 51#include <SDL3/SDL_opengles2.h> 52#endif // SDL_VIDEO_OPENGL_ES2 && !SDL_VIDEO_OPENGL 53 54// GL_CONTEXT_RELEASE_BEHAVIOR and GL_CONTEXT_RELEASE_BEHAVIOR_KHR have the same number. 55#ifndef GL_CONTEXT_RELEASE_BEHAVIOR 56#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB 57#endif 58 59// GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH and GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR have the same number. 60#ifndef GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 61#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC 62#endif 63 64// This is always the same number between the various EXT/ARB/GLES extensions. 65#ifndef GL_FRAMEBUFFER_SRGB 66#define GL_FRAMEBUFFER_SRGB 0x8DB9 67#endif 68 69#ifdef SDL_PLATFORM_EMSCRIPTEN 70#include <emscripten.h> 71#endif 72 73#ifdef SDL_PLATFORM_3DS 74#include <3ds.h> 75#endif 76 77#ifndef GL_RGBA_FLOAT_MODE_ARB 78#define GL_RGBA_FLOAT_MODE_ARB 0x8820 79#endif /* GL_RGBA_FLOAT_MODE_ARB */ 80 81// Available video drivers 82static VideoBootStrap *bootstrap[] = { 83#ifdef SDL_VIDEO_DRIVER_PRIVATE 84 &PRIVATE_bootstrap, 85#endif 86#ifdef SDL_VIDEO_DRIVER_COCOA 87 &COCOA_bootstrap, 88#endif 89#ifdef SDL_VIDEO_DRIVER_X11 90#ifdef SDL_VIDEO_DRIVER_WAYLAND 91 &Wayland_preferred_bootstrap, 92#endif 93 &X11_bootstrap, 94#endif 95#ifdef SDL_VIDEO_DRIVER_WAYLAND 96 &Wayland_bootstrap, 97#endif 98#ifdef SDL_VIDEO_DRIVER_VIVANTE 99 &VIVANTE_bootstrap, 100#endif 101#ifdef SDL_VIDEO_DRIVER_WINDOWS 102 &WINDOWS_bootstrap, 103#endif 104#ifdef SDL_VIDEO_DRIVER_HAIKU 105 &HAIKU_bootstrap, 106#endif 107#ifdef SDL_VIDEO_DRIVER_UIKIT 108 &UIKIT_bootstrap, 109#endif 110#ifdef SDL_VIDEO_DRIVER_ANDROID 111 &Android_bootstrap, 112#endif 113#ifdef SDL_VIDEO_DRIVER_PS2 114 &PS2_bootstrap, 115#endif 116#ifdef SDL_VIDEO_DRIVER_PSP 117 &PSP_bootstrap, 118#endif 119#ifdef SDL_VIDEO_DRIVER_VITA 120 &VITA_bootstrap, 121#endif 122#ifdef SDL_VIDEO_DRIVER_N3DS 123 &N3DS_bootstrap, 124#endif 125#ifdef SDL_VIDEO_DRIVER_NGAGE 126 &NGAGE_bootstrap, 127#endif 128#ifdef SDL_VIDEO_DRIVER_KMSDRM 129 &KMSDRM_bootstrap, 130#endif 131#ifdef SDL_VIDEO_DRIVER_RISCOS 132 &RISCOS_bootstrap, 133#endif 134#ifdef SDL_VIDEO_DRIVER_RPI 135 &RPI_bootstrap, 136#endif 137#ifdef SDL_VIDEO_DRIVER_EMSCRIPTEN 138 &Emscripten_bootstrap, 139#endif 140#ifdef SDL_VIDEO_DRIVER_QNX 141 &QNX_bootstrap, 142#endif 143#ifdef SDL_VIDEO_DRIVER_OFFSCREEN 144 &OFFSCREEN_bootstrap, 145#endif 146#ifdef SDL_VIDEO_DRIVER_DUMMY 147 &DUMMY_bootstrap, 148#ifdef SDL_INPUT_LINUXEV 149 &DUMMY_evdev_bootstrap, 150#endif 151#endif 152#ifdef SDL_VIDEO_DRIVER_OPENVR 153 &OPENVR_bootstrap, 154#endif 155 NULL 156}; 157 158#define CHECK_WINDOW_MAGIC(window, result) \ 159 CHECK_PARAM(!_this) { \ 160 SDL_UninitializedVideo(); \ 161 return result; \ 162 } \ 163 CHECK_PARAM(!SDL_ObjectValid(window, SDL_OBJECT_TYPE_WINDOW)) { \ 164 SDL_SetError("Invalid window"); \ 165 return result; \ 166 } 167 168#define CHECK_DISPLAY_MAGIC(display, result) \ 169 CHECK_PARAM(!display) { \ 170 return result; \ 171 } \ 172 173#define CHECK_WINDOW_NOT_POPUP(window, result) \ 174 CHECK_PARAM(SDL_WINDOW_IS_POPUP(window)) { \ 175 SDL_SetError("Operation invalid on popup windows"); \ 176 return result; \ 177 } 178 179#if defined(SDL_PLATFORM_MACOS) && defined(SDL_VIDEO_DRIVER_COCOA) 180// Support for macOS fullscreen spaces, etc. 181extern bool Cocoa_IsWindowInFullscreenSpace(SDL_Window *window); 182extern bool Cocoa_IsWindowInFullscreenSpaceTransition(SDL_Window *window); 183extern bool Cocoa_SetWindowFullscreenSpace(SDL_Window *window, bool state, bool blocking); 184extern bool Cocoa_IsShowingModalDialog(SDL_Window *window); 185#endif 186 187#ifdef SDL_VIDEO_DRIVER_UIKIT 188extern void SDL_UpdateLifecycleObserver(void); 189#endif 190 191static void SDL_CheckWindowDisplayChanged(SDL_Window *window); 192static void SDL_CheckWindowDisplayScaleChanged(SDL_Window *window); 193static void SDL_CheckWindowSafeAreaChanged(SDL_Window *window); 194 195// Convenience functions for reading driver flags 196static bool SDL_ModeSwitchingEmulated(SDL_VideoDevice *_this) 197{ 198 if (_this->device_caps & VIDEO_DEVICE_CAPS_MODE_SWITCHING_EMULATED) { 199 return true; 200 } 201 return false; 202} 203 204static bool SDL_SendsFullscreenDimensions(SDL_VideoDevice *_this) 205{ 206 return !!(_this->device_caps & VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS); 207} 208 209static bool IsFullscreenOnly(SDL_VideoDevice *_this) 210{ 211 return !!(_this->device_caps & VIDEO_DEVICE_CAPS_FULLSCREEN_ONLY); 212} 213 214static bool SDL_SendsDisplayChanges(SDL_VideoDevice *_this) 215{ 216 return !!(_this->device_caps & VIDEO_DEVICE_CAPS_SENDS_DISPLAY_CHANGES); 217} 218 219static bool SDL_DriverSendsHDRChanges(SDL_VideoDevice *_this) 220{ 221 return !!(_this->device_caps & VIDEO_DEVICE_CAPS_SENDS_HDR_CHANGES); 222} 223 224static bool SDL_DriverHasSlowFramebuffer(SDL_VideoDevice *_this) 225{ 226 return !!(_this->device_caps & VIDEO_DEVICE_CAPS_SLOW_FRAMEBUFFER); 227} 228 229// Hint to treat all window ops as synchronous 230static bool syncHint; 231 232static void SDL_SyncHintWatcher(void *userdata, const char *name, const char *oldValue, const char *newValue) 233{ 234 syncHint = SDL_GetStringBoolean(newValue, false); 235} 236 237static void SDL_SyncIfRequired(SDL_Window *window) 238{ 239 if (syncHint) { 240 SDL_SyncWindow(window); 241 } 242} 243 244static void SDL_UpdateWindowHierarchy(SDL_Window *window, SDL_Window *parent) 245{ 246 // Unlink the window from the existing parent. 247 if (window->parent) { 248 if (window->next_sibling) { 249 window->next_sibling->prev_sibling = window->prev_sibling; 250 } 251 if (window->prev_sibling) { 252 window->prev_sibling->next_sibling = window->next_sibling; 253 } else { 254 window->parent->first_child = window->next_sibling; 255 } 256 257 window->parent = NULL; 258 } 259 260 if (parent) { 261 window->parent = parent; 262 263 window->next_sibling = parent->first_child; 264 if (parent->first_child) { 265 parent->first_child->prev_sibling = window; 266 } 267 parent->first_child = window; 268 } 269} 270 271// Support for framebuffer emulation using an accelerated renderer 272 273#define SDL_PROP_WINDOW_TEXTUREDATA_POINTER "SDL.internal.window.texturedata" 274 275typedef struct 276{ 277 SDL_Renderer *renderer; 278 SDL_Texture *texture; 279 void *pixels; 280 int pitch; 281 int bytes_per_pixel; 282} SDL_WindowTextureData; 283 284static Uint32 SDL_DefaultGraphicsBackends(SDL_VideoDevice *_this) 285{ 286#if (defined(SDL_VIDEO_OPENGL) && defined(SDL_PLATFORM_MACOS)) || (defined(SDL_PLATFORM_IOS) && !TARGET_OS_MACCATALYST) || defined(SDL_PLATFORM_QNXNTO) 287 if (_this->GL_CreateContext) { 288 return SDL_WINDOW_OPENGL; 289 } 290#endif 291#if defined(SDL_VIDEO_METAL) && (TARGET_OS_MACCATALYST || defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS)) 292 if (_this->Metal_CreateView) { 293 return SDL_WINDOW_METAL; 294 } 295#endif 296#if defined(SDL_VIDEO_OPENGL) && defined(SDL_VIDEO_DRIVER_OPENVR) 297 if (SDL_strcmp(_this->name, "openvr") == 0) { 298 return SDL_WINDOW_OPENGL; 299 } 300#endif 301 return 0; 302} 303 304static bool SDL_CreateWindowTexture(SDL_VideoDevice *_this, SDL_Window *window, SDL_PixelFormat *format, void **pixels, int *pitch) 305{ 306 SDL_PropertiesID props = SDL_GetWindowProperties(window); 307 SDL_WindowTextureData *data = (SDL_WindowTextureData *)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_TEXTUREDATA_POINTER, NULL); 308 const bool transparent = (window->flags & SDL_WINDOW_TRANSPARENT) ? true : false; 309 int i; 310 int w, h; 311 const SDL_PixelFormat *texture_formats; 312 313 SDL_GetWindowSizeInPixels(window, &w, &h); 314 315 if (!data) { 316 SDL_Renderer *renderer = NULL; 317 const char *render_driver = NULL; 318 319 // See if there's a render driver being requested 320 const char *hint = SDL_GetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION); 321 if (hint && *hint != '0' && *hint != '1' && 322 SDL_strcasecmp(hint, "true") != 0 && 323 SDL_strcasecmp(hint, "false") != 0 && 324 SDL_strcasecmp(hint, SDL_SOFTWARE_RENDERER) != 0) { 325 render_driver = hint; 326 } 327 328 if (!render_driver) { 329 render_driver = SDL_GetHint(SDL_HINT_RENDER_DRIVER); 330 } 331 if (render_driver && SDL_strcasecmp(render_driver, SDL_SOFTWARE_RENDERER) == 0) { 332 render_driver = NULL; 333 } 334 335 char *render_driver_copy = NULL; 336 if (render_driver && *render_driver) { 337 render_driver_copy = SDL_strdup(render_driver); 338 render_driver = render_driver_copy; 339 if (render_driver_copy) { // turn any "software" requests into "xxxxxxxx" so we don't end up in infinite recursion. 340 char *prev = render_driver_copy; 341 char *ptr = prev; 342 while ((ptr = SDL_strchr(ptr, ',')) != NULL) { 343 *ptr = '\0'; 344 const bool is_sw = (SDL_strcasecmp(prev, SDL_SOFTWARE_RENDERER) == 0); 345 *ptr = ','; 346 if (is_sw) { 347 SDL_memset(prev, 'x', SDL_strlen(SDL_SOFTWARE_RENDERER)); 348 ptr = prev; 349 } else { 350 ptr++; 351 prev = ptr; 352 } 353 } 354 355 if (SDL_strcasecmp(prev, SDL_SOFTWARE_RENDERER) == 0) { 356 SDL_memset(prev, 'x', SDL_strlen(SDL_SOFTWARE_RENDERER)); 357 } 358 } 359 } 360 361 // Check to see if there's a specific driver requested 362 if (render_driver) { 363 renderer = SDL_CreateRenderer(window, render_driver); 364 SDL_free(render_driver_copy); 365 if (!renderer) { 366 // The error for this specific renderer has already been set 367 return false; 368 } 369 } else { 370 SDL_assert(render_driver_copy == NULL); 371 const int total = SDL_GetNumRenderDrivers(); 372 for (i = 0; i < total; ++i) { 373 const char *name = SDL_GetRenderDriver(i); 374 if (name && SDL_strcmp(name, SDL_SOFTWARE_RENDERER) != 0) { 375 renderer = SDL_CreateRenderer(window, name); 376 if (renderer) { 377 break; // this will work. 378 } 379 } 380 } 381 if (!renderer) { 382 return SDL_SetError("No hardware accelerated renderers available"); 383 } 384 } 385 386 SDL_assert(renderer != NULL); // should have explicitly checked this above. 387 388 // Create the data after we successfully create the renderer (bug #1116) 389 data = (SDL_WindowTextureData *)SDL_calloc(1, sizeof(*data)); 390 if (!data) { 391 SDL_DestroyRenderer(renderer); 392 return false; 393 } 394 if (!SDL_SetPointerProperty(props, SDL_PROP_WINDOW_TEXTUREDATA_POINTER, data)) { 395 SDL_DestroyRenderer(renderer); 396 return false; 397 } 398 399 data->renderer = renderer; 400 } 401 402 texture_formats = (const SDL_PixelFormat *)SDL_GetPointerProperty(SDL_GetRendererProperties(data->renderer), SDL_PROP_RENDERER_TEXTURE_FORMATS_POINTER, NULL); 403 if (!texture_formats) { 404 return false; 405 } 406 407 // Free any old texture and pixel data 408 if (data->texture) { 409 SDL_DestroyTexture(data->texture); 410 data->texture = NULL; 411 } 412 SDL_free(data->pixels); 413 data->pixels = NULL; 414 415 // Find the first format with or without an alpha channel 416 *format = texture_formats[0]; 417 418 for (i = 0; texture_formats[i] != SDL_PIXELFORMAT_UNKNOWN; ++i) { 419 SDL_PixelFormat texture_format = texture_formats[i]; 420 if (!SDL_ISPIXELFORMAT_FOURCC(texture_format) && 421 !SDL_ISPIXELFORMAT_10BIT(texture_format) && 422 !SDL_ISPIXELFORMAT_FLOAT(texture_format) && 423 !SDL_ISPIXELFORMAT_INDEXED(texture_format) && 424 transparent == SDL_ISPIXELFORMAT_ALPHA(texture_format)) { 425 *format = texture_format; 426 break; 427 } 428 } 429 430 data->texture = SDL_CreateTexture(data->renderer, *format, 431 SDL_TEXTUREACCESS_STREAMING, 432 w, h); 433 if (!data->texture) { 434 // codechecker_false_positive [Malloc] Static analyzer doesn't realize allocated `data` is saved to SDL_PROP_WINDOW_TEXTUREDATA_POINTER and not leaked here. 435 return false; // NOLINT(clang-analyzer-unix.Malloc) 436 } 437 SDL_SetTextureBlendMode(data->texture, SDL_BLENDMODE_NONE); 438 439 // Create framebuffer data 440 data->bytes_per_pixel = SDL_BYTESPERPIXEL(*format); 441 data->pitch = (((w * data->bytes_per_pixel) + 3) & ~3); 442 443 { 444 // Make static analysis happy about potential SDL_malloc(0) calls. 445 const size_t allocsize = (size_t)h * data->pitch; 446 data->pixels = SDL_malloc((allocsize > 0) ? allocsize : 1); 447 if (!data->pixels) { 448 return false; 449 } 450 } 451 452 *pixels = data->pixels; 453 *pitch = data->pitch; 454 455 // Make sure we're not double-scaling the viewport 456 SDL_SetRenderViewport(data->renderer, NULL); 457 458 return true; 459} 460 461bool SDL_SetWindowTextureVSync(SDL_VideoDevice *_this, SDL_Window *window, int vsync) 462{ 463 SDL_WindowTextureData *data; 464 465 data = (SDL_WindowTextureData *)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_TEXTUREDATA_POINTER, NULL); 466 if (!data) { 467 return false; 468 } 469 if (!data->renderer) { 470 return false; 471 } 472 return SDL_SetRenderVSync(data->renderer, vsync); 473} 474 475static bool SDL_GetWindowTextureVSync(SDL_VideoDevice *_this, SDL_Window *window, int *vsync) 476{ 477 SDL_WindowTextureData *data; 478 479 data = (SDL_WindowTextureData *)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_TEXTUREDATA_POINTER, NULL); 480 if (!data) { 481 return false; 482 } 483 if (!data->renderer) { 484 return false; 485 } 486 return SDL_GetRenderVSync(data->renderer, vsync); 487} 488 489static bool SDL_UpdateWindowTexture(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rects, int numrects) 490{ 491 SDL_WindowTextureData *data; 492 SDL_Rect rect; 493 void *src; 494 int w, h; 495 496 SDL_GetWindowSizeInPixels(window, &w, &h); 497 498 data = (SDL_WindowTextureData *)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_TEXTUREDATA_POINTER, NULL); 499 if (!data || !data->texture) { 500 return SDL_SetError("No window texture data"); 501 } 502 503 // Update a single rect that contains subrects for best DMA performance 504 if (SDL_GetSpanEnclosingRect(w, h, numrects, rects, &rect)) { 505 src = (void *)((Uint8 *)data->pixels + 506 rect.y * data->pitch + 507 rect.x * data->bytes_per_pixel); 508 if (!SDL_UpdateTexture(data->texture, &rect, src, data->pitch)) { 509 return false; 510 } 511 512 if (!SDL_RenderTexture(data->renderer, data->texture, NULL, NULL)) { 513 return false; 514 } 515 516 SDL_RenderPresent(data->renderer); 517 } 518 return true; 519} 520 521static void SDL_DestroyWindowTexture(SDL_VideoDevice *_this, SDL_Window *window) 522{ 523 SDL_PropertiesID props = SDL_GetWindowProperties(window); 524 if (SDL_HasProperty(props, SDL_PROP_WINDOW_TEXTUREDATA_POINTER)) { 525 SDL_WindowTextureData *data = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_TEXTUREDATA_POINTER, NULL); 526 527 if (data->texture) { 528 SDL_DestroyTexture(data->texture); 529 } 530 if (data->renderer) { 531 SDL_DestroyRenderer(data->renderer); 532 } 533 SDL_free(data->pixels); 534 SDL_free(data); 535 536 SDL_ClearProperty(props, SDL_PROP_WINDOW_TEXTUREDATA_POINTER); 537 } 538} 539 540static SDL_VideoDevice *_this = NULL; 541static SDL_AtomicInt SDL_messagebox_count; 542 543static int SDLCALL cmpmodes(const void *A, const void *B) 544{ 545 const SDL_DisplayMode *a = (const SDL_DisplayMode *)A; 546 const SDL_DisplayMode *b = (const SDL_DisplayMode *)B; 547 int a_refresh_rate = (int)(a->refresh_rate * 100); 548 int b_refresh_rate = (int)(b->refresh_rate * 100); 549 int a_pixel_density = (int)(a->pixel_density * 100); 550 int b_pixel_density = (int)(b->pixel_density * 100); 551 552 if (a->w != b->w) { 553 return b->w - a->w; 554 } else if (a->h != b->h) { 555 return b->h - a->h; 556 } else if (SDL_BITSPERPIXEL(a->format) != SDL_BITSPERPIXEL(b->format)) { 557 return SDL_BITSPERPIXEL(b->format) - SDL_BITSPERPIXEL(a->format); 558 } else if (SDL_PIXELLAYOUT(a->format) != SDL_PIXELLAYOUT(b->format)) { 559 return SDL_PIXELLAYOUT(b->format) - SDL_PIXELLAYOUT(a->format); 560 } else if (a_refresh_rate != b_refresh_rate) { 561 return b_refresh_rate - a_refresh_rate; 562 } else if (a_pixel_density != b_pixel_density) { 563 return a_pixel_density - b_pixel_density; 564 } 565 return 0; 566} 567 568bool SDL_UninitializedVideo(void) 569{ 570 return SDL_SetError("Video subsystem has not been initialized"); 571} 572 573// Deduplicated list of video bootstrap drivers. 574static const VideoBootStrap *deduped_bootstrap[SDL_arraysize(bootstrap) - 1]; 575 576int SDL_GetNumVideoDrivers(void) 577{ 578 static int num_drivers = -1; 579 580 if (num_drivers >= 0) { 581 return num_drivers; 582 } 583 584 num_drivers = 0; 585 586 // Build a list of unique video drivers. 587 for (int i = 0; bootstrap[i] != NULL; ++i) { 588 bool duplicate = false; 589 for (int j = 0; j < i; ++j) { 590 if (SDL_strcmp(bootstrap[i]->name, bootstrap[j]->name) == 0) { 591 duplicate = true; 592 break; 593 } 594 } 595 596 if (!duplicate) { 597 deduped_bootstrap[num_drivers++] = bootstrap[i]; 598 } 599 } 600 601 return num_drivers; 602} 603 604const char *SDL_GetVideoDriver(int index) 605{ 606 CHECK_PARAM(index < 0 || index >= SDL_GetNumVideoDrivers()) { 607 SDL_InvalidParamError("index"); 608 return NULL; 609 } 610 return deduped_bootstrap[index]->name; 611} 612 613/* 614 * Initialize the video and event subsystems -- determine native pixel format 615 */ 616bool SDL_VideoInit(const char *driver_name) 617{ 618 SDL_VideoDevice *video; 619 bool init_events = false; 620 bool init_keyboard = false; 621 bool init_mouse = false; 622 bool init_touch = false; 623 bool init_pen = false; 624 int i = 0; 625 626 // Check to make sure we don't overwrite '_this' 627 if (_this) { 628 SDL_VideoQuit(); 629 } 630 631 SDL_InitTicks(); 632 633 // Start the event loop 634 if (!SDL_InitSubSystem(SDL_INIT_EVENTS)) { 635 goto pre_driver_error; 636 } 637 init_events = true; 638 if (!SDL_InitKeyboard()) { 639 goto pre_driver_error; 640 } 641 init_keyboard = true; 642 if (!SDL_PreInitMouse()) { 643 goto pre_driver_error; 644 } 645 init_mouse = true; 646 if (!SDL_InitTouch()) { 647 goto pre_driver_error; 648 } 649 init_touch = true; 650 if (!SDL_InitPen()) { 651 goto pre_driver_error; 652 } 653 init_pen = true; 654 655 // Select the proper video driver 656 video = NULL; 657 if (!driver_name) { 658 driver_name = SDL_GetHint(SDL_HINT_VIDEO_DRIVER); 659 } 660 if (driver_name && *driver_name != 0) { 661 const char *driver_attempt = driver_name; 662 while (driver_attempt && *driver_attempt != 0 && !video) { 663 const char *driver_attempt_end = SDL_strchr(driver_attempt, ','); 664 size_t driver_attempt_len = (driver_attempt_end) ? (driver_attempt_end - driver_attempt) 665 : SDL_strlen(driver_attempt); 666 667 for (i = 0; bootstrap[i]; ++i) { 668 if (!bootstrap[i]->is_preferred && 669 (driver_attempt_len == SDL_strlen(bootstrap[i]->name)) && 670 (SDL_strncasecmp(bootstrap[i]->name, driver_attempt, driver_attempt_len) == 0)) { 671 video = bootstrap[i]->create(); 672 if (video) { 673 break; 674 } 675 } 676 } 677 678 driver_attempt = (driver_attempt_end) ? (driver_attempt_end + 1) : NULL; 679 } 680 } else { 681 for (i = 0; bootstrap[i]; ++i) { 682 video = bootstrap[i]->create(); 683 if (video) { 684 break; 685 } 686 } 687 } 688 if (video) { 689 SDL_DebugLogBackend("video", bootstrap[i]->name); 690 } else { 691 if (driver_name) { 692 SDL_SetError("%s not available", driver_name); 693 goto pre_driver_error; 694 } 695 SDL_SetError("No available video device"); 696 goto pre_driver_error; 697 } 698 699 /* From this point on, use SDL_VideoQuit to cleanup on error, rather than 700 pre_driver_error. */ 701 _this = video; 702 _this->name = bootstrap[i]->name; 703 _this->thread = SDL_GetCurrentThreadID(); 704 705 // Set some very sane GL defaults 706 _this->gl_config.driver_loaded = 0; 707 _this->gl_config.dll_handle = NULL; 708 SDL_GL_ResetAttributes(); 709 710 // Initialize the video subsystem 711 if (!_this->VideoInit(_this)) { 712 SDL_VideoQuit(); 713 return false; 714 } 715 716 // Make sure some displays were added 717 if (_this->num_displays == 0) { 718 SDL_VideoQuit(); 719 return SDL_SetError("The video driver did not add any displays"); 720 } 721 722 SDL_AddHintCallback(SDL_HINT_VIDEO_SYNC_WINDOW_OPERATIONS, SDL_SyncHintWatcher, NULL); 723 724 /* Disable the screen saver by default. This is a change from <= 2.0.1, 725 but most things using SDL are games or media players; you wouldn't 726 want a screensaver to trigger if you're playing exclusively with a 727 joystick, or passively watching a movie. Things that use SDL but 728 function more like a normal desktop app should explicitly re-enable the 729 screensaver. */ 730 if (!SDL_GetHintBoolean(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, false)) { 731 SDL_DisableScreenSaver(); 732 } 733 734 SDL_PostInitMouse(); 735 736 // We're ready to go! 737 return true; 738 739pre_driver_error: 740 SDL_assert(_this == NULL); 741 if (init_pen) { 742 SDL_QuitPen(); 743 } 744 if (init_touch) { 745 SDL_QuitTouch(); 746 } 747 if (init_mouse) { 748 SDL_QuitMouse(); 749 } 750 if (init_keyboard) { 751 SDL_QuitKeyboard(); 752 } 753 if (init_events) { 754 SDL_QuitSubSystem(SDL_INIT_EVENTS); 755 } 756 return false; 757} 758 759const char *SDL_GetCurrentVideoDriver(void) 760{ 761 if (!_this) { 762 SDL_UninitializedVideo(); 763 return NULL; 764 } 765 return _this->name; 766} 767 768SDL_VideoDevice *SDL_GetVideoDevice(void) 769{ 770 return _this; 771} 772 773bool SDL_OnVideoThread(void) 774{ 775 return (_this && SDL_GetCurrentThreadID() == _this->thread); 776} 777 778void SDL_SetSystemTheme(SDL_SystemTheme theme) 779{ 780 if (_this && theme != _this->system_theme) { 781 _this->system_theme = theme; 782 SDL_SendSystemThemeChangedEvent(); 783 } 784} 785 786SDL_SystemTheme SDL_GetSystemTheme(void) 787{ 788 if (_this) { 789 return _this->system_theme; 790 } else { 791 return SDL_SYSTEM_THEME_UNKNOWN; 792 } 793} 794 795void SDL_UpdateDesktopBounds(void) 796{ 797 SDL_Rect rect; 798 SDL_zero(rect); 799 800 SDL_DisplayID *displays = SDL_GetDisplays(NULL); 801 if (displays) { 802 for (int i = 0; displays[i]; ++i) { 803 SDL_Rect bounds; 804 if (SDL_GetDisplayBounds(displays[i], &bounds)) { 805 if (i == 0) { 806 SDL_copyp(&rect, &bounds); 807 } else { 808 SDL_GetRectUnion(&rect, &bounds, &rect); 809 } 810 } 811 } 812 SDL_free(displays); 813 } 814 SDL_copyp(&_this->desktop_bounds, &rect); 815} 816 817static void SDL_FinalizeDisplayMode(SDL_DisplayMode *mode) 818{ 819 // Make sure all the fields are set up correctly 820 if (mode->pixel_density <= 0.0f) { 821 mode->pixel_density = 1.0f; 822 } 823 824 if (mode->refresh_rate_numerator > 0) { 825 if (mode->refresh_rate_denominator <= 0) { 826 mode->refresh_rate_denominator = 1; 827 } 828 mode->refresh_rate = (float)mode->refresh_rate_numerator / mode->refresh_rate_denominator; 829 } else { 830 SDL_CalculateFraction(mode->refresh_rate, &mode->refresh_rate_numerator, &mode->refresh_rate_denominator); 831 } 832 mode->refresh_rate = SDL_roundf(mode->refresh_rate * 100) / 100.0f; 833} 834 835SDL_DisplayID SDL_AddBasicVideoDisplay(const SDL_DisplayMode *desktop_mode) 836{ 837 SDL_VideoDisplay display; 838 839 SDL_zero(display); 840 if (desktop_mode) { 841 SDL_memcpy(&display.desktop_mode, desktop_mode, sizeof(display.desktop_mode)); 842 } 843 return SDL_AddVideoDisplay(&display, false); 844} 845 846SDL_DisplayID SDL_AddVideoDisplay(const SDL_VideoDisplay *display, bool send_event) 847{ 848 SDL_VideoDisplay **displays, *new_display; 849 SDL_DisplayID id; 850 SDL_PropertiesID props; 851 int i; 852 853 new_display = (SDL_VideoDisplay *)SDL_malloc(sizeof(*new_display)); 854 if (!new_display) { 855 return 0; 856 } 857 858 displays = (SDL_VideoDisplay **)SDL_realloc(_this->displays, (_this->num_displays + 1) * sizeof(*displays)); 859 if (!displays) { 860 SDL_free(new_display); 861 return 0; 862 } 863 _this->displays = displays; 864 _this->displays[_this->num_displays++] = new_display; 865 866 id = SDL_GetNextObjectID(); 867 SDL_copyp(new_display, display); 868 new_display->id = id; 869 new_display->device = _this; 870 if (display->name) { 871 new_display->name = SDL_strdup(display->name); 872 } else { 873 char name[32]; 874 875 SDL_itoa(id, name, 10); 876 new_display->name = SDL_strdup(name); 877 } 878 if (new_display->content_scale == 0.0f) { 879 new_display->content_scale = 1.0f; 880 } 881 882 new_display->desktop_mode.displayID = id; 883 new_display->current_mode = &new_display->desktop_mode; 884 SDL_FinalizeDisplayMode(&new_display->desktop_mode); 885 886 for (i = 0; i < new_display->num_fullscreen_modes; ++i) { 887 new_display->fullscreen_modes[i].displayID = id; 888 } 889 890 new_display->HDR.HDR_headroom = SDL_max(display->HDR.HDR_headroom, 1.0f); 891 new_display->HDR.SDR_white_level = SDL_max(display->HDR.SDR_white_level, 1.0f); 892 893 props = SDL_GetDisplayProperties(id); 894 SDL_SetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, new_display->HDR.HDR_headroom > 1.0f); 895 896 SDL_UpdateDesktopBounds(); 897 898 if (send_event) { 899 SDL_SendDisplayEvent(new_display, SDL_EVENT_DISPLAY_ADDED, 0, 0); 900 } 901 902 return id; 903} 904 905void SDL_OnDisplayAdded(SDL_VideoDisplay *display) 906{ 907 SDL_Window *window; 908 909 // See if any windows have changed to the new display 910 for (window = _this->windows; window; window = window->next) { 911 SDL_CheckWindowDisplayChanged(window); 912 } 913} 914 915void SDL_OnDisplayMoved(SDL_VideoDisplay *display) 916{ 917 SDL_UpdateDesktopBounds(); 918} 919 920void SDL_DelVideoDisplay(SDL_DisplayID displayID, bool send_event) 921{ 922 SDL_VideoDisplay *display; 923 int display_index = SDL_GetDisplayIndex(displayID); 924 if (display_index < 0) { 925 return; 926 } 927 928 display = _this->displays[display_index]; 929 930 if (send_event) { 931 SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_REMOVED, 0, 0); 932 } 933 934 SDL_DestroyProperties(display->props); 935 SDL_free(display->name); 936 SDL_ResetFullscreenDisplayModes(display); 937 SDL_free(display->desktop_mode.internal); 938 display->desktop_mode.internal = NULL; 939 SDL_free(display->internal); 940 display->internal = NULL; 941 SDL_free(display); 942 943 if (display_index < (_this->num_displays - 1)) { 944 SDL_memmove(&_this->displays[display_index], &_this->displays[display_index + 1], (_this->num_displays - display_index - 1) * sizeof(_this->displays[display_index])); 945 } 946 --_this->num_displays; 947 948 SDL_UpdateDesktopBounds(); 949} 950 951SDL_DisplayID *SDL_GetDisplays(int *count) 952{ 953 int i; 954 SDL_DisplayID *displays; 955 956 if (!_this) { 957 if (count) { 958 *count = 0; 959 } 960 961 SDL_UninitializedVideo(); 962 return NULL; 963 } 964 965 displays = (SDL_DisplayID *)SDL_malloc((_this->num_displays + 1) * sizeof(*displays)); 966 if (displays) { 967 if (count) { 968 *count = _this->num_displays; 969 } 970 971 for (i = 0; i < _this->num_displays; ++i) { 972 displays[i] = _this->displays[i]->id; 973 } 974 displays[i] = 0; 975 } else { 976 if (count) { 977 *count = 0; 978 } 979 } 980 return displays; 981} 982 983SDL_VideoDisplay *SDL_GetVideoDisplay(SDL_DisplayID displayID) 984{ 985 int display_index; 986 987 display_index = SDL_GetDisplayIndex(displayID); 988 if (display_index < 0) { 989 return NULL; 990 } 991 return _this->displays[display_index]; 992} 993 994SDL_VideoDisplay *SDL_GetVideoDisplayForWindow(SDL_Window *window) 995{ 996 return SDL_GetVideoDisplay(SDL_GetDisplayForWindow(window)); 997} 998 999SDL_DisplayID SDL_GetPrimaryDisplay(void) 1000{ 1001 if (!_this || _this->num_displays == 0) { 1002 SDL_UninitializedVideo(); 1003 return 0; 1004 } 1005 return _this->displays[0]->id; 1006} 1007 1008int SDL_GetDisplayIndex(SDL_DisplayID displayID) 1009{ 1010 int display_index; 1011 1012 if (!_this) { 1013 SDL_UninitializedVideo(); 1014 return -1; 1015 } 1016 1017 for (display_index = 0; display_index < _this->num_displays; ++display_index) { 1018 if (displayID == _this->displays[display_index]->id) { 1019 return display_index; 1020 } 1021 } 1022 SDL_SetError("Invalid display"); 1023 return -1; 1024} 1025 1026SDL_DisplayData *SDL_GetDisplayDriverData(SDL_DisplayID displayID) 1027{ 1028 SDL_VideoDisplay *display = SDL_GetVideoDisplay(displayID); 1029 1030 CHECK_DISPLAY_MAGIC(display, NULL); 1031 1032 return display->internal; 1033} 1034 1035SDL_DisplayData *SDL_GetDisplayDriverDataForWindow(SDL_Window *window) 1036{ 1037 return SDL_GetDisplayDriverData(SDL_GetDisplayForWindow(window)); 1038} 1039 1040SDL_PropertiesID SDL_GetDisplayProperties(SDL_DisplayID displayID) 1041{ 1042 SDL_VideoDisplay *display = SDL_GetVideoDisplay(displayID); 1043 1044 CHECK_DISPLAY_MAGIC(display, 0); 1045 1046 if (display->props == 0) { 1047 display->props = SDL_CreateProperties(); 1048 } 1049 return display->props; 1050} 1051 1052const char *SDL_GetDisplayName(SDL_DisplayID displayID) 1053{ 1054 SDL_VideoDisplay *display = SDL_GetVideoDisplay(displayID); 1055 1056 CHECK_DISPLAY_MAGIC(display, NULL); 1057 1058 return display->name; 1059} 1060 1061bool SDL_GetDisplayBounds(SDL_DisplayID displayID, SDL_Rect *rect) 1062{ 1063 SDL_VideoDisplay *display = SDL_GetVideoDisplay(displayID); 1064 1065 CHECK_DISPLAY_MAGIC(display, false); 1066 1067 CHECK_PARAM(!rect) { 1068 return SDL_InvalidParamError("rect"); 1069 } 1070 1071 if (_this->GetDisplayBounds) { 1072 SDL_zerop(rect); 1073 if (_this->GetDisplayBounds(_this, display, rect)) { 1074 return true; 1075 } 1076 } 1077 1078 // Assume that the displays are left to right 1079 if (displayID == SDL_GetPrimaryDisplay()) { 1080 rect->x = 0; 1081 rect->y = 0; 1082 } else { 1083 SDL_GetDisplayBounds(_this->displays[SDL_GetDisplayIndex(displayID) - 1]->id, rect); 1084 rect->x += rect->w; 1085 } 1086 rect->w = display->current_mode->w; 1087 rect->h = display->current_mode->h; 1088 return true; 1089} 1090 1091static int ParseDisplayUsableBoundsHint(SDL_Rect *rect) 1092{ 1093 const char *hint = SDL_GetHint(SDL_HINT_DISPLAY_USABLE_BOUNDS); 1094 return hint && (SDL_sscanf(hint, "%d,%d,%d,%d", &rect->x, &rect->y, &rect->w, &rect->h) == 4); 1095} 1096 1097bool SDL_GetDisplayUsableBounds(SDL_DisplayID displayID, SDL_Rect *rect) 1098{ 1099 SDL_VideoDisplay *display = SDL_GetVideoDisplay(displayID); 1100 1101 CHECK_DISPLAY_MAGIC(display, false); 1102 1103 CHECK_PARAM(!rect) { 1104 return SDL_InvalidParamError("rect"); 1105 } 1106 1107 if (displayID == SDL_GetPrimaryDisplay() && ParseDisplayUsableBoundsHint(rect)) { 1108 return true; 1109 } 1110 1111 if (_this->GetDisplayUsableBounds) { 1112 SDL_zerop(rect); 1113 if (_this->GetDisplayUsableBounds(_this, display, rect)) { 1114 return true; 1115 } 1116 } 1117 1118 // Oh well, just give the entire display bounds. 1119 return SDL_GetDisplayBounds(displayID, rect); 1120} 1121 1122SDL_DisplayOrientation SDL_GetNaturalDisplayOrientation(SDL_DisplayID displayID) 1123{ 1124 SDL_VideoDisplay *display = SDL_GetVideoDisplay(displayID); 1125 1126 CHECK_DISPLAY_MAGIC(display, SDL_ORIENTATION_UNKNOWN); 1127 1128 if (display->natural_orientation != SDL_ORIENTATION_UNKNOWN) { 1129 return display->natural_orientation; 1130 } else { 1131 // Default to landscape if the driver hasn't set it 1132 return SDL_ORIENTATION_LANDSCAPE; 1133 } 1134} 1135 1136SDL_DisplayOrientation SDL_GetCurrentDisplayOrientation(SDL_DisplayID displayID) 1137{ 1138 SDL_VideoDisplay *display = SDL_GetVideoDisplay(displayID); 1139 1140 CHECK_DISPLAY_MAGIC(display, SDL_ORIENTATION_UNKNOWN); 1141 1142 if (display->current_orientation != SDL_ORIENTATION_UNKNOWN) { 1143 return display->current_orientation; 1144 } else { 1145 // Default to landscape if the driver hasn't set it 1146 return SDL_ORIENTATION_LANDSCAPE; 1147 } 1148} 1149 1150void SDL_SetDisplayContentScale(SDL_VideoDisplay *display, float scale) 1151{ 1152 if (scale != display->content_scale) { 1153 SDL_Window *window; 1154 1155 display->content_scale = scale; 1156 SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED, 0, 0); 1157 1158 // Check the windows on this display 1159 for (window = _this->windows; window; window = window->next) { 1160 if (display->id == window->displayID) { 1161 SDL_CheckWindowDisplayScaleChanged(window); 1162 } 1163 } 1164 } 1165} 1166 1167float SDL_GetDisplayContentScale(SDL_DisplayID displayID) 1168{ 1169 SDL_VideoDisplay *display = SDL_GetVideoDisplay(displayID); 1170 1171 CHECK_DISPLAY_MAGIC(display, 0.0f); 1172 1173 return display->content_scale; 1174} 1175 1176void SDL_SetWindowHDRProperties(SDL_Window *window, const SDL_HDROutputProperties *HDR, bool send_event) 1177{ 1178 const SDL_HDROutputProperties HDR_clamped = { 1179 SDL_max(HDR->SDR_white_level, 1.0f), 1180 SDL_max(HDR->HDR_headroom, 1.0f) 1181 }; 1182 1183 if (window->HDR.HDR_headroom != HDR_clamped.HDR_headroom || window->HDR.SDR_white_level != HDR_clamped.SDR_white_level) { 1184 SDL_PropertiesID window_props = SDL_GetWindowProperties(window); 1185 1186 SDL_SetFloatProperty(window_props, SDL_PROP_WINDOW_HDR_HEADROOM_FLOAT, HDR_clamped.HDR_headroom); 1187 SDL_SetFloatProperty(window_props, SDL_PROP_WINDOW_SDR_WHITE_LEVEL_FLOAT, HDR_clamped.SDR_white_level); 1188 SDL_SetBooleanProperty(window_props, SDL_PROP_WINDOW_HDR_ENABLED_BOOLEAN, HDR_clamped.HDR_headroom > 1.0f); 1189 SDL_copyp(&window->HDR, &HDR_clamped); 1190 1191 if (send_event) { 1192 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_HDR_STATE_CHANGED, HDR_clamped.HDR_headroom > 1.0f, 0); 1193 } 1194 } 1195} 1196 1197void SDL_SetDisplayHDRProperties(SDL_VideoDisplay *display, const SDL_HDROutputProperties *HDR) 1198{ 1199 const SDL_HDROutputProperties HDR_clamped = { 1200 SDL_max(HDR->SDR_white_level, 1.0f), 1201 SDL_max(HDR->HDR_headroom, 1.0f) 1202 }; 1203 const bool changed = HDR_clamped.SDR_white_level != display->HDR.SDR_white_level || 1204 HDR_clamped.HDR_headroom != display->HDR.HDR_headroom; 1205 1206 SDL_copyp(&display->HDR, &HDR_clamped); 1207 1208 if (changed && !SDL_DriverSendsHDRChanges(_this)) { 1209 for (SDL_Window *w = display->device->windows; w; w = w->next) { 1210 if (SDL_GetDisplayForWindow(w) == display->id) { 1211 SDL_SetWindowHDRProperties(w, &display->HDR, true); 1212 } 1213 } 1214 } 1215} 1216 1217static void SDL_UpdateFullscreenDisplayModes(SDL_VideoDisplay *display) 1218{ 1219 if (display->num_fullscreen_modes == 0 && _this->GetDisplayModes) { 1220 _this->GetDisplayModes(_this, display); 1221 } 1222} 1223 1224// Return the matching mode as a pointer into our current mode list 1225static const SDL_DisplayMode *SDL_GetFullscreenModeMatch(const SDL_DisplayMode *mode) 1226{ 1227 SDL_VideoDisplay *display; 1228 SDL_DisplayMode fullscreen_mode; 1229 1230 if (mode->w <= 0 || mode->h <= 0) { 1231 // Use the desktop mode 1232 return NULL; 1233 } 1234 1235 SDL_memcpy(&fullscreen_mode, mode, sizeof(fullscreen_mode)); 1236 if (fullscreen_mode.displayID == 0) { 1237 fullscreen_mode.displayID = SDL_GetPrimaryDisplay(); 1238 } 1239 SDL_FinalizeDisplayMode(&fullscreen_mode); 1240 1241 mode = NULL; 1242 1243 display = SDL_GetVideoDisplay(fullscreen_mode.displayID); 1244 if (display) { 1245 SDL_UpdateFullscreenDisplayModes(display); 1246 1247 // Search for an exact match 1248 if (!mode) { 1249 for (int i = 0; i < display->num_fullscreen_modes; ++i) { 1250 if (SDL_memcmp(&fullscreen_mode, &display->fullscreen_modes[i], sizeof(fullscreen_mode)) == 0) { 1251 mode = &display->fullscreen_modes[i]; 1252 break; 1253 } 1254 } 1255 } 1256 1257 // Search for a mode with the same characteristics 1258 if (!mode) { 1259 for (int i = 0; i < display->num_fullscreen_modes; ++i) { 1260 if (cmpmodes(&fullscreen_mode, &display->fullscreen_modes[i]) == 0) { 1261 mode = &display->fullscreen_modes[i]; 1262 break; 1263 } 1264 } 1265 } 1266 } 1267 return mode; 1268} 1269 1270bool SDL_AddFullscreenDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode) 1271{ 1272 SDL_DisplayMode *modes; 1273 SDL_DisplayMode new_mode; 1274 int i, nmodes; 1275 1276 // Finalize the mode for the display 1277 SDL_memcpy(&new_mode, mode, sizeof(new_mode)); 1278 new_mode.displayID = display->id; 1279 SDL_FinalizeDisplayMode(&new_mode); 1280 1281 // Make sure we don't already have the mode in the list 1282 modes = display->fullscreen_modes; 1283 nmodes = display->num_fullscreen_modes; 1284 for (i = 0; i < nmodes; ++i) { 1285 if (cmpmodes(&new_mode, &modes[i]) == 0) { 1286 return false; 1287 } 1288 } 1289 1290 // Go ahead and add the new mode 1291 if (nmodes == display->max_fullscreen_modes) { 1292 modes = (SDL_DisplayMode *)SDL_malloc((display->max_fullscreen_modes + 32) * sizeof(*modes)); 1293 if (!modes) { 1294 return false; 1295 } 1296 1297 if (display->fullscreen_modes) { 1298 // Copy the list and update the current mode pointer, if necessary. 1299 SDL_memcpy(modes, display->fullscreen_modes, nmodes * sizeof(*modes)); 1300 for (i = 0; i < nmodes; ++i) { 1301 if (display->current_mode == &display->fullscreen_modes[i]) { 1302 display->current_mode = &modes[i]; 1303 } 1304 } 1305 1306 SDL_free(display->fullscreen_modes); 1307 } 1308 1309 display->fullscreen_modes = modes; 1310 display->max_fullscreen_modes += 32; 1311 } 1312 SDL_memcpy(&modes[display->num_fullscreen_modes++], &new_mode, sizeof(new_mode)); 1313 1314 // Re-sort video modes 1315 SDL_qsort(display->fullscreen_modes, display->num_fullscreen_modes, 1316 sizeof(SDL_DisplayMode), cmpmodes); 1317 1318 return true; 1319} 1320 1321void SDL_ResetFullscreenDisplayModes(SDL_VideoDisplay *display) 1322{ 1323 int i; 1324 1325 for (i = display->num_fullscreen_modes; i--;) { 1326 SDL_free(display->fullscreen_modes[i].internal); 1327 display->fullscreen_modes[i].internal = NULL; 1328 } 1329 SDL_free(display->fullscreen_modes); 1330 display->fullscreen_modes = NULL; 1331 display->num_fullscreen_modes = 0; 1332 display->max_fullscreen_modes = 0; 1333 display->current_mode = &display->desktop_mode; 1334} 1335 1336SDL_DisplayMode **SDL_GetFullscreenDisplayModes(SDL_DisplayID displayID, int *count) 1337{ 1338 int i; 1339 int num_modes; 1340 SDL_DisplayMode **result; 1341 SDL_VideoDisplay *display = SDL_GetVideoDisplay(displayID); 1342 1343 if (count) { 1344 *count = 0; 1345 } 1346 1347 CHECK_DISPLAY_MAGIC(display, NULL); 1348 1349 SDL_UpdateFullscreenDisplayModes(display); 1350 1351 num_modes = display->num_fullscreen_modes; 1352 result = (SDL_DisplayMode **)SDL_malloc((num_modes + 1) * sizeof(*result) + num_modes * sizeof(**result)); 1353 if (result) { 1354 SDL_DisplayMode *modes = (SDL_DisplayMode *)((Uint8 *)result + ((num_modes + 1) * sizeof(*result))); 1355 SDL_memcpy(modes, display->fullscreen_modes, num_modes * sizeof(*modes)); 1356 for (i = 0; i < num_modes; ++i) { 1357 result[i] = modes++; 1358 } 1359 result[i] = NULL; 1360 1361 if (count) { 1362 *count = num_modes; 1363 } 1364 } else { 1365 if (count) { 1366 *count = 0; 1367 } 1368 } 1369 return result; 1370} 1371 1372bool SDL_GetClosestFullscreenDisplayMode(SDL_DisplayID displayID, int w, int h, float refresh_rate, bool include_high_density_modes, SDL_DisplayMode *result) 1373{ 1374 CHECK_PARAM(!result) { 1375 return SDL_InvalidParamError("closest"); // Parameter `result` is called `closest` in the header. 1376 } 1377 1378 const SDL_DisplayMode *mode, *closest = NULL; 1379 float aspect_ratio; 1380 int i; 1381 SDL_VideoDisplay *display = SDL_GetVideoDisplay(displayID); 1382 1383 SDL_zerop(result); 1384 1385 CHECK_DISPLAY_MAGIC(display, false); 1386 1387 if (h > 0) { 1388 aspect_ratio = (float)w / h; 1389 } else { 1390 aspect_ratio = 1.0f; 1391 } 1392 1393 if (refresh_rate == 0.0f) { 1394 refresh_rate = display->desktop_mode.refresh_rate; 1395 } 1396 1397 SDL_UpdateFullscreenDisplayModes(display); 1398 1399 for (i = 0; i < display->num_fullscreen_modes; ++i) { 1400 mode = &display->fullscreen_modes[i]; 1401 1402 if (w > mode->w) { 1403 // Out of sorted modes large enough here 1404 break; 1405 } 1406 if (h > mode->h) { 1407 /* Wider, but not tall enough, due to a different aspect ratio. 1408 * This mode must be skipped, but closer modes may still follow */ 1409 continue; 1410 } 1411 if (mode->pixel_density > 1.0f && !include_high_density_modes) { 1412 continue; 1413 } 1414 if (closest) { 1415 float current_aspect_ratio = (float)mode->w / mode->h; 1416 float closest_aspect_ratio = (float)closest->w / closest->h; 1417 if (SDL_fabsf(aspect_ratio - closest_aspect_ratio) < SDL_fabsf(aspect_ratio - current_aspect_ratio)) { 1418 // The mode we already found has a better aspect ratio match 1419 continue; 1420 } 1421 1422 if (mode->w == closest->w && mode->h == closest->h && 1423 SDL_fabsf(closest->refresh_rate - refresh_rate) < SDL_fabsf(mode->refresh_rate - refresh_rate)) { 1424 /* We already found a mode and the new mode is further from our 1425 * refresh rate target */ 1426 continue; 1427 } 1428 } 1429 1430 closest = mode; 1431 } 1432 if (!closest) { 1433 return SDL_SetError("Couldn't find any matching video modes"); 1434 } 1435 1436 SDL_copyp(result, closest); 1437 1438 return true; 1439} 1440 1441static bool DisplayModeChanged(const SDL_DisplayMode *old_mode, const SDL_DisplayMode *new_mode) 1442{ 1443 return ((old_mode->displayID && old_mode->displayID != new_mode->displayID) || 1444 (old_mode->format && old_mode->format != new_mode->format) || 1445 (old_mode->w && old_mode->h && (old_mode->w != new_mode->w ||old_mode->h != new_mode->h)) || 1446 (old_mode->pixel_density != 0.0f && old_mode->pixel_density != new_mode->pixel_density) || 1447 (old_mode->refresh_rate != 0.0f && old_mode->refresh_rate != new_mode->refresh_rate)); 1448} 1449 1450void SDL_SetDesktopDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode) 1451{ 1452 SDL_DisplayMode last_mode; 1453 1454 if (display->fullscreen_active) { 1455 // This is a temporary mode change, don't save the desktop mode 1456 return; 1457 } 1458 1459 SDL_copyp(&last_mode, &display->desktop_mode); 1460 1461 SDL_free(display->desktop_mode.internal); 1462 SDL_copyp(&display->desktop_mode, mode); 1463 display->desktop_mode.displayID = display->id; 1464 SDL_FinalizeDisplayMode(&display->desktop_mode); 1465 1466 if (DisplayModeChanged(&last_mode, &display->desktop_mode)) { 1467 SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED, mode->w, mode->h); 1468 if (display->current_mode == &display->desktop_mode) { 1469 SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED, mode->w, mode->h); 1470 } 1471 } 1472} 1473 1474const SDL_DisplayMode *SDL_GetDesktopDisplayMode(SDL_DisplayID displayID) 1475{ 1476 SDL_VideoDisplay *display = SDL_GetVideoDisplay(displayID); 1477 1478 CHECK_DISPLAY_MAGIC(display, NULL); 1479 1480 return &display->desktop_mode; 1481} 1482 1483void SDL_SetCurrentDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode) 1484{ 1485 SDL_DisplayMode last_mode; 1486 1487 if (display->current_mode) { 1488 SDL_copyp(&last_mode, display->current_mode); 1489 } else { 1490 SDL_zero(last_mode); 1491 } 1492 1493 display->current_mode = mode; 1494 1495 if (DisplayModeChanged(&last_mode, mode)) { 1496 SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED, mode->w, mode->h); 1497 } 1498} 1499 1500const SDL_DisplayMode *SDL_GetCurrentDisplayMode(SDL_DisplayID displayID) 1501{ 1502 SDL_VideoDisplay *display = SDL_GetVideoDisplay(displayID); 1503 1504 CHECK_DISPLAY_MAGIC(display, NULL); 1505 1506 // Make sure our mode list is updated 1507 SDL_UpdateFullscreenDisplayModes(display); 1508 1509 return display->current_mode; 1510} 1511 1512bool SDL_SetDisplayModeForDisplay(SDL_VideoDisplay *display, SDL_DisplayMode *mode) 1513{ 1514 /* Mode switching is being emulated per-window; nothing to do and cannot fail, 1515 * except for XWayland, which still needs the actual mode setting call since 1516 * it's emulated via the XRandR interface. 1517 */ 1518 if (SDL_ModeSwitchingEmulated(_this) && SDL_strcmp(_this->name, "x11") != 0) { 1519 return true; 1520 } 1521 1522 if (!mode) { 1523 mode = &display->desktop_mode; 1524 } 1525 1526 if (mode == display->current_mode) { 1527 return true; 1528 } 1529 1530 // Actually change the display mode 1531 if (_this->SetDisplayMode) { 1532 bool result; 1533 1534 _this->setting_display_mode = true; 1535 result = _this->SetDisplayMode(_this, display, mode); 1536 _this->setting_display_mode = false; 1537 if (!result) { 1538 return false; 1539 } 1540 } 1541 1542 SDL_SetCurrentDisplayMode(display, mode); 1543 1544 return true; 1545} 1546 1547/** 1548 * If x, y are outside of rect, snaps them to the closest point inside rect 1549 * (between rect->x, rect->y, inclusive, and rect->x + w, rect->y + h, exclusive) 1550 */ 1551static void SDL_GetClosestPointOnRect(const SDL_Rect *rect, SDL_Point *point) 1552{ 1553 const int right = rect->x + rect->w - 1; 1554 const int bottom = rect->y + rect->h - 1; 1555 1556 if (point->x < rect->x) { 1557 point->x = rect->x; 1558 } else if (point->x > right) { 1559 point->x = right; 1560 } 1561 1562 if (point->y < rect->y) { 1563 point->y = rect->y; 1564 } else if (point->y > bottom) { 1565 point->y = bottom; 1566 } 1567} 1568 1569static SDL_DisplayID GetDisplayForRect(int x, int y, int w, int h) 1570{ 1571 int i, dist; 1572 SDL_DisplayID closest = 0; 1573 int closest_dist = 0x7FFFFFFF; 1574 SDL_Point closest_point_on_display; 1575 SDL_Point delta; 1576 SDL_Point center; 1577 center.x = x + w / 2; 1578 center.y = y + h / 2; 1579 1580 if (_this) { 1581 for (i = 0; i < _this->num_displays; ++i) { 1582 SDL_VideoDisplay *display = _this->displays[i]; 1583 SDL_Rect display_rect; 1584 SDL_GetDisplayBounds(display->id, &display_rect); 1585 1586 // Check if the window is fully enclosed 1587 if (SDL_GetRectEnclosingPoints(¢er, 1, &display_rect, NULL)) { 1588 return display->id; 1589 } 1590 1591 // Snap window center to the display rect 1592 closest_point_on_display = center; 1593 SDL_GetClosestPointOnRect(&display_rect, &closest_point_on_display); 1594 1595 delta.x = center.x - closest_point_on_display.x; 1596 delta.y = center.y - closest_point_on_display.y; 1597 dist = (delta.x * delta.x + delta.y * delta.y); 1598 if (dist < closest_dist) { 1599 closest = display->id; 1600 closest_dist = dist; 1601 } 1602 } 1603 } 1604 1605 if (closest == 0) { 1606 SDL_SetError("Couldn't find any displays"); 1607 } 1608 1609 return closest; 1610} 1611 1612void SDL_RelativeToGlobalForWindow(SDL_Window *window, int rel_x, int rel_y, int *abs_x, int *abs_y) 1613{ 1614 SDL_Window *w; 1615 1616 if (SDL_WINDOW_IS_POPUP(window)) { 1617 // Calculate the total offset of the popup from the parents 1618 for (w = window->parent; w; w = w->parent) { 1619 rel_x += w->x; 1620 rel_y += w->y; 1621 1622 if (!SDL_WINDOW_IS_POPUP(w)) { 1623 break; 1624 } 1625 } 1626 } 1627 1628 if (abs_x) { 1629 *abs_x = rel_x; 1630 } 1631 if (abs_y) { 1632 *abs_y = rel_y; 1633 } 1634} 1635 1636void SDL_GlobalToRelativeForWindow(SDL_Window *window, int abs_x, int abs_y, int *rel_x, int *rel_y) 1637{ 1638 SDL_Window *w; 1639 1640 if (SDL_WINDOW_IS_POPUP(window)) { 1641 // Convert absolute window coordinates to relative for a popup 1642 for (w = window->parent; w; w = w->parent) { 1643 abs_x -= w->x; 1644 abs_y -= w->y; 1645 1646 if (!SDL_WINDOW_IS_POPUP(w)) { 1647 break; 1648 } 1649 } 1650 } 1651 1652 if (rel_x) { 1653 *rel_x = abs_x; 1654 } 1655 if (rel_y) { 1656 *rel_y = abs_y; 1657 } 1658} 1659 1660SDL_DisplayID SDL_GetDisplayForPoint(const SDL_Point *point) 1661{ 1662 CHECK_PARAM(!point) { 1663 SDL_InvalidParamError("point"); 1664 return 0; 1665 } 1666 1667 return GetDisplayForRect(point->x, point->y, 1, 1); 1668} 1669 1670SDL_DisplayID SDL_GetDisplayForRect(const SDL_Rect *rect) 1671{ 1672 CHECK_PARAM(!rect) { 1673 SDL_InvalidParamError("rect"); 1674 return 0; 1675 } 1676 1677 return GetDisplayForRect(rect->x, rect->y, rect->w, rect->h); 1678} 1679 1680static SDL_DisplayID GetDisplayAtOrigin(int x, int y) 1681{ 1682 for (int i = 0; i < _this->num_displays; ++i) { 1683 SDL_Rect rect; 1684 const SDL_DisplayID cur_id = _this->displays[i]->id; 1685 if (SDL_GetDisplayBounds(cur_id, &rect)) { 1686 if (x == rect.x && y == rect.y) { 1687 return cur_id; 1688 } 1689 } 1690 } 1691 1692 return 0; 1693} 1694 1695SDL_DisplayID SDL_GetDisplayForWindowPosition(SDL_Window *window) 1696{ 1697 int x, y; 1698 SDL_DisplayID displayID = 0; 1699 1700 CHECK_WINDOW_MAGIC(window, 0); 1701 1702 if (_this->GetDisplayForWindow) { 1703 displayID = _this->GetDisplayForWindow(_this, window); 1704 } 1705 1706 /* A backend implementation may fail to get a display for the window 1707 * (for example if the window is off-screen), but other code may expect it 1708 * to succeed in that situation, so we fall back to a generic position- 1709 * based implementation in that case. */ 1710 SDL_RelativeToGlobalForWindow(window, window->x, window->y, &x, &y); 1711 1712 if (!displayID) { 1713 /* Fullscreen windows may be larger than the display if they were moved between differently sized 1714 * displays and the new position was received before the new size or vice versa. Using the center 1715 * of the window rect in this case can report the wrong display, so use the origin. 1716 */ 1717 if (window->flags & SDL_WINDOW_FULLSCREEN) { 1718 displayID = GetDisplayForRect(x, y, 1, 1); 1719 } else { 1720 displayID = GetDisplayForRect(x, y, window->w, window->h); 1721 } 1722 } 1723 if (!displayID) { 1724 // Use the primary display for a window if we can't find it anywhere else 1725 displayID = SDL_GetPrimaryDisplay(); 1726 } 1727 return displayID; 1728} 1729 1730SDL_VideoDisplay *SDL_GetVideoDisplayForFullscreenWindow(SDL_Window *window) 1731{ 1732 SDL_DisplayID displayID = 0; 1733 1734 CHECK_WINDOW_MAGIC(window, NULL); 1735 1736 // An explicit fullscreen display overrides all 1737 if (window->current_fullscreen_mode.displayID) { 1738 displayID = window->current_fullscreen_mode.displayID; 1739 } 1740 1741 /* This is used to handle the very common pattern of SDL_SetWindowPosition() 1742 * followed immediately by SDL_SetWindowFullscreen() to make the window fullscreen 1743 * desktop on a specific display. If the backend doesn't support changing the 1744 * window position, or an async window manager hasn't yet actually moved the window, 1745 * the current position won't be updated at the time of the fullscreen call. 1746 */ 1747 if (!displayID) { 1748 displayID = window->pending_displayID; 1749 } 1750 if (!displayID) { 1751 // Use the pending position and dimensions, if available, otherwise, use the current. 1752 const int x = window->last_position_pending ? window->pending.x : window->x; 1753 const int y = window->last_position_pending ? window->pending.y : window->y; 1754 const int w = window->last_size_pending ? window->pending.w : window->w; 1755 const int h = window->last_size_pending ? window->pending.h : window->h; 1756 1757 // Check if the window is exactly at the origin of a display. Otherwise, fall back to the generic check. 1758 displayID = GetDisplayAtOrigin(x, y); 1759 if (!displayID) { 1760 displayID = GetDisplayForRect(x, y, w, h); 1761 } 1762 } 1763 if (!displayID) { 1764 // Use the primary display for a window if we can't find it anywhere else 1765 displayID = SDL_GetPrimaryDisplay(); 1766 } 1767 return SDL_GetVideoDisplay(displayID); 1768} 1769 1770#define SDL_PROP_SDL2_COMPAT_WINDOW_PREFERRED_FULLSCREEN_DISPLAY "sdl2-compat.window.preferred_fullscreen_display" 1771 1772SDL_DisplayID SDL_GetDisplayForWindow(SDL_Window *window) 1773{ 1774 SDL_DisplayID displayID = 0; 1775 1776 CHECK_WINDOW_MAGIC(window, 0); 1777 1778 /* sdl2-compat calls this function to get a display on which to make the window fullscreen, 1779 * so pass it the preferred fullscreen display ID in a property. 1780 */ 1781 SDL_PropertiesID window_props = SDL_GetWindowProperties(window); 1782 SDL_VideoDisplay *fs_display = SDL_GetVideoDisplayForFullscreenWindow(window); 1783 if (fs_display) { 1784 SDL_SetNumberProperty(window_props, SDL_PROP_SDL2_COMPAT_WINDOW_PREFERRED_FULLSCREEN_DISPLAY, fs_display->id); 1785 } else { 1786 SDL_ClearProperty(window_props, SDL_PROP_SDL2_COMPAT_WINDOW_PREFERRED_FULLSCREEN_DISPLAY); 1787 } 1788 1789 // An explicit fullscreen display overrides all 1790 if (window->flags & SDL_WINDOW_FULLSCREEN) { 1791 displayID = window->current_fullscreen_mode.displayID; 1792 } 1793 1794 if (!displayID) { 1795 displayID = SDL_GetDisplayForWindowPosition(window); 1796 } 1797 return displayID; 1798} 1799 1800static void SDL_CheckWindowDisplayChanged(SDL_Window *window) 1801{ 1802 if (SDL_SendsDisplayChanges(_this)) { 1803 return; 1804 } 1805 1806 SDL_DisplayID displayID = SDL_GetDisplayForWindowPosition(window); 1807 1808 if (displayID != window->displayID) { 1809 // See if we are fully committed to the new display 1810 // 80% is about the right value, tested with 350% scale on the left monitor and 100% scale on the right 1811 SDL_Rect old_bounds, new_bounds; 1812 SDL_Rect window_rect; 1813 SDL_Rect old_overlap, new_overlap; 1814 1815 if (SDL_GetDisplayBounds(window->displayID, &old_bounds) && 1816 SDL_GetDisplayBounds(displayID, &new_bounds)) { 1817 window_rect.x = window->x; 1818 window_rect.y = window->y; 1819 window_rect.w = window->w; 1820 window_rect.h = window->h; 1821 1822 if (SDL_GetRectIntersection(&old_bounds, &window_rect, &old_overlap) && 1823 SDL_GetRectIntersection(&new_bounds, &window_rect, &new_overlap)) { 1824 int old_area = old_overlap.w * old_overlap.h; 1825 int new_area = new_overlap.w * new_overlap.h; 1826 float new_overlap_ratio = (new_area / ((float)old_area + new_area)); 1827 if (new_overlap_ratio < 0.80) { 1828 return; 1829 } 1830 } 1831 } 1832 } 1833 1834 if (displayID != window->displayID) { 1835 int i, display_index; 1836 1837 // Sanity check our fullscreen windows 1838 display_index = SDL_GetDisplayIndex(displayID); 1839 for (i = 0; i < _this->num_displays; ++i) { 1840 SDL_VideoDisplay *display = _this->displays[i]; 1841 1842 if (display->fullscreen_window == window) { 1843 if (display_index != i) { 1844 if (display_index < 0) { 1845 display_index = i; 1846 } else { 1847 SDL_VideoDisplay *new_display = _this->displays[display_index]; 1848 1849 // The window was moved to a different display 1850 if (new_display->fullscreen_window && 1851 new_display->fullscreen_window != window) { 1852 // Uh oh, there's already a fullscreen window here; minimize it 1853 SDL_MinimizeWindow(new_display->fullscreen_window); 1854 } 1855 new_display->fullscreen_window = window; 1856 display->fullscreen_window = NULL; 1857 } 1858 } 1859 break; 1860 } 1861 } 1862 1863 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_DISPLAY_CHANGED, (int)displayID, 0); 1864 } 1865} 1866 1867float SDL_GetWindowPixelDensity(SDL_Window *window) 1868{ 1869 int window_w, window_h, pixel_w, pixel_h; 1870 float pixel_density = 1.0f; 1871 1872 CHECK_WINDOW_MAGIC(window, 0.0f); 1873 1874 if (SDL_GetWindowSize(window, &window_w, &window_h) && 1875 SDL_GetWindowSizeInPixels(window, &pixel_w, &pixel_h)) { 1876 pixel_density = (float)pixel_w / window_w; 1877 } 1878 return pixel_density; 1879} 1880 1881float SDL_GetWindowDisplayScale(SDL_Window *window) 1882{ 1883 CHECK_WINDOW_MAGIC(window, 0.0f); 1884 1885 return window->display_scale; 1886} 1887 1888static void SDL_CheckWindowDisplayScaleChanged(SDL_Window *window) 1889{ 1890 float display_scale; 1891 1892 if (_this->GetWindowContentScale) { 1893 display_scale = _this->GetWindowContentScale(_this, window); 1894 } else { 1895 const float pixel_density = SDL_GetWindowPixelDensity(window); 1896 const float content_scale = SDL_GetDisplayContentScale(window->displayID); 1897 1898 display_scale = pixel_density * content_scale; 1899 } 1900 1901 if (display_scale != window->display_scale) { 1902 window->display_scale = display_scale; 1903 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED, 0, 0); 1904 } 1905} 1906 1907bool SDL_UpdateFullscreenMode(SDL_Window *window, SDL_FullscreenOp fullscreen, bool commit) 1908{ 1909 SDL_VideoDisplay *display = NULL; 1910 SDL_DisplayMode *mode = NULL; 1911 int i; 1912 1913 CHECK_WINDOW_MAGIC(window, false); 1914 1915 window->fullscreen_exclusive = false; 1916 window->update_fullscreen_on_display_changed = false; 1917 1918 // If we are in the process of hiding don't go back to fullscreen 1919 if (window->is_destroying || window->is_hiding) { 1920 fullscreen = SDL_FULLSCREEN_OP_LEAVE; 1921 } 1922 1923 // Get the correct display for this operation 1924 if (fullscreen) { 1925 display = SDL_GetVideoDisplayForFullscreenWindow(window); 1926 if (!display) { 1927 // This should never happen, but it did... 1928 goto done; 1929 } 1930 } else { 1931 for (i = 0; i < _this->num_displays; ++i) { 1932 display = _this->displays[i]; 1933 if (display->fullscreen_window == window) { 1934 break; 1935 } 1936 } 1937 if (!display || i == _this->num_displays) { 1938 // Already not fullscreen on any display 1939 display = NULL; 1940 } 1941 } 1942 1943 if (fullscreen) { 1944 mode = (SDL_DisplayMode *)SDL_GetWindowFullscreenMode(window); 1945 if (mode) { 1946 window->fullscreen_exclusive = true; 1947 } else { 1948 // Make sure the current mode is zeroed for fullscreen desktop. 1949 SDL_zero(window->current_fullscreen_mode); 1950 } 1951 } 1952 1953#if defined(SDL_PLATFORM_MACOS) && defined(SDL_VIDEO_DRIVER_COCOA) 1954 /* if the window is going away and no resolution change is necessary, 1955 do nothing, or else we may trigger an ugly double-transition 1956 */ 1957 if (SDL_strcmp(_this->name, "cocoa") == 0) { // don't do this for X11, etc 1958 if (window->is_destroying && !window->last_fullscreen_exclusive_display) { 1959 window->fullscreen_exclusive = false; 1960 if (display) { 1961 display->fullscreen_window = NULL; 1962 } 1963 goto done; 1964 } 1965 if (commit) { 1966 // If we're switching between a fullscreen Space and exclusive fullscreen, we need to get back to normal first. 1967 if (fullscreen && Cocoa_IsWindowInFullscreenSpace(window) && !window->last_fullscreen_exclusive_display && window->fullscreen_exclusive) { 1968 if (!Cocoa_SetWindowFullscreenSpace(window, false, true)) { 1969 goto error; 1970 } 1971 } else if (fullscreen && window->last_fullscreen_exclusive_display && !window->fullscreen_exclusive) { 1972 for (i = 0; i < _this->num_displays; ++i) { 1973 SDL_VideoDisplay *last_display = _this->displays[i]; 1974 if (last_display->fullscreen_window == window) { 1975 SDL_SetDisplayModeForDisplay(last_display, NULL); 1976 if (_this->SetWindowFullscreen) { 1977 _this->SetWindowFullscreen(_this, window, last_display, false); 1978 } 1979 last_display->fullscreen_window = NULL; 1980 } 1981 } 1982 } 1983 1984 if (Cocoa_SetWindowFullscreenSpace(window, !!fullscreen, syncHint)) { 1985 goto done; 1986 } 1987 } 1988 } 1989#endif 1990 1991 if (display) { 1992 // Restore the video mode on other displays if needed 1993 for (i = 0; i < _this->num_displays; ++i) { 1994 SDL_VideoDisplay *other = _this->displays[i]; 1995 if (other != display && other->fullscreen_window == window) { 1996 SDL_SetDisplayModeForDisplay(other, NULL); 1997 other->fullscreen_window = NULL; 1998 } 1999 } 2000 } 2001 2002 if (fullscreen) { 2003 int mode_w = 0, mode_h = 0; 2004 bool resized = false; 2005 2006 // Hide any other fullscreen window on this display 2007 if (display->fullscreen_window && 2008 display->fullscreen_window != window) { 2009 SDL_MinimizeWindow(display->fullscreen_window); 2010 } 2011 2012 display->fullscreen_active = window->fullscreen_exclusive; 2013 2014 if (!SDL_SetDisplayModeForDisplay(display, mode)) { 2015 goto error; 2016 } 2017 if (commit) { 2018 SDL_FullscreenResult ret = SDL_FULLSCREEN_SUCCEEDED; 2019 if (_this->SetWindowFullscreen) { 2020 ret = _this->SetWindowFullscreen(_this, window, display, fullscreen); 2021 } else { 2022 resized = true; 2023 } 2024 2025 if (ret == SDL_FULLSCREEN_SUCCEEDED) { 2026 // Window is fullscreen immediately upon return. If the driver hasn't already sent the event, do so now. 2027 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { 2028 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0); 2029 } 2030 } else if (ret == SDL_FULLSCREEN_FAILED) { 2031 display->fullscreen_active = false; 2032 goto error; 2033 } 2034 } 2035 2036 if (window->flags & SDL_WINDOW_FULLSCREEN) { 2037 display->fullscreen_window = window; 2038 2039 /* Android may not resize the window to exactly what our fullscreen mode is, 2040 * especially on windowed Android environments like the Chromebook or Samsung DeX. 2041 * Given this, we shouldn't use the mode size. Android's SetWindowFullscreen 2042 * will generate the window event for us with the proper final size. 2043 * 2044 * This is also unnecessary on Cocoa, Wayland, Win32, and X11 (will send SDL_EVENT_WINDOW_RESIZED). 2045 */ 2046 if (!SDL_SendsFullscreenDimensions(_this)) { 2047 SDL_Rect displayRect; 2048 2049 if (mode) { 2050 mode_w = mode->w; 2051 mode_h = mode->h; 2052 SDL_GetDisplayBounds(mode->displayID, &displayRect); 2053 } else { 2054 mode_w = display->desktop_mode.w; 2055 mode_h = display->desktop_mode.h; 2056 SDL_GetDisplayBounds(display->id, &displayRect); 2057 } 2058 2059 if (window->w != mode_w || window->h != mode_h) { 2060 resized = true; 2061 } 2062 2063 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, displayRect.x, displayRect.y); 2064 2065 if (resized) { 2066 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, mode_w, mode_h); 2067 } else { 2068 SDL_OnWindowResized(window); 2069 } 2070 } 2071 } 2072 } else { 2073 bool resized = false; 2074 2075 // Restore the desktop mode 2076 if (display) { 2077 display->fullscreen_active = false; 2078 2079 SDL_SetDisplayModeForDisplay(display, NULL); 2080 } 2081 if (commit) { 2082 SDL_FullscreenResult ret = SDL_FULLSCREEN_SUCCEEDED; 2083 if (_this->SetWindowFullscreen) { 2084 SDL_VideoDisplay *full_screen_display = display ? display : SDL_GetVideoDisplayForFullscreenWindow(window); 2085 if (full_screen_display) { 2086 ret = _this->SetWindowFullscreen(_this, window, full_screen_display, SDL_FULLSCREEN_OP_LEAVE); 2087 } 2088 } else { 2089 resized = true; 2090 } 2091 2092 if (ret == SDL_FULLSCREEN_SUCCEEDED) { 2093 // Window left fullscreen immediately upon return. If the driver hasn't already sent the event, do so now. 2094 if (window->flags & SDL_WINDOW_FULLSCREEN) { 2095 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0); 2096 } 2097 } else if (ret == SDL_FULLSCREEN_FAILED) { 2098 goto error; 2099 } 2100 } 2101 2102 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { 2103 if (display) { 2104 display->fullscreen_window = NULL; 2105 } 2106 2107 if (!SDL_SendsFullscreenDimensions(_this)) { 2108 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, window->windowed.x, window->windowed.y); 2109 if (resized) { 2110 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->windowed.w, window->windowed.h); 2111 } else { 2112 SDL_OnWindowResized(window); 2113 } 2114 } 2115 } 2116 } 2117 2118done: 2119 window->last_fullscreen_exclusive_display = display && (window->flags & SDL_WINDOW_FULLSCREEN) && window->fullscreen_exclusive ? display->id : 0; 2120 return true; 2121 2122error: 2123 if (fullscreen) { 2124 // Something went wrong and the window is no longer fullscreen. 2125 SDL_UpdateFullscreenMode(window, SDL_FULLSCREEN_OP_LEAVE, commit); 2126 } 2127 return false; 2128} 2129 2130bool SDL_SetWindowFullscreenMode(SDL_Window *window, const SDL_DisplayMode *mode) 2131{ 2132 CHECK_WINDOW_MAGIC(window, false); 2133 CHECK_WINDOW_NOT_POPUP(window, false); 2134 2135 if (mode) { 2136 if (!SDL_GetFullscreenModeMatch(mode)) { 2137 return SDL_SetError("Invalid fullscreen display mode"); 2138 } 2139 2140 // Save the mode so we can look up the closest match later 2141 SDL_copyp(&window->requested_fullscreen_mode, mode); 2142 } else { 2143 SDL_zero(window->requested_fullscreen_mode); 2144 } 2145 2146 /* Copy to the current mode now, in case an asynchronous fullscreen window request 2147 * is in progress. It will be overwritten if a new request is made. 2148 */ 2149 SDL_copyp(&window->current_fullscreen_mode, &window->requested_fullscreen_mode); 2150 2151#if defined(SDL_PLATFORM_MACOS) && defined(SDL_VIDEO_DRIVER_COCOA) 2152 /* If this is called while in the middle of a Cocoa fullscreen spaces transition, 2153 * wait until the transition has completed, or the window can wind up in a weird, 2154 * broken state if a mode switch occurs while in a fullscreen space. 2155 */ 2156 if (SDL_strcmp(_this->name, "cocoa") == 0 && Cocoa_IsWindowInFullscreenSpaceTransition(window)) { 2157 SDL_SyncWindow(window); 2158 } 2159#endif 2160 if (SDL_WINDOW_FULLSCREEN_VISIBLE(window)) { 2161 SDL_UpdateFullscreenMode(window, SDL_FULLSCREEN_OP_UPDATE, true); 2162 SDL_SyncIfRequired(window); 2163 } 2164 2165 return true; 2166} 2167 2168const SDL_DisplayMode *SDL_GetWindowFullscreenMode(SDL_Window *window) 2169{ 2170 CHECK_WINDOW_MAGIC(window, NULL); 2171 CHECK_WINDOW_NOT_POPUP(window, NULL); 2172 2173 if (window->flags & SDL_WINDOW_FULLSCREEN) { 2174 return SDL_GetFullscreenModeMatch(&window->current_fullscreen_mode); 2175 } else { 2176 return SDL_GetFullscreenModeMatch(&window->requested_fullscreen_mode); 2177 } 2178} 2179 2180void *SDL_GetWindowICCProfile(SDL_Window *window, size_t *size) 2181{ 2182 if (!_this->GetWindowICCProfile) { 2183 SDL_Unsupported(); 2184 return NULL; 2185 } 2186 return _this->GetWindowICCProfile(_this, window, size); 2187} 2188 2189SDL_PixelFormat SDL_GetWindowPixelFormat(SDL_Window *window) 2190{ 2191 SDL_DisplayID displayID; 2192 const SDL_DisplayMode *mode; 2193 2194 CHECK_WINDOW_MAGIC(window, SDL_PIXELFORMAT_UNKNOWN); 2195 2196 displayID = SDL_GetDisplayForWindow(window); 2197 mode = SDL_GetCurrentDisplayMode(displayID); 2198 if (mode) { 2199 return mode->format; 2200 } else { 2201 return SDL_PIXELFORMAT_UNKNOWN; 2202 } 2203} 2204 2205#define CREATE_FLAGS \ 2206 (SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_POPUP_MENU | SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_VULKAN | SDL_WINDOW_MINIMIZED | SDL_WINDOW_METAL | SDL_WINDOW_TRANSPARENT | SDL_WINDOW_NOT_FOCUSABLE | SDL_WINDOW_FILL_DOCUMENT) 2207 2208static SDL_INLINE bool IsAcceptingDragAndDrop(void) 2209{ 2210 if (SDL_EventEnabled(SDL_EVENT_DROP_FILE) || SDL_EventEnabled(SDL_EVENT_DROP_TEXT)) { 2211 return true; 2212 } 2213 return false; 2214} 2215 2216// prepare a newly-created window 2217static SDL_INLINE void PrepareDragAndDropSupport(SDL_Window *window) 2218{ 2219 if (_this->AcceptDragAndDrop) { 2220 _this->AcceptDragAndDrop(window, IsAcceptingDragAndDrop()); 2221 } 2222} 2223 2224// toggle d'n'd for all existing windows. 2225void SDL_ToggleDragAndDropSupport(void) 2226{ 2227 if (_this && _this->AcceptDragAndDrop) { 2228 const bool enable = IsAcceptingDragAndDrop(); 2229 SDL_Window *window; 2230 for (window = _this->windows; window; window = window->next) { 2231 _this->AcceptDragAndDrop(window, enable); 2232 } 2233 } 2234} 2235 2236SDL_Window ** SDLCALL SDL_GetWindows(int *count) 2237{ 2238 if (count) { 2239 *count = 0; 2240 } 2241 2242 if (!_this) { 2243 SDL_UninitializedVideo(); 2244 return NULL; 2245 } 2246 2247 SDL_Window *window; 2248 int num_added = 0; 2249 int num_windows = 0; 2250 for (window = _this->windows; window; window = window->next) { 2251 ++num_windows; 2252 } 2253 2254 SDL_Window **windows = (SDL_Window **)SDL_malloc((num_windows + 1) * sizeof(*windows)); 2255 if (!windows) { 2256 return NULL; 2257 } 2258 2259 for (window = _this->windows; window; window = window->next) { 2260 windows[num_added++] = window; 2261 if (num_added == num_windows) { 2262 // Race condition? Multi-threading not supported, ignore it 2263 break; 2264 } 2265 } 2266 windows[num_added] = NULL; 2267 2268 if (count) { 2269 *count = num_added; 2270 } 2271 return windows; 2272} 2273 2274static void ApplyWindowFlags(SDL_Window *window, SDL_WindowFlags flags) 2275{ 2276 if (!SDL_WINDOW_IS_POPUP(window)) { 2277 if (!(flags & (SDL_WINDOW_MINIMIZED | SDL_WINDOW_MAXIMIZED))) { 2278 SDL_RestoreWindow(window); 2279 } 2280 if (flags & SDL_WINDOW_MAXIMIZED) { 2281 SDL_MaximizeWindow(window); 2282 } 2283 2284 SDL_SetWindowFullscreen(window, (flags & SDL_WINDOW_FULLSCREEN) != 0); 2285 2286 if (flags & SDL_WINDOW_MINIMIZED) { 2287 SDL_MinimizeWindow(window); 2288 } 2289 2290 if (flags & SDL_WINDOW_MODAL) { 2291 SDL_SetWindowModal(window, true); 2292 } 2293 2294 if (flags & SDL_WINDOW_MOUSE_GRABBED) { 2295 SDL_SetWindowMouseGrab(window, true); 2296 } 2297 if (flags & SDL_WINDOW_KEYBOARD_GRABBED) { 2298 SDL_SetWindowKeyboardGrab(window, true); 2299 } 2300 } 2301} 2302 2303static void SDL_FinishWindowCreation(SDL_Window *window, SDL_WindowFlags flags) 2304{ 2305 PrepareDragAndDropSupport(window); 2306 2307 if (window->flags & SDL_WINDOW_EXTERNAL) { 2308 // Whoever has created the window has already applied whatever flags are needed 2309 } else { 2310 ApplyWindowFlags(window, flags); 2311 if (!(flags & SDL_WINDOW_HIDDEN)) { 2312 SDL_ShowWindow(window); 2313 } 2314 } 2315 2316#if defined(SDL_PLATFORM_LINUX) 2317 if (!SDL_WINDOW_IS_POPUP(window)) { 2318 // On Linux the progress state is persisted throughout multiple program runs, so reset state on window creation 2319 SDL_SetWindowProgressState(window, SDL_PROGRESS_STATE_NONE); 2320 SDL_SetWindowProgressValue(window, 0.0f); 2321 } 2322#endif 2323} 2324 2325static bool SDL_ContextNotSupported(const char *name) 2326{ 2327 return SDL_SetError("%s support is either not configured in SDL " 2328 "or not available in current SDL video driver " 2329 "(%s) or platform", 2330 name, 2331 _this->name); 2332} 2333 2334static bool SDL_DllNotSupported(const char *name) 2335{ 2336 return SDL_SetError("No dynamic %s support in current SDL video driver (%s)", name, _this->name); 2337} 2338 2339static struct { 2340 const char *property_name; 2341 SDL_WindowFlags flag; 2342 bool invert_value; 2343} SDL_WindowFlagProperties[] = { 2344 { SDL_PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN, SDL_WINDOW_ALWAYS_ON_TOP, false }, 2345 { SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN, SDL_WINDOW_BORDERLESS, false }, 2346 { SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN, SDL_WINDOW_NOT_FOCUSABLE, true }, 2347 { SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, SDL_WINDOW_FULLSCREEN, false }, 2348 { SDL_PROP_WINDOW_CREATE_HIDDEN_BOOLEAN, SDL_WINDOW_HIDDEN, false }, 2349 { SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN, SDL_WINDOW_HIGH_PIXEL_DENSITY, false }, 2350 { SDL_PROP_WINDOW_CREATE_MAXIMIZED_BOOLEAN, SDL_WINDOW_MAXIMIZED, false }, 2351 { SDL_PROP_WINDOW_CREATE_MENU_BOOLEAN, SDL_WINDOW_POPUP_MENU, false }, 2352 { SDL_PROP_WINDOW_CREATE_METAL_BOOLEAN, SDL_WINDOW_METAL, false }, 2353 { SDL_PROP_WINDOW_CREATE_MINIMIZED_BOOLEAN, SDL_WINDOW_MINIMIZED, false }, 2354 { SDL_PROP_WINDOW_CREATE_MODAL_BOOLEAN, SDL_WINDOW_MODAL, false }, 2355 { SDL_PROP_WINDOW_CREATE_MOUSE_GRABBED_BOOLEAN, SDL_WINDOW_MOUSE_GRABBED, false }, 2356 { SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, SDL_WINDOW_OPENGL, false }, 2357 { SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, SDL_WINDOW_RESIZABLE, false }, 2358 { SDL_PROP_WINDOW_CREATE_TRANSPARENT_BOOLEAN, SDL_WINDOW_TRANSPARENT, false }, 2359 { SDL_PROP_WINDOW_CREATE_TOOLTIP_BOOLEAN, SDL_WINDOW_TOOLTIP, false }, 2360 { SDL_PROP_WINDOW_CREATE_UTILITY_BOOLEAN, SDL_WINDOW_UTILITY, false }, 2361 { SDL_PROP_WINDOW_CREATE_VULKAN_BOOLEAN, SDL_WINDOW_VULKAN, false } 2362}; 2363 2364static SDL_WindowFlags SDL_GetWindowFlagProperties(SDL_PropertiesID props) 2365{ 2366 unsigned i; 2367 SDL_WindowFlags flags = (SDL_WindowFlags)SDL_GetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, 0); 2368 2369 for (i = 0; i < SDL_arraysize(SDL_WindowFlagProperties); ++i) { 2370 if (SDL_WindowFlagProperties[i].invert_value) { 2371 if (!SDL_GetBooleanProperty(props, SDL_WindowFlagProperties[i].property_name, true)) { 2372 flags |= SDL_WindowFlagProperties[i].flag; 2373 } 2374 } else { 2375 if (SDL_GetBooleanProperty(props, SDL_WindowFlagProperties[i].property_name, false)) { 2376 flags |= SDL_WindowFlagProperties[i].flag; 2377 } 2378 } 2379 } 2380 return flags; 2381} 2382 2383SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props) 2384{ 2385 SDL_Window *window; 2386 const char *title = SDL_GetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, NULL); 2387 int x = (int)SDL_GetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_UNDEFINED); 2388 int y = (int)SDL_GetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_UNDEFINED); 2389 int w = (int)SDL_GetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, 0); 2390 int h = (int)SDL_GetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, 0); 2391 SDL_Window *parent = (SDL_Window *)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_CREATE_PARENT_POINTER, NULL); 2392 SDL_WindowFlags flags = SDL_GetWindowFlagProperties(props); 2393 SDL_WindowFlags type_flags, graphics_flags; 2394 SDL_DisplayID displayID = 0; 2395 bool undefined_x = false; 2396 bool undefined_y = false; 2397 bool external_graphics_context = SDL_GetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN, false); 2398 2399 if (!_this) { 2400 // Initialize the video system if needed 2401 if (!SDL_Init(SDL_INIT_VIDEO)) { 2402 return NULL; 2403 } 2404 2405 // Make clang-tidy happy 2406 if (!_this) { 2407 return NULL; 2408 } 2409 } 2410 2411 if ((flags & SDL_WINDOW_MODAL) && !SDL_ObjectValid(parent, SDL_OBJECT_TYPE_WINDOW)) { 2412 SDL_SetError("Modal windows must specify a parent window"); 2413 return NULL; 2414 } 2415 2416 if ((flags & (SDL_WINDOW_TOOLTIP | SDL_WINDOW_POPUP_MENU)) != 0) { 2417 if (!(_this->device_caps & VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT)) { 2418 SDL_Unsupported(); 2419 return NULL; 2420 } 2421 2422 // Tooltip and popup menu window must specify a parent window 2423 if (!SDL_ObjectValid(parent, SDL_OBJECT_TYPE_WINDOW)) { 2424 SDL_SetError("Tooltip and popup menu windows must specify a parent window"); 2425 return NULL; 2426 } 2427 2428 // Remove invalid flags 2429 flags &= ~(SDL_WINDOW_MINIMIZED | SDL_WINDOW_MAXIMIZED | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS); 2430 } 2431 2432 // Ensure no more than one of these flags is set 2433 type_flags = flags & (SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_POPUP_MENU | SDL_WINDOW_MODAL); 2434 if (type_flags & (type_flags - 1)) { 2435 SDL_SetError("Conflicting window type flags specified: 0x%.8x", (unsigned int)type_flags); 2436 return NULL; 2437 } 2438 2439 // Make sure the display list is up to date for window placement 2440 if (_this->RefreshDisplays) { 2441 _this->RefreshDisplays(_this); 2442 } 2443 2444 // Some platforms can't create zero-sized windows 2445 if (w < 1) { 2446 w = 1; 2447 } 2448 if (h < 1) { 2449 h = 1; 2450 } 2451 2452 if (SDL_WINDOWPOS_ISUNDEFINED(x) || SDL_WINDOWPOS_ISUNDEFINED(y) || 2453 SDL_WINDOWPOS_ISCENTERED(x) || SDL_WINDOWPOS_ISCENTERED(y)) { 2454 SDL_Rect bounds; 2455 2456 if ((SDL_WINDOWPOS_ISUNDEFINED(x) || SDL_WINDOWPOS_ISCENTERED(x)) && (x & 0xFFFF)) { 2457 displayID = (x & 0xFFFF); 2458 } else if ((SDL_WINDOWPOS_ISUNDEFINED(y) || SDL_WINDOWPOS_ISCENTERED(y)) && (y & 0xFFFF)) { 2459 displayID = (y & 0xFFFF); 2460 } 2461 if (displayID == 0 || SDL_GetDisplayIndex(displayID) < 0) { 2462 displayID = SDL_GetPrimaryDisplay(); 2463 } 2464 2465 SDL_zero(bounds); 2466 SDL_GetDisplayUsableBounds(displayID, &bounds); 2467 if (w > bounds.w || h > bounds.h) { 2468 // This window is larger than the usable bounds, just center on the display 2469 SDL_GetDisplayBounds(displayID, &bounds); 2470 } 2471 if (SDL_WINDOWPOS_ISCENTERED(x) || SDL_WINDOWPOS_ISUNDEFINED(x)) { 2472 if (SDL_WINDOWPOS_ISUNDEFINED(x)) { 2473 undefined_x = true; 2474 } 2475 x = bounds.x + (bounds.w - w) / 2; 2476 } 2477 if (SDL_WINDOWPOS_ISCENTERED(y) || SDL_WINDOWPOS_ISUNDEFINED(y)) { 2478 if (SDL_WINDOWPOS_ISUNDEFINED(y)) { 2479 undefined_y = true; 2480 } 2481 y = bounds.y + (bounds.h - h) / 2; 2482 } 2483 } 2484 2485 // ensure no more than one of these flags is set 2486 graphics_flags = flags & (SDL_WINDOW_OPENGL | SDL_WINDOW_METAL | SDL_WINDOW_VULKAN); 2487 if (graphics_flags & (graphics_flags - 1)) { 2488 SDL_SetError("Conflicting window graphics flags specified: 0x%.8x", (unsigned int)graphics_flags); 2489 return NULL; 2490 } 2491 2492 // Some platforms have certain graphics backends enabled by default 2493 if (!graphics_flags && !external_graphics_context) { 2494 flags |= SDL_DefaultGraphicsBackends(_this); 2495 } 2496 2497 if (flags & SDL_WINDOW_OPENGL) { 2498 if (!_this->GL_CreateContext) { 2499 SDL_ContextNotSupported("OpenGL"); 2500 return NULL; 2501 } 2502 if (!SDL_GL_LoadLibrary(NULL)) { 2503 return NULL; 2504 } 2505 } 2506 2507 if (flags & SDL_WINDOW_VULKAN) { 2508 if (!_this->Vulkan_CreateSurface) { 2509 SDL_ContextNotSupported("Vulkan"); 2510 return NULL; 2511 } 2512 if (!SDL_Vulkan_LoadLibrary(NULL)) { 2513 return NULL; 2514 } 2515 } 2516 2517 if (flags & SDL_WINDOW_METAL) { 2518 if (!_this->Metal_CreateView) { 2519 SDL_ContextNotSupported("Metal"); 2520 return NULL; 2521 } 2522 } 2523 2524 window = (SDL_Window *)SDL_calloc(1, sizeof(*window)); 2525 if (!window) { 2526 return NULL; 2527 } 2528 SDL_SetObjectValid(window, SDL_OBJECT_TYPE_WINDOW, true); 2529 window->id = SDL_GetNextObjectID(); 2530 window->floating.x = window->windowed.x = window->x = x; 2531 window->floating.y = window->windowed.y = window->y = y; 2532 window->floating.w = window->windowed.w = window->w = w; 2533 window->floating.h = window->windowed.h = window->h = h; 2534 window->undefined_x = undefined_x; 2535 window->undefined_y = undefined_y; 2536 window->pending_displayID = displayID; 2537 2538 SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window); 2539 if (display) { 2540 SDL_SetWindowHDRProperties(window, &display->HDR, false); 2541 } 2542 2543 if (flags & SDL_WINDOW_FULLSCREEN || IsFullscreenOnly(_this)) { 2544 SDL_Rect bounds; 2545 2546 SDL_GetDisplayBounds(display ? display->id : SDL_GetPrimaryDisplay(), &bounds); 2547 window->x = bounds.x; 2548 window->y = bounds.y; 2549 window->w = bounds.w; 2550 window->h = bounds.h; 2551 window->pending_flags |= SDL_WINDOW_FULLSCREEN; 2552 flags |= SDL_WINDOW_FULLSCREEN; 2553 } 2554 2555 window->flags = ((flags & CREATE_FLAGS) | SDL_WINDOW_HIDDEN); 2556 window->display_scale = 1.0f; 2557 window->opacity = 1.0f; 2558 window->next = _this->windows; 2559 window->is_destroying = false; 2560 window->displayID = SDL_GetDisplayForWindow(window); 2561 window->external_graphics_context = external_graphics_context; 2562 window->constrain_popup = SDL_GetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_CONSTRAIN_POPUP_BOOLEAN, true); 2563 2564 if (!_this->SetWindowFillDocument) { 2565 window->flags &= ~SDL_WINDOW_FILL_DOCUMENT; // not an error, just unsupported here, so remove the flag. 2566 } 2567 2568 if (_this->windows) { 2569 _this->windows->prev = window; 2570 } 2571 _this->windows = window; 2572 2573 // Set the parent before creation. 2574 SDL_UpdateWindowHierarchy(window, parent); 2575 2576 if (_this->CreateSDLWindow && !_this->CreateSDLWindow(_this, window, props)) { 2577 PUSH_SDL_ERROR() 2578 SDL_DestroyWindow(window); 2579 POP_SDL_ERROR() 2580 return NULL; 2581 } 2582 2583 /* Clear minimized if not on windows, only windows handles it at create rather than FinishWindowCreation, 2584 * but it's important or window focus will get broken on windows! 2585 */ 2586#if !defined(SDL_PLATFORM_WINDOWS) 2587 if (window->flags & SDL_WINDOW_MINIMIZED) { 2588 window->flags &= ~SDL_WINDOW_MINIMIZED; 2589 } 2590#endif 2591 2592 if (title) { 2593 SDL_SetWindowTitle(window, title); 2594 } 2595 SDL_FinishWindowCreation(window, flags); 2596 2597 // Make sure window pixel size is up to date 2598 SDL_CheckWindowPixelSizeChanged(window); 2599 2600#ifdef SDL_VIDEO_DRIVER_UIKIT 2601 SDL_UpdateLifecycleObserver(); 2602#endif 2603 2604 SDL_ClearError(); 2605 2606 return window; 2607} 2608 2609SDL_Window *SDL_CreateWindow(const char *title, int w, int h, SDL_WindowFlags flags) 2610{ 2611 SDL_Window *window; 2612 SDL_PropertiesID props = SDL_CreateProperties(); 2613 if (title && *title) { 2614 SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, title); 2615 } 2616 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, w); 2617 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, h); 2618 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, flags); 2619 window = SDL_CreateWindowWithProperties(props); 2620 SDL_DestroyProperties(props); 2621 return window; 2622} 2623 2624SDL_Window *SDL_CreatePopupWindow(SDL_Window *parent, int offset_x, int offset_y, int w, int h, SDL_WindowFlags flags) 2625{ 2626 SDL_Window *window; 2627 SDL_PropertiesID props = SDL_CreateProperties(); 2628 2629 // Popups must specify either the tooltip or popup menu window flags 2630 if (!(flags & (SDL_WINDOW_TOOLTIP | SDL_WINDOW_POPUP_MENU))) { 2631 SDL_SetError("Popup windows must specify either the 'SDL_WINDOW_TOOLTIP' or the 'SDL_WINDOW_POPUP_MENU' flag"); 2632 return NULL; 2633 } 2634 2635 SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_PARENT_POINTER, parent); 2636 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, offset_x); 2637 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, offset_y); 2638 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, w); 2639 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, h); 2640 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, flags); 2641 window = SDL_CreateWindowWithProperties(props); 2642 SDL_DestroyProperties(props); 2643 return window; 2644} 2645 2646static bool SDL_ReconfigureWindowInternal(SDL_Window *window, SDL_WindowFlags flags) 2647{ 2648 bool loaded_opengl = false; 2649 bool loaded_vulkan = false; 2650 2651 if (!_this->ReconfigureWindow) { 2652 return false; 2653 } 2654 2655 // Don't attempt to reconfigure external windows. 2656 if (window->flags & SDL_WINDOW_EXTERNAL) { 2657 return false; 2658 } 2659 2660 // Only attempt to reconfigure if the window has no existing graphics flags. 2661 if (window->flags & (SDL_WINDOW_OPENGL | SDL_WINDOW_METAL | SDL_WINDOW_VULKAN)) { 2662 return false; 2663 } 2664 2665 const SDL_WindowFlags graphics_flags = flags & (SDL_WINDOW_OPENGL | SDL_WINDOW_METAL | SDL_WINDOW_VULKAN); 2666 if (graphics_flags & (graphics_flags - 1)) { 2667 return SDL_SetError("Conflicting window flags specified"); 2668 } 2669 2670 if ((flags & SDL_WINDOW_OPENGL) && !_this->GL_CreateContext) { 2671 return SDL_ContextNotSupported("OpenGL"); 2672 } 2673 if ((flags & SDL_WINDOW_VULKAN) && !_this->Vulkan_CreateSurface) { 2674 return SDL_ContextNotSupported("Vulkan"); 2675 } 2676 if ((flags & SDL_WINDOW_METAL) && !_this->Metal_CreateView) { 2677 return SDL_ContextNotSupported("Metal"); 2678 } 2679 2680 SDL_DestroyWindowSurface(window); 2681 2682 if (graphics_flags & SDL_WINDOW_OPENGL) { 2683 loaded_opengl = SDL_GL_LoadLibrary(NULL); 2684 if (!loaded_opengl) { 2685 return false; 2686 } 2687 } else if (graphics_flags & SDL_WINDOW_VULKAN) { 2688 loaded_vulkan = SDL_Vulkan_LoadLibrary(NULL); 2689 if (!loaded_vulkan) { 2690 return false; 2691 } 2692 } 2693 2694 // Try to reconfigure the window for the requested graphics flags. 2695 if (!_this->ReconfigureWindow(_this, window, graphics_flags)) { 2696 if (loaded_opengl) { 2697 SDL_GL_UnloadLibrary(); 2698 } 2699 if (loaded_vulkan) { 2700 SDL_Vulkan_UnloadLibrary(); 2701 } 2702 2703 return false; 2704 } 2705 2706 window->flags |= graphics_flags; 2707 2708 return true; 2709} 2710 2711bool SDL_RecreateWindow(SDL_Window *window, SDL_WindowFlags flags) 2712{ 2713 bool loaded_opengl = false; 2714 bool need_gl_unload = false; 2715 bool need_gl_load = false; 2716 bool loaded_vulkan = false; 2717 bool need_vulkan_unload = false; 2718 bool need_vulkan_load = false; 2719 SDL_WindowFlags graphics_flags; 2720 2721 // ensure no more than one of these flags is set 2722 graphics_flags = flags & (SDL_WINDOW_OPENGL | SDL_WINDOW_METAL | SDL_WINDOW_VULKAN); 2723 if (graphics_flags & (graphics_flags - 1)) { 2724 return SDL_SetError("Conflicting window flags specified"); 2725 } 2726 2727 if ((flags & SDL_WINDOW_OPENGL) && !_this->GL_CreateContext) { 2728 return SDL_ContextNotSupported("OpenGL"); 2729 } 2730 if ((flags & SDL_WINDOW_VULKAN) && !_this->Vulkan_CreateSurface) { 2731 return SDL_ContextNotSupported("Vulkan"); 2732 } 2733 if ((flags & SDL_WINDOW_METAL) && !_this->Metal_CreateView) { 2734 return SDL_ContextNotSupported("Metal"); 2735 } 2736 2737 if (window->flags & SDL_WINDOW_EXTERNAL) { 2738 // Can't destroy and re-create external windows, hrm 2739 flags |= SDL_WINDOW_EXTERNAL; 2740 } else { 2741 flags &= ~SDL_WINDOW_EXTERNAL; 2742 } 2743 2744 // If this is a modal dialog, clear the modal status. 2745 if (window->flags & SDL_WINDOW_MODAL) { 2746 SDL_SetWindowModal(window, false); 2747 } 2748 2749 // Restore video mode, etc. 2750 if (!(window->flags & SDL_WINDOW_EXTERNAL)) { 2751 const bool restore_on_show = window->restore_on_show; 2752 SDL_HideWindow(window); 2753 window->restore_on_show = restore_on_show; 2754 } 2755 2756 // Tear down the old native window 2757 SDL_DestroyWindowSurface(window); 2758 2759 if ((window->flags & SDL_WINDOW_OPENGL) != (flags & SDL_WINDOW_OPENGL)) { 2760 if (flags & SDL_WINDOW_OPENGL) { 2761 need_gl_load = true; 2762 } else { 2763 need_gl_unload = true; 2764 } 2765 } else if (window->flags & SDL_WINDOW_OPENGL) { 2766 need_gl_unload = true; 2767 need_gl_load = true; 2768 } 2769 2770 if ((window->flags & SDL_WINDOW_VULKAN) != (flags & SDL_WINDOW_VULKAN)) { 2771 if (flags & SDL_WINDOW_VULKAN) { 2772 need_vulkan_load = true; 2773 } else { 2774 need_vulkan_unload = true; 2775 } 2776 } else if (window->flags & SDL_WINDOW_VULKAN) { 2777 need_vulkan_unload = true; 2778 need_vulkan_load = true; 2779 } 2780 2781 if (need_gl_unload) { 2782 SDL_GL_UnloadLibrary(); 2783 } 2784 2785 if (need_vulkan_unload) { 2786 SDL_Vulkan_UnloadLibrary(); 2787 } 2788 2789 if (_this->DestroyWindow && !(flags & SDL_WINDOW_EXTERNAL)) { 2790 _this->DestroyWindow(_this, window); 2791 } 2792 2793 if (need_gl_load) { 2794 if (!SDL_GL_LoadLibrary(NULL)) { 2795 return false; 2796 } 2797 loaded_opengl = true; 2798 } 2799 2800 if (need_vulkan_load) { 2801 if (!SDL_Vulkan_LoadLibrary(NULL)) { 2802 return false; 2803 } 2804 loaded_vulkan = true; 2805 } 2806 2807 window->flags = ((flags & CREATE_FLAGS) | SDL_WINDOW_HIDDEN); 2808 window->is_destroying = false; 2809 2810 if (_this->CreateSDLWindow && !(flags & SDL_WINDOW_EXTERNAL)) { 2811 /* Reset the window size to the original floating value, so the 2812 * recreated window has the proper base size. 2813 */ 2814 window->x = window->windowed.x = window->floating.x; 2815 window->y = window->windowed.y = window->floating.y; 2816 window->w = window->windowed.w = window->floating.w; 2817 window->h = window->windowed.h = window->floating.h; 2818 2819 if (!_this->CreateSDLWindow(_this, window, 0)) { 2820 if (loaded_opengl) { 2821 SDL_GL_UnloadLibrary(); 2822 window->flags &= ~SDL_WINDOW_OPENGL; 2823 } 2824 if (loaded_vulkan) { 2825 SDL_Vulkan_UnloadLibrary(); 2826 window->flags &= ~SDL_WINDOW_VULKAN; 2827 } 2828 return false; 2829 } 2830 } 2831 2832 if (flags & SDL_WINDOW_EXTERNAL) { 2833 window->flags |= SDL_WINDOW_EXTERNAL; 2834 } 2835 2836 if (_this->SetWindowTitle && window->title) { 2837 _this->SetWindowTitle(_this, window); 2838 } 2839 2840 if (_this->SetWindowIcon && window->icon) { 2841 _this->SetWindowIcon(_this, window, window->icon); 2842 } 2843 2844 if (_this->SetWindowMinimumSize && (window->min_w || window->min_h)) { 2845 _this->SetWindowMinimumSize(_this, window); 2846 } 2847 2848 if (_this->SetWindowMaximumSize && (window->max_w || window->max_h)) { 2849 _this->SetWindowMaximumSize(_this, window); 2850 } 2851 2852 if (_this->SetWindowAspectRatio && (window->min_aspect > 0.0f || window->max_aspect > 0.0f)) { 2853 _this->SetWindowAspectRatio(_this, window); 2854 } 2855 2856 if (window->hit_test) { 2857 _this->SetWindowHitTest(window, true); 2858 } 2859 2860 SDL_FinishWindowCreation(window, flags); 2861 2862 return true; 2863} 2864 2865bool SDL_ReconfigureWindow(SDL_Window *window, SDL_WindowFlags flags) 2866{ 2867 // Try to reconfigure the window for the desired flags first, before completely destroying and recreating it. 2868 if (!SDL_ReconfigureWindowInternal(window, flags)) { 2869 return SDL_RecreateWindow(window, flags); 2870 } 2871 2872 return true; 2873} 2874 2875bool SDL_HasWindows(void) 2876{ 2877 return _this && _this->windows; 2878} 2879 2880SDL_WindowID SDL_GetWindowID(SDL_Window *window) 2881{ 2882 CHECK_WINDOW_MAGIC(window, 0); 2883 2884 return window->id; 2885} 2886 2887SDL_Window *SDL_GetWindowFromID(SDL_WindowID id) 2888{ 2889 SDL_Window *window; 2890 2891 if (!_this) { 2892 SDL_UninitializedVideo(); 2893 return NULL; 2894 } 2895 if (id) { 2896 for (window = _this->windows; window; window = window->next) { 2897 if (window->id == id) { 2898 return window; 2899 } 2900 } 2901 } 2902 SDL_SetError("Invalid window ID"); 2903 return NULL; 2904} 2905 2906SDL_Window *SDL_GetWindowParent(SDL_Window *window) 2907{ 2908 CHECK_WINDOW_MAGIC(window, NULL); 2909 2910 return window->parent; 2911} 2912 2913SDL_PropertiesID SDL_GetWindowProperties(SDL_Window *window) 2914{ 2915 CHECK_WINDOW_MAGIC(window, 0); 2916 2917 if (window->props == 0) { 2918 window->props = SDL_CreateProperties(); 2919 } 2920 return window->props; 2921} 2922 2923SDL_WindowFlags SDL_GetWindowFlags(SDL_Window *window) 2924{ 2925 CHECK_WINDOW_MAGIC(window, 0); 2926 2927 return window->flags | window->pending_flags; 2928} 2929 2930bool SDL_SetWindowTitle(SDL_Window *window, const char *title) 2931{ 2932 CHECK_WINDOW_MAGIC(window, false); 2933 CHECK_WINDOW_NOT_POPUP(window, false); 2934 2935 if (title == window->title) { 2936 return true; 2937 } 2938 if (!title) { 2939 title = ""; 2940 } 2941 if (window->title && SDL_strcmp(title, window->title) == 0) { 2942 return true; 2943 } 2944 2945 SDL_free(window->title); 2946 2947 window->title = SDL_strdup(title); 2948 2949 if (_this->SetWindowTitle) { 2950 _this->SetWindowTitle(_this, window); 2951 } 2952 return true; 2953} 2954 2955const char *SDL_GetWindowTitle(SDL_Window *window) 2956{ 2957 CHECK_WINDOW_MAGIC(window, ""); 2958 2959 return window->title ? window->title : ""; 2960} 2961 2962bool SDL_SetWindowIcon(SDL_Window *window, SDL_Surface *icon) 2963{ 2964 CHECK_WINDOW_MAGIC(window, false); 2965 2966 CHECK_PARAM(!icon) { 2967 return SDL_InvalidParamError("icon"); 2968 } 2969 2970 SDL_DestroySurface(window->icon); 2971 2972 // Convert the icon into ARGB8888 2973 window->icon = SDL_ConvertSurface(icon, SDL_PIXELFORMAT_ARGB8888); 2974 if (!window->icon) { 2975 return false; 2976 } 2977 2978 if (!_this->SetWindowIcon) { 2979 return SDL_Unsupported(); 2980 } 2981 2982 return _this->SetWindowIcon(_this, window, window->icon); 2983} 2984 2985bool SDL_SetWindowPosition(SDL_Window *window, int x, int y) 2986{ 2987 SDL_DisplayID original_displayID; 2988 2989 CHECK_WINDOW_MAGIC(window, false); 2990 2991 const int w = window->last_size_pending ? window->pending.w : window->windowed.w; 2992 const int h = window->last_size_pending ? window->pending.h : window->windowed.h; 2993 2994 original_displayID = SDL_GetDisplayForWindow(window); 2995 window->pending_displayID = 0; 2996 2997 if (SDL_WINDOWPOS_ISUNDEFINED(x)) { 2998 x = window->windowed.x; 2999 } 3000 if (SDL_WINDOWPOS_ISUNDEFINED(y)) { 3001 y = window->windowed.y; 3002 } 3003 if (SDL_WINDOWPOS_ISCENTERED(x) || SDL_WINDOWPOS_ISCENTERED(y)) { 3004 SDL_DisplayID displayID = original_displayID; 3005 SDL_Rect bounds; 3006 3007 if (SDL_WINDOWPOS_ISCENTERED(x) && (x & 0xFFFF)) { 3008 displayID = (x & 0xFFFF); 3009 } else if (SDL_WINDOWPOS_ISCENTERED(y) && (y & 0xFFFF)) { 3010 displayID = (y & 0xFFFF); 3011 } 3012 if (displayID == 0 || SDL_GetDisplayIndex(displayID) < 0) { 3013 displayID = SDL_GetPrimaryDisplay(); 3014 } 3015 3016 window->pending_displayID = displayID; 3017 3018 SDL_zero(bounds); 3019 if (!SDL_GetDisplayUsableBounds(displayID, &bounds) || w > bounds.w || h > bounds.h) { 3020 if (!SDL_GetDisplayBounds(displayID, &bounds)) { 3021 return false; 3022 } 3023 } 3024 if (SDL_WINDOWPOS_ISCENTERED(x)) { 3025 x = bounds.x + (bounds.w - w) / 2; 3026 } 3027 if (SDL_WINDOWPOS_ISCENTERED(y)) { 3028 y = bounds.y + (bounds.h - h) / 2; 3029 } 3030 } else { 3031 /* See if the requested window position matches the origin of any displays and set 3032 * the pending fullscreen display ID if it does. This needs to be set early in case 3033 * the window is prevented from moving to the exact origin due to struts. 3034 */ 3035 window->pending_displayID = GetDisplayAtOrigin(x, y); 3036 } 3037 3038 window->pending.x = x; 3039 window->pending.y = y; 3040 window->undefined_x = false; 3041 window->undefined_y = false; 3042 window->last_position_pending = true; 3043 3044 if (_this->SetWindowPosition) { 3045 const bool result = _this->SetWindowPosition(_this, window); 3046 if (result) { 3047 SDL_SyncIfRequired(window); 3048 } 3049 return result; 3050 } 3051 3052 return SDL_Unsupported(); 3053} 3054 3055bool SDL_GetWindowPosition(SDL_Window *window, int *x, int *y) 3056{ 3057 CHECK_WINDOW_MAGIC(window, false); 3058 3059#ifdef SDL_PLATFORM_MACOS 3060 // On newer MacBooks, the fullscreen window might be placed below the camera notch, so use the actual window position 3061 bool use_display_origin = false; 3062#else 3063 // Fullscreen windows are always at their display's origin 3064 bool use_display_origin = ((window->flags & SDL_WINDOW_FULLSCREEN) != 0); 3065#endif 3066 if (use_display_origin) { 3067 SDL_DisplayID displayID; 3068 3069 if (x) { 3070 *x = 0; 3071 } 3072 if (y) { 3073 *y = 0; 3074 } 3075 3076 /* Find the window's monitor and update to the 3077 monitor offset. */ 3078 displayID = SDL_GetDisplayForWindow(window); 3079 if (displayID != 0) { 3080 SDL_Rect bounds; 3081 3082 SDL_zero(bounds); 3083 3084 SDL_GetDisplayBounds(displayID, &bounds); 3085 if (x) { 3086 *x = bounds.x; 3087 } 3088 if (y) { 3089 *y = bounds.y; 3090 } 3091 } 3092 } else { 3093 const bool use_pending = (window->flags & SDL_WINDOW_HIDDEN) && window->last_position_pending; 3094 if (x) { 3095 *x = use_pending ? window->pending.x : window->x; 3096 } 3097 if (y) { 3098 *y = use_pending ? window->pending.y : window->y; 3099 } 3100 } 3101 return true; 3102} 3103 3104bool SDL_SetWindowBordered(SDL_Window *window, bool bordered) 3105{ 3106 CHECK_WINDOW_MAGIC(window, false); 3107 CHECK_WINDOW_NOT_POPUP(window, false); 3108 3109 const bool want = (bordered != false); // normalize the flag. 3110 const bool have = !(window->flags & SDL_WINDOW_BORDERLESS); 3111 if ((want != have) && (_this->SetWindowBordered)) { 3112 if (want) { 3113 window->flags &= ~SDL_WINDOW_BORDERLESS; 3114 } else { 3115 window->flags |= SDL_WINDOW_BORDERLESS; 3116 } 3117 _this->SetWindowBordered(_this, window, want); 3118 } 3119 3120 return true; 3121} 3122 3123bool SDL_SetWindowResizable(SDL_Window *window, bool resizable) 3124{ 3125 CHECK_WINDOW_MAGIC(window, false); 3126 CHECK_WINDOW_NOT_POPUP(window, false); 3127 3128 const bool want = (resizable != false); // normalize the flag. 3129 const bool have = ((window->flags & SDL_WINDOW_RESIZABLE) != 0); 3130 if ((want != have) && (_this->SetWindowResizable)) { 3131 if (want) { 3132 window->flags |= SDL_WINDOW_RESIZABLE; 3133 } else { 3134 window->flags &= ~SDL_WINDOW_RESIZABLE; 3135 SDL_copyp(&window->windowed, &window->floating); 3136 } 3137 _this->SetWindowResizable(_this, window, want); 3138 } 3139 3140 return true; 3141} 3142 3143bool SDL_SetWindowAlwaysOnTop(SDL_Window *window, bool on_top) 3144{ 3145 CHECK_WINDOW_MAGIC(window, false); 3146 CHECK_WINDOW_NOT_POPUP(window, false); 3147 3148 const bool want = (on_top != false); // normalize the flag. 3149 const bool have = ((window->flags & SDL_WINDOW_ALWAYS_ON_TOP) != 0); 3150 if ((want != have) && (_this->SetWindowAlwaysOnTop)) { 3151 if (want) { 3152 window->flags |= SDL_WINDOW_ALWAYS_ON_TOP; 3153 } else { 3154 window->flags &= ~SDL_WINDOW_ALWAYS_ON_TOP; 3155 } 3156 _this->SetWindowAlwaysOnTop(_this, window, want); 3157 } 3158 3159 return true; 3160} 3161 3162bool SDL_SetWindowSize(SDL_Window *window, int w, int h) 3163{ 3164 CHECK_WINDOW_MAGIC(window, false); 3165 3166 CHECK_PARAM(w <= 0) { 3167 return SDL_InvalidParamError("w"); 3168 } 3169 CHECK_PARAM(h <= 0) { 3170 return SDL_InvalidParamError("h"); 3171 } 3172 3173 // It is possible for the aspect ratio constraints to not satisfy the size constraints. 3174 // The size constraints will override the aspect ratio constraints so we will apply the 3175 // the aspect ratio constraints first 3176 float new_aspect = w / (float)h; 3177 if (window->max_aspect > 0.0f && new_aspect > window->max_aspect) { 3178 w = (int)SDL_roundf(h * window->max_aspect); 3179 } else if (window->min_aspect > 0.0f && new_aspect < window->min_aspect) { 3180 h = (int)SDL_roundf(w / window->min_aspect); 3181 } 3182 3183 // Make sure we don't exceed any window size limits 3184 if (window->min_w && w < window->min_w) { 3185 w = window->min_w; 3186 } 3187 if (window->max_w && w > window->max_w) { 3188 w = window->max_w; 3189 } 3190 if (window->min_h && h < window->min_h) { 3191 h = window->min_h; 3192 } 3193 if (window->max_h && h > window->max_h) { 3194 h = window->max_h; 3195 } 3196 3197 window->last_size_pending = true; 3198 window->pending.w = w; 3199 window->pending.h = h; 3200 3201 if (_this->SetWindowSize) { 3202 _this->SetWindowSize(_this, window); 3203 SDL_SyncIfRequired(window); 3204 } else { 3205 return SDL_Unsupported(); 3206 } 3207 return true; 3208} 3209 3210bool SDL_GetWindowSize(SDL_Window *window, int *w, int *h) 3211{ 3212 CHECK_WINDOW_MAGIC(window, false); 3213 if (w) { 3214 *w = window->w; 3215 } 3216 if (h) { 3217 *h = window->h; 3218 } 3219 return true; 3220} 3221 3222bool SDL_SetWindowAspectRatio(SDL_Window *window, float min_aspect, float max_aspect) 3223{ 3224 CHECK_WINDOW_MAGIC(window, false); 3225 3226 window->min_aspect = min_aspect; 3227 window->max_aspect = max_aspect; 3228 if (_this->SetWindowAspectRatio) { 3229 _this->SetWindowAspectRatio(_this, window); 3230 } 3231 return SDL_SetWindowSize(window, window->floating.w, window->floating.h); 3232} 3233 3234bool SDL_GetWindowAspectRatio(SDL_Window *window, float *min_aspect, float *max_aspect) 3235{ 3236 CHECK_WINDOW_MAGIC(window, false); 3237 3238 if (min_aspect) { 3239 *min_aspect = window->min_aspect; 3240 } 3241 if (max_aspect) { 3242 *max_aspect = window->max_aspect; 3243 } 3244 return true; 3245} 3246 3247bool SDL_GetWindowBordersSize(SDL_Window *window, int *top, int *left, int *bottom, int *right) 3248{ 3249 int dummy = 0; 3250 3251 if (!top) { 3252 top = &dummy; 3253 } 3254 if (!left) { 3255 left = &dummy; 3256 } 3257 if (!right) { 3258 right = &dummy; 3259 } 3260 if (!bottom) { 3261 bottom = &dummy; 3262 } 3263 3264 // Always initialize, so applications don't have to care 3265 *top = *left = *bottom = *right = 0; 3266 3267 CHECK_WINDOW_MAGIC(window, false); 3268 3269 if (!_this->GetWindowBordersSize) { 3270 return SDL_Unsupported(); 3271 } 3272 3273 return _this->GetWindowBordersSize(_this, window, top, left, bottom, right); 3274} 3275 3276bool SDL_GetWindowSizeInPixels(SDL_Window *window, int *w, int *h) 3277{ 3278 int filter; 3279 3280 CHECK_WINDOW_MAGIC(window, false); 3281 3282 if (!w) { 3283 w = &filter; 3284 } 3285 3286 if (!h) { 3287 h = &filter; 3288 } 3289 3290 if (_this->GetWindowSizeInPixels) { 3291 _this->GetWindowSizeInPixels(_this, window, w, h); 3292 } else { 3293 SDL_DisplayID displayID = SDL_GetDisplayForWindow(window); 3294 const SDL_DisplayMode *mode; 3295 3296 SDL_GetWindowSize(window, w, h); 3297 3298 if ((window->flags & SDL_WINDOW_FULLSCREEN) && SDL_GetWindowFullscreenMode(window)) { 3299 mode = SDL_GetCurrentDisplayMode(displayID); 3300 } else { 3301 mode = SDL_GetDesktopDisplayMode(displayID); 3302 } 3303 if (mode) { 3304 *w = (int)SDL_ceilf(*w * mode->pixel_density); 3305 *h = (int)SDL_ceilf(*h * mode->pixel_density); 3306 } 3307 } 3308 return true; 3309} 3310 3311bool SDL_SetWindowMinimumSize(SDL_Window *window, int min_w, int min_h) 3312{ 3313 CHECK_WINDOW_MAGIC(window, false); 3314 CHECK_PARAM(min_w < 0) { 3315 return SDL_InvalidParamError("min_w"); 3316 } 3317 CHECK_PARAM(min_h < 0) { 3318 return SDL_InvalidParamError("min_h"); 3319 } 3320 3321 CHECK_PARAM((window->max_w && min_w > window->max_w) || (window->max_h && min_h > window->max_h)) { 3322 return SDL_SetError("SDL_SetWindowMinimumSize(): Tried to set minimum size larger than maximum size"); 3323 } 3324 3325 window->min_w = min_w; 3326 window->min_h = min_h; 3327 3328 if (_this->SetWindowMinimumSize) { 3329 _this->SetWindowMinimumSize(_this, window); 3330 } 3331 3332 // Ensure that window is not smaller than minimal size 3333 int w = window->last_size_pending ? window->pending.w : window->floating.w; 3334 int h = window->last_size_pending ? window->pending.h : window->floating.h; 3335 w = window->min_w ? SDL_max(w, window->min_w) : w; 3336 h = window->min_h ? SDL_max(h, window->min_h) : h; 3337 return SDL_SetWindowSize(window, w, h); 3338} 3339 3340bool SDL_GetWindowMinimumSize(SDL_Window *window, int *min_w, int *min_h) 3341{ 3342 CHECK_WINDOW_MAGIC(window, false); 3343 if (min_w) { 3344 *min_w = window->min_w; 3345 } 3346 if (min_h) { 3347 *min_h = window->min_h; 3348 } 3349 return true; 3350} 3351 3352bool SDL_SetWindowMaximumSize(SDL_Window *window, int max_w, int max_h) 3353{ 3354 CHECK_WINDOW_MAGIC(window, false); 3355 CHECK_PARAM(max_w < 0) { 3356 return SDL_InvalidParamError("max_w"); 3357 } 3358 CHECK_PARAM(max_h < 0) { 3359 return SDL_InvalidParamError("max_h"); 3360 } 3361 3362 if ((max_w && max_w < window->min_w) || 3363 (max_h && max_h < window->min_h)) { 3364 return SDL_SetError("SDL_SetWindowMaximumSize(): Tried to set maximum size smaller than minimum size"); 3365 } 3366 3367 window->max_w = max_w; 3368 window->max_h = max_h; 3369 3370 if (_this->SetWindowMaximumSize) { 3371 _this->SetWindowMaximumSize(_this, window); 3372 } 3373 3374 // Ensure that window is not larger than maximal size 3375 int w = window->last_size_pending ? window->pending.w : window->floating.w; 3376 int h = window->last_size_pending ? window->pending.h : window->floating.h; 3377 w = window->max_w ? SDL_min(w, window->max_w) : w; 3378 h = window->max_h ? SDL_min(h, window->max_h) : h; 3379 return SDL_SetWindowSize(window, w, h); 3380} 3381 3382bool SDL_GetWindowMaximumSize(SDL_Window *window, int *max_w, int *max_h) 3383{ 3384 CHECK_WINDOW_MAGIC(window, false); 3385 if (max_w) { 3386 *max_w = window->max_w; 3387 } 3388 if (max_h) { 3389 *max_h = window->max_h; 3390 } 3391 return true; 3392} 3393 3394bool SDL_ShowWindow(SDL_Window *window) 3395{ 3396 SDL_Window *child; 3397 CHECK_WINDOW_MAGIC(window, false); 3398 3399 if (!(window->flags & SDL_WINDOW_HIDDEN)) { 3400 return true; 3401 } 3402 3403 // If the parent is hidden, set the flag to restore this when the parent is shown 3404 if (window->parent && (window->parent->flags & SDL_WINDOW_HIDDEN)) { 3405 window->restore_on_show = true; 3406 return true; 3407 } 3408 3409 if (_this->ShowWindow) { 3410 _this->ShowWindow(_this, window); 3411 } else { 3412 SDL_SetMouseFocus(window); 3413 SDL_SetKeyboardFocus(window); 3414 } 3415 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SHOWN, 0, 0); 3416 3417 // Restore child windows 3418 for (child = window->first_child; child; child = child->next_sibling) { 3419 if (!child->restore_on_show && (child->flags & SDL_WINDOW_HIDDEN)) { 3420 break; 3421 } 3422 SDL_ShowWindow(child); 3423 child->restore_on_show = false; 3424 } 3425 return true; 3426} 3427 3428bool SDL_HideWindow(SDL_Window *window) 3429{ 3430 SDL_Window *child; 3431 CHECK_WINDOW_MAGIC(window, false); 3432 3433 if (window->flags & SDL_WINDOW_HIDDEN) { 3434 window->restore_on_show = false; 3435 return true; 3436 } 3437 3438 // Hide all child windows 3439 for (child = window->first_child; child; child = child->next_sibling) { 3440 if (child->flags & SDL_WINDOW_HIDDEN) { 3441 break; 3442 } 3443 SDL_HideWindow(child); 3444 child->restore_on_show = true; 3445 } 3446 3447 // Store the flags for restoration later. 3448 const SDL_WindowFlags pending_mask = (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_KEYBOARD_GRABBED | SDL_WINDOW_MOUSE_GRABBED); 3449 window->pending_flags = (window->flags & pending_mask); 3450 3451 window->is_hiding = true; 3452 if (_this->HideWindow) { 3453 _this->HideWindow(_this, window); 3454 } else { 3455 SDL_SetMouseFocus(NULL); 3456 SDL_SetKeyboardFocus(NULL); 3457 } 3458 window->is_hiding = false; 3459 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_HIDDEN, 0, 0); 3460 return true; 3461} 3462 3463bool SDL_RaiseWindow(SDL_Window *window) 3464{ 3465 CHECK_WINDOW_MAGIC(window, false); 3466 3467 if (window->flags & SDL_WINDOW_HIDDEN) { 3468 return true; 3469 } 3470 if (_this->RaiseWindow) { 3471 _this->RaiseWindow(_this, window); 3472 } 3473 return true; 3474} 3475 3476bool SDL_MaximizeWindow(SDL_Window *window) 3477{ 3478 CHECK_WINDOW_MAGIC(window, false); 3479 CHECK_WINDOW_NOT_POPUP(window, false); 3480 3481 if (!_this->MaximizeWindow) { 3482 return SDL_Unsupported(); 3483 } 3484 3485 if (!(window->flags & SDL_WINDOW_RESIZABLE)) { 3486 return SDL_SetError("A window without the 'SDL_WINDOW_RESIZABLE' flag can't be maximized"); 3487 } 3488 3489 if (window->flags & SDL_WINDOW_HIDDEN) { 3490 window->pending_flags |= SDL_WINDOW_MAXIMIZED; 3491 return true; 3492 } 3493 3494 _this->MaximizeWindow(_this, window); 3495 SDL_SyncIfRequired(window); 3496 return true; 3497} 3498 3499bool SDL_MinimizeWindow(SDL_Window *window) 3500{ 3501 CHECK_WINDOW_MAGIC(window, false); 3502 CHECK_WINDOW_NOT_POPUP(window, false); 3503 3504 if (!_this->MinimizeWindow) { 3505 return SDL_Unsupported(); 3506 } 3507 3508 if (window->flags & SDL_WINDOW_HIDDEN) { 3509 window->pending_flags |= SDL_WINDOW_MINIMIZED; 3510 return true; 3511 } 3512 3513 _this->MinimizeWindow(_this, window); 3514 SDL_SyncIfRequired(window); 3515 return true; 3516} 3517 3518bool SDL_RestoreWindow(SDL_Window *window) 3519{ 3520 CHECK_WINDOW_MAGIC(window, false); 3521 CHECK_WINDOW_NOT_POPUP(window, false); 3522 3523 if (!_this->RestoreWindow) { 3524 return SDL_Unsupported(); 3525 } 3526 3527 if (window->flags & SDL_WINDOW_HIDDEN) { 3528 window->pending_flags &= ~(SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED); 3529 return true; 3530 } 3531 3532 _this->RestoreWindow(_this, window); 3533 SDL_SyncIfRequired(window); 3534 return true; 3535} 3536 3537bool SDL_SetWindowFullscreen(SDL_Window *window, bool fullscreen) 3538{ 3539 bool result; 3540 3541 CHECK_WINDOW_MAGIC(window, false); 3542 CHECK_WINDOW_NOT_POPUP(window, false); 3543 3544 if (window->flags & SDL_WINDOW_HIDDEN) { 3545 if (fullscreen) { 3546 window->pending_flags |= SDL_WINDOW_FULLSCREEN; 3547 } else { 3548 window->pending_flags &= ~SDL_WINDOW_FULLSCREEN; 3549 } 3550 return true; 3551 } 3552 3553 if (fullscreen) { 3554 // Set the current fullscreen mode to the desired mode 3555 SDL_copyp(&window->current_fullscreen_mode, &window->requested_fullscreen_mode); 3556 } 3557 3558 result = SDL_UpdateFullscreenMode(window, fullscreen ? SDL_FULLSCREEN_OP_ENTER : SDL_FULLSCREEN_OP_LEAVE, true); 3559 3560 if (!fullscreen || !result) { 3561 // Clear the current fullscreen mode. 3562 SDL_zero(window->current_fullscreen_mode); 3563 } 3564 3565 if (result) { 3566 SDL_SyncIfRequired(window); 3567 } 3568 3569 return result; 3570} 3571 3572bool SDL_SyncWindow(SDL_Window *window) 3573{ 3574 CHECK_WINDOW_MAGIC(window, false); 3575 3576 if (_this->SyncWindow) { 3577 return _this->SyncWindow(_this, window); 3578 } else { 3579 return true; 3580 } 3581} 3582 3583static bool ShouldAttemptTextureFramebuffer(void) 3584{ 3585 const char *hint; 3586 bool attempt_texture_framebuffer; 3587 3588 // If the driver doesn't support window framebuffers, always use the render API. 3589 if (!_this->CreateWindowFramebuffer) { 3590 return true; 3591 } 3592 3593 // See if there's a hint override 3594 hint = SDL_GetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION); 3595 if (hint && *hint) { 3596 if (*hint == '0' || SDL_strcasecmp(hint, "false") == 0 || SDL_strcasecmp(hint, SDL_SOFTWARE_RENDERER) == 0) { 3597 attempt_texture_framebuffer = false; 3598 } else { 3599 attempt_texture_framebuffer = true; 3600 } 3601 } else { 3602 if (SDL_DriverHasSlowFramebuffer(_this)) { 3603 attempt_texture_framebuffer = true; 3604 } else { 3605 attempt_texture_framebuffer = false; 3606 } 3607 } 3608 return attempt_texture_framebuffer; 3609} 3610 3611static SDL_Surface *SDL_CreateWindowFramebuffer(SDL_Window *window) 3612{ 3613 SDL_PixelFormat format = SDL_PIXELFORMAT_UNKNOWN; 3614 void *pixels = NULL; 3615 int pitch = 0; 3616 bool created_framebuffer = false; 3617 int w, h; 3618 3619 SDL_GetWindowSizeInPixels(window, &w, &h); 3620 3621 /* This will switch the video backend from using a software surface to 3622 using a GPU texture through the 2D render API, if we think this would 3623 be more efficient. This only checks once, on demand. */ 3624 if (!_this->checked_texture_framebuffer) { 3625 if (ShouldAttemptTextureFramebuffer()) { 3626 if (!SDL_CreateWindowTexture(_this, window, &format, &pixels, &pitch)) { 3627 /* !!! FIXME: if this failed halfway (made renderer, failed to make texture, etc), 3628 !!! FIXME: we probably need to clean this up so it doesn't interfere with 3629 !!! FIXME: a software fallback at the system level (can we blit to an 3630 !!! FIXME: OpenGL window? etc). */ 3631 } else { 3632 // future attempts will just try to use a texture framebuffer. 3633 /* !!! FIXME: maybe we shouldn't override these but check if we used a texture 3634 !!! FIXME: framebuffer at the right places; is it feasible we could have an 3635 !!! FIXME: accelerated OpenGL window and a second ends up in software? */ 3636 _this->CreateWindowFramebuffer = SDL_CreateWindowTexture; 3637 _this->SetWindowFramebufferVSync = SDL_SetWindowTextureVSync; 3638 _this->GetWindowFramebufferVSync = SDL_GetWindowTextureVSync; 3639 _this->UpdateWindowFramebuffer = SDL_UpdateWindowTexture; 3640 _this->DestroyWindowFramebuffer = SDL_DestroyWindowTexture; 3641 created_framebuffer = true; 3642 } 3643 } 3644 3645 _this->checked_texture_framebuffer = true; // don't check this again. 3646 } 3647 3648 if (!created_framebuffer) { 3649 if (!_this->CreateWindowFramebuffer || !_this->UpdateWindowFramebuffer) { 3650 SDL_SetError("Window framebuffer support not available"); 3651 return NULL; 3652 } 3653 3654 if (!_this->CreateWindowFramebuffer(_this, window, &format, &pixels, &pitch)) { 3655 return NULL; 3656 } 3657 } 3658 3659 if (window->surface) { 3660 // We may have gone recursive and already created the surface 3661 return window->surface; 3662 } 3663 3664 return SDL_CreateSurfaceFrom(w, h, format, pixels, pitch); 3665} 3666 3667bool SDL_WindowHasSurface(SDL_Window *window) 3668{ 3669 CHECK_WINDOW_MAGIC(window, false); 3670 3671 return window->surface ? true : false; 3672} 3673 3674SDL_Surface *SDL_GetWindowSurface(SDL_Window *window) 3675{ 3676 CHECK_WINDOW_MAGIC(window, NULL); 3677 3678 if (!window->surface_valid) { 3679 if (window->surface) { 3680 window->surface->internal_flags &= ~SDL_INTERNAL_SURFACE_DONTFREE; 3681 SDL_DestroySurface(window->surface); 3682 window->surface = NULL; 3683 } 3684 3685 window->surface = SDL_CreateWindowFramebuffer(window); 3686 if (window->surface) { 3687 window->surface_valid = true; 3688 window->surface->internal_flags |= SDL_INTERNAL_SURFACE_DONTFREE; 3689 } 3690 } 3691 return window->surface; 3692} 3693 3694bool SDL_SetWindowSurfaceVSync(SDL_Window *window, int vsync) 3695{ 3696 CHECK_WINDOW_MAGIC(window, false); 3697 3698 if (!_this->SetWindowFramebufferVSync) { 3699 return SDL_Unsupported(); 3700 } 3701 return _this->SetWindowFramebufferVSync(_this, window, vsync); 3702} 3703 3704bool SDL_GetWindowSurfaceVSync(SDL_Window *window, int *vsync) 3705{ 3706 CHECK_WINDOW_MAGIC(window, false); 3707 3708 if (!_this->GetWindowFramebufferVSync) { 3709 return SDL_Unsupported(); 3710 } 3711 return _this->GetWindowFramebufferVSync(_this, window, vsync); 3712} 3713 3714bool SDL_UpdateWindowSurface(SDL_Window *window) 3715{ 3716 SDL_Rect full_rect; 3717 3718 CHECK_WINDOW_MAGIC(window, false); 3719 3720 full_rect.x = 0; 3721 full_rect.y = 0; 3722 SDL_GetWindowSizeInPixels(window, &full_rect.w, &full_rect.h); 3723 3724 return SDL_UpdateWindowSurfaceRects(window, &full_rect, 1); 3725} 3726 3727bool SDL_UpdateWindowSurfaceRects(SDL_Window *window, const SDL_Rect *rects, 3728 int numrects) 3729{ 3730 CHECK_WINDOW_MAGIC(window, false); 3731 3732 if (!window->surface_valid) { 3733 return SDL_SetError("Window surface is invalid, please call SDL_GetWindowSurface() to get a new surface"); 3734 } 3735 3736 SDL_assert(_this->checked_texture_framebuffer); // we should have done this before we had a valid surface. 3737 3738 return _this->UpdateWindowFramebuffer(_this, window, rects, numrects); 3739} 3740 3741bool SDL_DestroyWindowSurface(SDL_Window *window) 3742{ 3743 CHECK_WINDOW_MAGIC(window, false); 3744 3745 if (window->surface) { 3746 window->surface->internal_flags &= ~SDL_INTERNAL_SURFACE_DONTFREE; 3747 SDL_DestroySurface(window->surface); 3748 window->surface = NULL; 3749 window->surface_valid = false; 3750 } 3751 3752 if (_this->checked_texture_framebuffer) { // never checked? No framebuffer to destroy. Don't risk calling the wrong implementation. 3753 if (_this->DestroyWindowFramebuffer) { 3754 _this->DestroyWindowFramebuffer(_this, window); 3755 } 3756 } 3757 return true; 3758} 3759 3760bool SDL_SetWindowOpacity(SDL_Window *window, float opacity) 3761{ 3762 bool result; 3763 3764 CHECK_WINDOW_MAGIC(window, false); 3765 3766 if (!_this->SetWindowOpacity) { 3767 return SDL_Unsupported(); 3768 } 3769 3770 if (opacity < 0.0f) { 3771 opacity = 0.0f; 3772 } else if (opacity > 1.0f) { 3773 opacity = 1.0f; 3774 } 3775 3776 result = _this->SetWindowOpacity(_this, window, opacity); 3777 if (result) { 3778 window->opacity = opacity; 3779 } 3780 3781 return result; 3782} 3783 3784float SDL_GetWindowOpacity(SDL_Window *window) 3785{ 3786 CHECK_WINDOW_MAGIC(window, -1.0f); 3787 3788 return window->opacity; 3789} 3790 3791bool SDL_SetWindowParent(SDL_Window *window, SDL_Window *parent) 3792{ 3793 CHECK_WINDOW_MAGIC(window, false); 3794 CHECK_WINDOW_NOT_POPUP(window, false); 3795 3796 if (parent) { 3797 CHECK_WINDOW_MAGIC(parent, false); 3798 CHECK_WINDOW_NOT_POPUP(parent, false); 3799 } 3800 3801 if (window == parent) { 3802 return SDL_SetError("Cannot set the parent of a window to itself."); 3803 } 3804 3805 if (!_this->SetWindowParent) { 3806 return SDL_Unsupported(); 3807 } 3808 3809 if (window->flags & SDL_WINDOW_MODAL) { 3810 return SDL_SetError("Modal windows cannot change parents; call SDL_SetWindowModal() to clear modal status first."); 3811 } 3812 3813 if (window->parent == parent) { 3814 return true; 3815 } 3816 3817 const bool ret = _this->SetWindowParent(_this, window, parent); 3818 SDL_UpdateWindowHierarchy(window, ret ? parent : NULL); 3819 3820 return ret; 3821} 3822 3823bool SDL_SetWindowModal(SDL_Window *window, bool modal) 3824{ 3825 CHECK_WINDOW_MAGIC(window, false); 3826 CHECK_WINDOW_NOT_POPUP(window, false); 3827 3828 if (!_this->SetWindowModal) { 3829 return SDL_Unsupported(); 3830 } 3831 3832 if (modal) { 3833 if (!window->parent) { 3834 return SDL_SetError("Window must have a parent to enable the modal state; use SDL_SetWindowParent() to set the parent first."); 3835 } 3836 window->flags |= SDL_WINDOW_MODAL; 3837 } else if (window->flags & SDL_WINDOW_MODAL) { 3838 window->flags &= ~SDL_WINDOW_MODAL; 3839 } else { 3840 return true; // Already not modal, so nothing to do. 3841 } 3842 3843 if (window->flags & SDL_WINDOW_HIDDEN) { 3844 return true; 3845 } 3846 3847 return _this->SetWindowModal(_this, window, modal); 3848} 3849 3850bool SDL_ShouldRelinquishPopupFocus(SDL_Window *window, SDL_Window **new_focus) 3851{ 3852 SDL_Window *focus = window->parent; 3853 bool set_focus = !!(window->flags & SDL_WINDOW_INPUT_FOCUS); 3854 3855 // Find the highest level window, up to the toplevel parent, that isn't being hidden or destroyed, and can grab the keyboard focus. 3856 while (SDL_WINDOW_IS_POPUP(focus) && ((focus->flags & SDL_WINDOW_NOT_FOCUSABLE) || focus->is_hiding || focus->is_destroying)) { 3857 focus = focus->parent; 3858 3859 // If some window in the chain currently had focus, set it to the new lowest-level window. 3860 if (!set_focus) { 3861 set_focus = !!(focus->flags & SDL_WINDOW_INPUT_FOCUS); 3862 } 3863 } 3864 3865 *new_focus = focus; 3866 return set_focus; 3867} 3868 3869bool SDL_ShouldFocusPopup(SDL_Window *window) 3870{ 3871 SDL_Window *toplevel_parent; 3872 for (toplevel_parent = window->parent; SDL_WINDOW_IS_POPUP(toplevel_parent); toplevel_parent = toplevel_parent->parent) { 3873 } 3874 3875 SDL_Window *current_focus = toplevel_parent->keyboard_focus; 3876 bool found_higher_focus = false; 3877 3878 /* Traverse the window tree from the currently focused window to the toplevel parent and see if we encounter 3879 * the new focus request. If the new window is found, a higher-level window already has focus. 3880 */ 3881 SDL_Window *w; 3882 for (w = current_focus; w != toplevel_parent; w = w->parent) { 3883 if (w == window) { 3884 found_higher_focus = true; 3885 break; 3886 } 3887 } 3888 3889 return !found_higher_focus || w == toplevel_parent; 3890} 3891 3892bool SDL_SetWindowFocusable(SDL_Window *window, bool focusable) 3893{ 3894 CHECK_WINDOW_MAGIC(window, false); 3895 3896 const bool want = (focusable != false); // normalize the flag. 3897 const bool have = !(window->flags & SDL_WINDOW_NOT_FOCUSABLE); 3898 if ((want != have) && (_this->SetWindowFocusable)) { 3899 if (want) { 3900 window->flags &= ~SDL_WINDOW_NOT_FOCUSABLE; 3901 } else { 3902 window->flags |= SDL_WINDOW_NOT_FOCUSABLE; 3903 } 3904 if (!_this->SetWindowFocusable(_this, window, want)) { 3905 return false; 3906 } 3907 } 3908 3909 return true; 3910} 3911 3912bool SDL_SetWindowFillDocument(SDL_Window *window, bool fill) 3913{ 3914 CHECK_WINDOW_MAGIC(window, false); 3915 3916 const bool want = (fill != false); // normalize the flag. 3917 const bool have = ((window->flags & SDL_WINDOW_FILL_DOCUMENT) != 0); 3918 if ((want != have) && (_this->SetWindowFillDocument)) { 3919 if (!_this->SetWindowFillDocument(_this, window, want)) { 3920 return false; 3921 } else if (want) { 3922 window->flags |= SDL_WINDOW_FILL_DOCUMENT; 3923 } else { 3924 window->flags &= ~SDL_WINDOW_FILL_DOCUMENT; 3925 } 3926 } 3927 3928 return true; 3929} 3930 3931void SDL_UpdateWindowGrab(SDL_Window *window) 3932{ 3933 bool keyboard_grabbed, mouse_grabbed; 3934 3935 if (window->flags & SDL_WINDOW_INPUT_FOCUS) { 3936 if (SDL_GetMouse()->relative_mode || (window->flags & SDL_WINDOW_MOUSE_GRABBED)) { 3937 mouse_grabbed = true; 3938 } else { 3939 mouse_grabbed = false; 3940 } 3941 3942 if (window->flags & SDL_WINDOW_KEYBOARD_GRABBED) { 3943 keyboard_grabbed = true; 3944 } else { 3945 keyboard_grabbed = false; 3946 } 3947 } else { 3948 mouse_grabbed = false; 3949 keyboard_grabbed = false; 3950 } 3951 3952 if (mouse_grabbed || keyboard_grabbed) { 3953 if (_this->grabbed_window && (_this->grabbed_window != window)) { 3954 // stealing a grab from another window! 3955 _this->grabbed_window->flags &= ~(SDL_WINDOW_MOUSE_GRABBED | SDL_WINDOW_KEYBOARD_GRABBED); 3956 if (_this->SetWindowMouseGrab) { 3957 _this->SetWindowMouseGrab(_this, _this->grabbed_window, false); 3958 } 3959 if (_this->SetWindowKeyboardGrab) { 3960 _this->SetWindowKeyboardGrab(_this, _this->grabbed_window, false); 3961 } 3962 } 3963 _this->grabbed_window = window; 3964 } else if (_this->grabbed_window == window) { 3965 _this->grabbed_window = NULL; // ungrabbing input. 3966 } 3967 3968 if (_this->SetWindowMouseGrab) { 3969 if (!_this->SetWindowMouseGrab(_this, window, mouse_grabbed)) { 3970 window->flags &= ~SDL_WINDOW_MOUSE_GRABBED; 3971 } 3972 } 3973 if (_this->SetWindowKeyboardGrab) { 3974 if (!_this->SetWindowKeyboardGrab(_this, window, keyboard_grabbed)) { 3975 window->flags &= ~SDL_WINDOW_KEYBOARD_GRABBED; 3976 } 3977 } 3978 3979 if (_this->grabbed_window && !(_this->grabbed_window->flags & (SDL_WINDOW_MOUSE_GRABBED | SDL_WINDOW_KEYBOARD_GRABBED))) { 3980 _this->grabbed_window = NULL; 3981 } 3982} 3983 3984bool SDL_SetWindowKeyboardGrab(SDL_Window *window, bool grabbed) 3985{ 3986 CHECK_WINDOW_MAGIC(window, false); 3987 CHECK_WINDOW_NOT_POPUP(window, false); 3988 3989 if (window->flags & SDL_WINDOW_HIDDEN) { 3990 if (grabbed) { 3991 window->pending_flags |= SDL_WINDOW_KEYBOARD_GRABBED; 3992 } else { 3993 window->pending_flags &= ~SDL_WINDOW_KEYBOARD_GRABBED; 3994 } 3995 return true; 3996 } 3997 3998 if (!!grabbed == !!(window->flags & SDL_WINDOW_KEYBOARD_GRABBED)) { 3999 return true; 4000 } 4001 if (grabbed) { 4002 window->flags |= SDL_WINDOW_KEYBOARD_GRABBED; 4003 } else { 4004 window->flags &= ~SDL_WINDOW_KEYBOARD_GRABBED; 4005 } 4006 SDL_UpdateWindowGrab(window); 4007 4008 if (grabbed && !(window->flags & SDL_WINDOW_KEYBOARD_GRABBED)) { 4009 return false; 4010 } 4011 return true; 4012} 4013 4014bool SDL_SetWindowMouseGrab(SDL_Window *window, bool grabbed) 4015{ 4016 CHECK_WINDOW_MAGIC(window, false); 4017 CHECK_WINDOW_NOT_POPUP(window, false); 4018 4019 if (window->flags & SDL_WINDOW_HIDDEN) { 4020 if (grabbed) { 4021 window->pending_flags |= SDL_WINDOW_MOUSE_GRABBED; 4022 } else { 4023 window->pending_flags &= ~SDL_WINDOW_MOUSE_GRABBED; 4024 } 4025 return true; 4026 } 4027 4028 if (!!grabbed == !!(window->flags & SDL_WINDOW_MOUSE_GRABBED)) { 4029 return true; 4030 } 4031 if (grabbed) { 4032 window->flags |= SDL_WINDOW_MOUSE_GRABBED; 4033 } else { 4034 window->flags &= ~SDL_WINDOW_MOUSE_GRABBED; 4035 } 4036 SDL_UpdateWindowGrab(window); 4037 4038 if (grabbed && !(window->flags & SDL_WINDOW_MOUSE_GRABBED)) { 4039 return false; 4040 } 4041 return true; 4042} 4043 4044bool SDL_GetWindowKeyboardGrab(SDL_Window *window) 4045{ 4046 CHECK_WINDOW_MAGIC(window, false); 4047 return window == _this->grabbed_window && (_this->grabbed_window->flags & SDL_WINDOW_KEYBOARD_GRABBED); 4048} 4049 4050bool SDL_GetWindowMouseGrab(SDL_Window *window) 4051{ 4052 CHECK_WINDOW_MAGIC(window, false); 4053 return window == _this->grabbed_window && (_this->grabbed_window->flags & SDL_WINDOW_MOUSE_GRABBED); 4054} 4055 4056SDL_Window *SDL_GetGrabbedWindow(void) 4057{ 4058 if (_this && _this->grabbed_window && 4059 (_this->grabbed_window->flags & (SDL_WINDOW_MOUSE_GRABBED | SDL_WINDOW_KEYBOARD_GRABBED)) != 0) { 4060 return _this->grabbed_window; 4061 } else { 4062 return NULL; 4063 } 4064} 4065 4066bool SDL_SetWindowMouseRect(SDL_Window *window, const SDL_Rect *rect) 4067{ 4068 CHECK_WINDOW_MAGIC(window, false); 4069 4070 if (!_this->SetWindowMouseRect) { 4071 return SDL_Unsupported(); 4072 } 4073 4074 SDL_Rect zero = { 0, 0, 0, 0 }; 4075 if (!rect) { 4076 rect = &zero; 4077 } 4078 4079 if (SDL_memcmp(&window->mouse_rect, rect, sizeof(*rect)) == 0) { 4080 return true; 4081 } 4082 SDL_memcpy(&window->mouse_rect, rect, sizeof(*rect)); 4083 4084 return _this->SetWindowMouseRect(_this, window); 4085} 4086 4087const SDL_Rect *SDL_GetWindowMouseRect(SDL_Window *window) 4088{ 4089 CHECK_WINDOW_MAGIC(window, NULL); 4090 4091 if (SDL_RectEmpty(&window->mouse_rect)) { 4092 return NULL; 4093 } else { 4094 return &window->mouse_rect; 4095 } 4096} 4097 4098bool SDL_SetWindowRelativeMouseMode(SDL_Window *window, bool enabled) 4099{ 4100 CHECK_WINDOW_MAGIC(window, false); 4101 4102 /* If the app toggles relative mode directly, it probably shouldn't 4103 * also be emulating it using repeated mouse warps, so disable 4104 * mouse warp emulation by default. 4105 */ 4106 SDL_DisableMouseWarpEmulation(); 4107 4108 if (enabled == SDL_GetWindowRelativeMouseMode(window)) { 4109 return true; 4110 } 4111 4112 if (enabled) { 4113 window->flags |= SDL_WINDOW_MOUSE_RELATIVE_MODE; 4114 } else { 4115 window->flags &= ~SDL_WINDOW_MOUSE_RELATIVE_MODE; 4116 } 4117 4118 if (!SDL_UpdateRelativeMouseMode()) { 4119 if (enabled) { 4120 window->flags &= ~SDL_WINDOW_MOUSE_RELATIVE_MODE; 4121 } else { 4122 window->flags |= SDL_WINDOW_MOUSE_RELATIVE_MODE; 4123 } 4124 return false; 4125 } 4126 return true; 4127} 4128 4129bool SDL_GetWindowRelativeMouseMode(SDL_Window *window) 4130{ 4131 CHECK_WINDOW_MAGIC(window, false); 4132 4133 if (window->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE) { 4134 return true; 4135 } else { 4136 return false; 4137 } 4138} 4139 4140bool SDL_FlashWindow(SDL_Window *window, SDL_FlashOperation operation) 4141{ 4142 CHECK_WINDOW_MAGIC(window, false); 4143 CHECK_WINDOW_NOT_POPUP(window, false); 4144 4145 if (_this->FlashWindow) { 4146 return _this->FlashWindow(_this, window, operation); 4147 } 4148 4149 return SDL_Unsupported(); 4150} 4151 4152bool SDL_SetWindowProgressState(SDL_Window *window, SDL_ProgressState state) 4153{ 4154 CHECK_WINDOW_MAGIC(window, false); 4155 CHECK_WINDOW_NOT_POPUP(window, false); 4156 4157 CHECK_PARAM(state < SDL_PROGRESS_STATE_NONE || state > SDL_PROGRESS_STATE_ERROR) { 4158 return SDL_InvalidParamError("state"); 4159 } 4160 4161 window->progress_state = state; 4162 4163 if (_this->ApplyWindowProgress) { 4164 if (!_this->ApplyWindowProgress(_this, window)) { 4165 return false; 4166 } 4167 } 4168 4169 return true; 4170} 4171 4172SDL_ProgressState SDL_GetWindowProgressState(SDL_Window *window) 4173{ 4174 CHECK_WINDOW_MAGIC(window, SDL_PROGRESS_STATE_INVALID); 4175 CHECK_WINDOW_NOT_POPUP(window, SDL_PROGRESS_STATE_INVALID); 4176 4177 return window->progress_state; 4178} 4179 4180bool SDL_SetWindowProgressValue(SDL_Window *window, float value) 4181{ 4182 CHECK_WINDOW_MAGIC(window, false); 4183 CHECK_WINDOW_NOT_POPUP(window, false); 4184 4185 value = SDL_clamp(value, 0.0f, 1.f); 4186 4187 window->progress_value = value; 4188 4189 if (_this->ApplyWindowProgress) { 4190 if (!_this->ApplyWindowProgress(_this, window)) { 4191 return false; 4192 } 4193 } 4194 4195 return true; 4196} 4197 4198float SDL_GetWindowProgressValue(SDL_Window *window) 4199{ 4200 CHECK_WINDOW_MAGIC(window, -1.0f); 4201 CHECK_WINDOW_NOT_POPUP(window, -1.0f); 4202 4203 return window->progress_value; 4204} 4205 4206void SDL_OnWindowShown(SDL_Window *window) 4207{ 4208 // Set window state if we have pending window flags cached 4209 ApplyWindowFlags(window, window->pending_flags); 4210 window->pending_flags = 0; 4211} 4212 4213void SDL_OnWindowHidden(SDL_Window *window) 4214{ 4215 /* Store the maximized and fullscreen flags for restoration later, in case 4216 * this was initiated by the window manager due to the window being unmapped 4217 * when minimized. 4218 */ 4219 window->pending_flags |= (window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)); 4220 4221 // The window is already hidden at this point, so just change the mode back if necessary. 4222 SDL_UpdateFullscreenMode(window, SDL_FULLSCREEN_OP_LEAVE, false); 4223} 4224 4225void SDL_OnWindowDisplayChanged(SDL_Window *window) 4226{ 4227 // Don't run this if a fullscreen change was made in an event watcher callback in response to a display changed event. 4228 if (window->update_fullscreen_on_display_changed && (window->flags & SDL_WINDOW_FULLSCREEN)) { 4229 const bool auto_mode_switch = SDL_GetHintBoolean(SDL_HINT_VIDEO_MATCH_EXCLUSIVE_MODE_ON_MOVE, true); 4230 4231 if (auto_mode_switch && (window->requested_fullscreen_mode.w != 0 || window->requested_fullscreen_mode.h != 0)) { 4232 SDL_DisplayID displayID = SDL_GetDisplayForWindowPosition(window); 4233 bool include_high_density_modes = false; 4234 4235 if (window->requested_fullscreen_mode.pixel_density > 1.0f) { 4236 include_high_density_modes = true; 4237 } 4238 const bool found_match = SDL_GetClosestFullscreenDisplayMode(displayID, window->requested_fullscreen_mode.w, window->requested_fullscreen_mode.h, 4239 window->requested_fullscreen_mode.refresh_rate, include_high_density_modes, &window->current_fullscreen_mode); 4240 4241 // If a mode without matching dimensions was not found, just go to fullscreen desktop. 4242 if (!found_match || 4243 window->requested_fullscreen_mode.w != window->current_fullscreen_mode.w || 4244 window->requested_fullscreen_mode.h != window->current_fullscreen_mode.h) { 4245 SDL_zero(window->current_fullscreen_mode); 4246 } 4247 } else { 4248 SDL_zero(window->current_fullscreen_mode); 4249 } 4250 4251 if (SDL_WINDOW_FULLSCREEN_VISIBLE(window)) { 4252 SDL_UpdateFullscreenMode(window, SDL_FULLSCREEN_OP_UPDATE, true); 4253 } 4254 } 4255 4256 SDL_CheckWindowPixelSizeChanged(window); 4257} 4258 4259void SDL_OnWindowMoved(SDL_Window *window) 4260{ 4261 SDL_CheckWindowDisplayChanged(window); 4262} 4263 4264void SDL_OnWindowResized(SDL_Window *window) 4265{ 4266 SDL_CheckWindowDisplayChanged(window); 4267 SDL_CheckWindowPixelSizeChanged(window); 4268 SDL_CheckWindowSafeAreaChanged(window); 4269 4270 if ((window->flags & SDL_WINDOW_TRANSPARENT) && _this->UpdateWindowShape) { 4271 SDL_Surface *surface = (SDL_Surface *)SDL_GetPointerProperty(window->props, SDL_PROP_WINDOW_SHAPE_POINTER, NULL); 4272 if (surface) { 4273 _this->UpdateWindowShape(_this, window, surface); 4274 } 4275 } 4276} 4277 4278void SDL_CheckWindowPixelSizeChanged(SDL_Window *window) 4279{ 4280 int pixel_w = 0, pixel_h = 0; 4281 4282 SDL_GetWindowSizeInPixels(window, &pixel_w, &pixel_h); 4283 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED, pixel_w, pixel_h); 4284 4285 SDL_CheckWindowDisplayScaleChanged(window); 4286} 4287 4288void SDL_OnWindowPixelSizeChanged(SDL_Window *window) 4289{ 4290 window->surface_valid = false; 4291} 4292 4293void SDL_OnWindowLiveResizeUpdate(SDL_Window *window) 4294{ 4295 if (SDL_HasMainCallbacks()) { 4296 SDL_IterateMainCallbacks(false); 4297 } else { 4298 // Send an expose event so the application can redraw 4299 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_EXPOSED, 1, 0); 4300 } 4301 4302 SDL_PumpEventMaintenance(); 4303} 4304 4305static void SDL_CheckWindowSafeAreaChanged(SDL_Window *window) 4306{ 4307 SDL_Rect rect; 4308 4309 rect.x = window->safe_inset_left; 4310 rect.y = window->safe_inset_top; 4311 rect.w = window->w - (window->safe_inset_right + window->safe_inset_left); 4312 rect.h = window->h - (window->safe_inset_top + window->safe_inset_bottom); 4313 if (SDL_memcmp(&rect, &window->safe_rect, sizeof(rect)) != 0) { 4314 SDL_copyp(&window->safe_rect, &rect); 4315 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SAFE_AREA_CHANGED, 0, 0); 4316 } 4317} 4318 4319void SDL_SetWindowSafeAreaInsets(SDL_Window *window, int left, int right, int top, int bottom) 4320{ 4321 window->safe_inset_left = left; 4322 window->safe_inset_right = right; 4323 window->safe_inset_top = top; 4324 window->safe_inset_bottom = bottom; 4325 SDL_CheckWindowSafeAreaChanged(window); 4326} 4327 4328bool SDL_GetWindowSafeArea(SDL_Window *window, SDL_Rect *rect) 4329{ 4330 if (rect) { 4331 SDL_zerop(rect); 4332 } 4333 4334 CHECK_WINDOW_MAGIC(window, false); 4335 4336 if (rect) { 4337 if (SDL_RectEmpty(&window->safe_rect)) { 4338 rect->w = window->w; 4339 rect->h = window->h; 4340 } else { 4341 SDL_copyp(rect, &window->safe_rect); 4342 } 4343 } 4344 return true; 4345} 4346 4347void SDL_OnWindowMinimized(SDL_Window *window) 4348{ 4349 if (window->flags & SDL_WINDOW_FULLSCREEN) { 4350 SDL_UpdateFullscreenMode(window, SDL_FULLSCREEN_OP_LEAVE, false); 4351 } 4352} 4353 4354void SDL_OnWindowMaximized(SDL_Window *window) 4355{ 4356} 4357 4358void SDL_OnWindowRestored(SDL_Window *window) 4359{ 4360 /* 4361 * FIXME: Is this fine to just remove this, or should it be preserved just 4362 * for the fullscreen case? In principle it seems like just hiding/showing 4363 * windows shouldn't affect the stacking order; maybe the right fix is to 4364 * re-decouple OnWindowShown and OnWindowRestored. 4365 */ 4366 // SDL_RaiseWindow(window); 4367 4368 if (window->flags & SDL_WINDOW_FULLSCREEN) { 4369 SDL_UpdateFullscreenMode(window, SDL_FULLSCREEN_OP_ENTER, false); 4370 } 4371} 4372 4373void SDL_OnWindowEnter(SDL_Window *window) 4374{ 4375 if (_this->OnWindowEnter) { 4376 _this->OnWindowEnter(_this, window); 4377 } 4378} 4379 4380void SDL_OnWindowLeave(SDL_Window *window) 4381{ 4382} 4383 4384void SDL_OnWindowFocusGained(SDL_Window *window) 4385{ 4386 SDL_Mouse *mouse = SDL_GetMouse(); 4387 4388 if (mouse && mouse->relative_mode) { 4389 SDL_SetMouseFocus(window); 4390 } 4391 4392 SDL_UpdateWindowGrab(window); 4393} 4394 4395static bool SDL_ShouldMinimizeOnFocusLoss(SDL_Window *window) 4396{ 4397 const char *hint; 4398 4399 if (!(window->flags & SDL_WINDOW_FULLSCREEN) || window->is_destroying) { 4400 return false; 4401 } 4402 4403#if defined(SDL_PLATFORM_MACOS) && defined(SDL_VIDEO_DRIVER_COCOA) 4404 if (SDL_strcmp(_this->name, "cocoa") == 0) { // don't do this for X11, etc 4405 if (Cocoa_IsShowingModalDialog(window)) { 4406 return false; // modal system dialogs can live over fullscreen windows, don't minimize. 4407 } else if (Cocoa_IsWindowInFullscreenSpace(window)) { 4408 return false; 4409 } 4410 } 4411#endif 4412 4413#ifdef SDL_PLATFORM_ANDROID 4414 { 4415 extern bool Android_JNI_ShouldMinimizeOnFocusLoss(void); 4416 if (!Android_JNI_ShouldMinimizeOnFocusLoss()) { 4417 return false; 4418 } 4419 } 4420#endif 4421 4422 // Real fullscreen windows should minimize on focus loss so the desktop video mode is restored 4423 hint = SDL_GetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS); 4424 if (!hint || !*hint || SDL_strcasecmp(hint, "auto") == 0) { 4425 if (window->fullscreen_exclusive && !SDL_ModeSwitchingEmulated(_this)) { 4426 return true; 4427 } else { 4428 return false; 4429 } 4430 } 4431 return SDL_GetHintBoolean(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, false); 4432} 4433 4434void SDL_OnWindowFocusLost(SDL_Window *window) 4435{ 4436 SDL_UpdateWindowGrab(window); 4437 4438 if (SDL_ShouldMinimizeOnFocusLoss(window)) { 4439 SDL_MinimizeWindow(window); 4440 } 4441} 4442 4443SDL_Window *SDL_GetToplevelForKeyboardFocus(void) 4444{ 4445 SDL_Window *focus = SDL_GetKeyboardFocus(); 4446 4447 if (focus) { 4448 // Get the toplevel parent window. 4449 while (focus->parent) { 4450 focus = focus->parent; 4451 } 4452 } 4453 4454 return focus; 4455} 4456 4457bool SDL_AddWindowRenderer(SDL_Window *window, SDL_Renderer *renderer) 4458{ 4459 SDL_Renderer **renderers = (SDL_Renderer **)SDL_realloc(window->renderers, (window->num_renderers + 1) * sizeof(*renderers)); 4460 if (!renderers) { 4461 return false; 4462 } 4463 4464 window->renderers = renderers; 4465 window->renderers[window->num_renderers++] = renderer; 4466 return true; 4467} 4468 4469void SDL_RemoveWindowRenderer(SDL_Window *window, SDL_Renderer *renderer) 4470{ 4471 for (int i = 0; i < window->num_renderers; ++i) { 4472 if (window->renderers[i] == renderer) { 4473 if (i < (window->num_renderers - 1)) { 4474 SDL_memmove(&window->renderers[i], &window->renderers[i + 1], (window->num_renderers - i - 1) * sizeof(window->renderers[i])); 4475 } 4476 --window->num_renderers; 4477 break; 4478 } 4479 } 4480} 4481 4482void SDL_DestroyWindow(SDL_Window *window) 4483{ 4484 CHECK_WINDOW_MAGIC(window,); 4485 4486 window->is_destroying = true; 4487 4488 // Destroy any child windows of this window 4489 while (window->first_child) { 4490 SDL_DestroyWindow(window->first_child); 4491 } 4492 4493 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_DESTROYED, 0, 0); 4494 4495 SDL_DestroyWindowSurface(window); 4496 4497 SDL_Renderer *renderer = SDL_GetRenderer(window); 4498 if (renderer) { 4499 SDL_DestroyRendererWithoutFreeing(renderer); 4500 } 4501 4502 // Restore video mode, etc. 4503 SDL_UpdateFullscreenMode(window, SDL_FULLSCREEN_OP_LEAVE, true); 4504 if (!(window->flags & SDL_WINDOW_EXTERNAL)) { 4505 SDL_HideWindow(window); 4506 } 4507 4508 SDL_DestroyProperties(window->text_input_props); 4509 4510 SDL_DestroyWindowTexture(_this, window); 4511 SDL_DestroyProperties(window->props); 4512 4513 /* Clear the modal status, but don't unset the parent just yet, as it 4514 * may be needed later in the destruction process if a backend needs 4515 * to update the input focus. 4516 */ 4517 if (_this->SetWindowModal && (window->flags & SDL_WINDOW_MODAL)) { 4518 _this->SetWindowModal(_this, window, false); 4519 } 4520 4521 // Make sure the destroyed window isn't referenced by any display as a fullscreen window. 4522 for (int i = 0; i < _this->num_displays; ++i) { 4523 if (_this->displays[i]->fullscreen_window == window) { 4524 _this->displays[i]->fullscreen_window = NULL; 4525 } 4526 } 4527 4528 // Make sure this window no longer has focus 4529 if (SDL_GetKeyboardFocus() == window) { 4530 SDL_SetKeyboardFocus(NULL); 4531 } 4532 if ((window->flags & SDL_WINDOW_MOUSE_CAPTURE)) { 4533 SDL_UpdateMouseCapture(true); 4534 } 4535 if (SDL_GetMouseFocus() == window) { 4536 SDL_SetMouseFocus(NULL); 4537 } 4538 4539 // Make no context current if this is the current context window 4540 if (window->flags & SDL_WINDOW_OPENGL) { 4541 if (_this->current_glwin == window) { 4542 SDL_GL_MakeCurrent(window, NULL); 4543 } 4544 } 4545 4546 if (_this->DestroyWindow) { 4547 _this->DestroyWindow(_this, window); 4548 } 4549 4550 // Unload the graphics libraries after the window is destroyed, which may clean up EGL surfaces 4551 if (window->flags & SDL_WINDOW_OPENGL) { 4552 SDL_GL_UnloadLibrary(); 4553 } 4554 if (window->flags & SDL_WINDOW_VULKAN) { 4555 SDL_Vulkan_UnloadLibrary(); 4556 } 4557 4558 if (_this->grabbed_window == window) { 4559 _this->grabbed_window = NULL; // ungrabbing input. 4560 } 4561 4562 if (_this->current_glwin == window) { 4563 _this->current_glwin = NULL; 4564 } 4565 4566 SDL_CompareAndSwapAtomicPointer(&_this->wakeup_window, window, NULL); 4567 4568 // Now invalidate magic 4569 SDL_SetObjectValid(window, SDL_OBJECT_TYPE_WINDOW, false); 4570 4571 // Free memory associated with the window 4572 SDL_free(window->title); 4573 SDL_DestroySurface(window->icon); 4574 4575 // Unlink the window from its siblings. 4576 SDL_UpdateWindowHierarchy(window, NULL); 4577 4578 // Unlink the window from the global window list 4579 if (window->next) { 4580 window->next->prev = window->prev; 4581 } 4582 if (window->prev) { 4583 window->prev->next = window->next; 4584 } else { 4585 _this->windows = window->next; 4586 } 4587 4588 SDL_free(window->renderers); 4589 SDL_free(window); 4590 4591#ifdef SDL_VIDEO_DRIVER_UIKIT 4592 SDL_UpdateLifecycleObserver(); 4593#endif 4594} 4595 4596bool SDL_ScreenSaverEnabled(void) 4597{ 4598 if (!_this) { 4599 return true; 4600 } 4601 return !_this->suspend_screensaver; 4602} 4603 4604bool SDL_EnableScreenSaver(void) 4605{ 4606 if (!_this) { 4607 return SDL_UninitializedVideo(); 4608 } 4609 if (!_this->suspend_screensaver) { 4610 return true; 4611 } 4612 _this->suspend_screensaver = false; 4613 if (_this->SuspendScreenSaver) { 4614 return _this->SuspendScreenSaver(_this); 4615 } 4616 4617 return SDL_Unsupported(); 4618} 4619 4620bool SDL_DisableScreenSaver(void) 4621{ 4622 if (!_this) { 4623 return SDL_UninitializedVideo(); 4624 } 4625 if (_this->suspend_screensaver) { 4626 return true; 4627 } 4628 _this->suspend_screensaver = true; 4629 if (_this->SuspendScreenSaver) { 4630 return _this->SuspendScreenSaver(_this); 4631 } 4632 4633 return SDL_Unsupported(); 4634} 4635 4636void SDL_VideoQuit(void) 4637{ 4638 int i; 4639 4640 if (!_this) { 4641 return; 4642 } 4643 4644 _this->is_quitting = true; 4645 4646 // Halt event processing before doing anything else 4647#if 0 // This was moved to the end to fix a memory leak 4648 SDL_QuitPen(); 4649#endif 4650 SDL_QuitTouch(); 4651 SDL_QuitMouse(); 4652 SDL_QuitKeyboard(); 4653 SDL_QuitSubSystem(SDL_INIT_EVENTS); 4654 4655 SDL_EnableScreenSaver(); 4656 4657 // Clean up the system video 4658 while (_this->windows) { 4659 SDL_DestroyWindow(_this->windows); 4660 } 4661 4662 if (_this->gl_config.driver_loaded && _this->GL_UnloadLibrary) { 4663 _this->GL_UnloadLibrary(_this); 4664 _this->gl_config.driver_loaded = 0; 4665 } 4666 if (_this->vulkan_config.loader_loaded && _this->Vulkan_UnloadLibrary) { 4667 _this->Vulkan_UnloadLibrary(_this); 4668 _this->vulkan_config.loader_loaded = 0; 4669 } 4670 4671 _this->VideoQuit(_this); 4672 4673 for (i = _this->num_displays; i--; ) { 4674 SDL_VideoDisplay *display = _this->displays[i]; 4675 SDL_DelVideoDisplay(display->id, false); 4676 } 4677 4678 SDL_assert(_this->num_displays == 0); 4679 SDL_free(_this->displays); 4680 _this->displays = NULL; 4681 4682 SDL_CancelClipboardData(0); 4683 4684 if (_this->primary_selection_text) { 4685 SDL_free(_this->primary_selection_text); 4686 _this->primary_selection_text = NULL; 4687 } 4688 _this->free(_this); 4689 _this = NULL; 4690 4691 // This needs to happen after the video subsystem has removed pen data 4692 SDL_QuitPen(); 4693} 4694 4695bool SDL_GL_LoadLibrary(const char *path) 4696{ 4697 bool result; 4698 4699 if (!_this) { 4700 return SDL_UninitializedVideo(); 4701 } 4702 if (_this->gl_config.driver_loaded) { 4703 if (path && SDL_strcmp(path, _this->gl_config.driver_path) != 0) { 4704 return SDL_SetError("OpenGL library already loaded"); 4705 } 4706 result = true; 4707 } else { 4708 if (!_this->GL_LoadLibrary) { 4709 return SDL_DllNotSupported("OpenGL"); 4710 } 4711 result = _this->GL_LoadLibrary(_this, path); 4712 } 4713 if (result) { 4714 ++_this->gl_config.driver_loaded; 4715 } else { 4716 if (_this->GL_UnloadLibrary) { 4717 _this->GL_UnloadLibrary(_this); 4718 } 4719 } 4720 return result; 4721} 4722 4723SDL_FunctionPointer SDL_GL_GetProcAddress(const char *proc) 4724{ 4725 SDL_FunctionPointer func; 4726 4727 if (!_this) { 4728 SDL_UninitializedVideo(); 4729 return NULL; 4730 } 4731 func = NULL; 4732 if (_this->GL_GetProcAddress) { 4733 if (_this->gl_config.driver_loaded) { 4734 func = _this->GL_GetProcAddress(_this, proc); 4735 } else { 4736 SDL_SetError("No GL driver has been loaded"); 4737 } 4738 } else { 4739 SDL_SetError("No dynamic GL support in current SDL video driver (%s)", _this->name); 4740 } 4741 return func; 4742} 4743 4744SDL_FunctionPointer SDL_EGL_GetProcAddress(const char *proc) 4745{ 4746#ifdef SDL_VIDEO_OPENGL_EGL 4747 SDL_FunctionPointer func; 4748 4749 if (!_this) { 4750 SDL_UninitializedVideo(); 4751 return NULL; 4752 } 4753 func = NULL; 4754 4755 if (_this->egl_data) { 4756 func = SDL_EGL_GetProcAddressInternal(_this, proc); 4757 } else { 4758 SDL_SetError("No EGL library has been loaded"); 4759 } 4760 4761 return func; 4762#else 4763 SDL_SetError("SDL was not built with EGL support"); 4764 return NULL; 4765#endif 4766} 4767 4768void SDL_GL_UnloadLibrary(void) 4769{ 4770 if (!_this) { 4771 SDL_UninitializedVideo(); 4772 return; 4773 } 4774 if (_this->gl_config.driver_loaded > 0) { 4775 if (--_this->gl_config.driver_loaded > 0) { 4776 return; 4777 } 4778 if (_this->GL_UnloadLibrary) { 4779 _this->GL_UnloadLibrary(_this); 4780 } 4781 } 4782} 4783 4784#if defined(SDL_VIDEO_OPENGL) || defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2) 4785typedef GLenum (APIENTRY* PFNGLGETERRORPROC) (void); 4786typedef void (APIENTRY* PFNGLGETINTEGERVPROC) (GLenum pname, GLint *params); 4787typedef const GLubyte *(APIENTRY* PFNGLGETSTRINGPROC) (GLenum name); 4788typedef void (APIENTRY* PFNGLENABLEPROC) (GLenum cap); 4789#ifndef SDL_VIDEO_OPENGL 4790typedef const GLubyte *(APIENTRY* PFNGLGETSTRINGIPROC) (GLenum name, GLuint index); 4791#endif 4792 4793static SDL_INLINE bool isAtLeastGL3(const char *verstr) 4794{ 4795 return verstr && (SDL_atoi(verstr) >= 3); 4796} 4797#endif // SDL_VIDEO_OPENGL || SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2 4798 4799bool SDL_GL_ExtensionSupported(const char *extension) 4800{ 4801#if defined(SDL_VIDEO_OPENGL) || defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2) 4802 PFNGLGETSTRINGPROC glGetStringFunc; 4803 const char *extensions; 4804 const char *start; 4805 const char *where, *terminator; 4806 4807 // Extension names should not have spaces. 4808 where = SDL_strchr(extension, ' '); 4809 if (where || *extension == '\0') { 4810 return false; 4811 } 4812 // See if there's a hint or environment variable override 4813 start = SDL_GetHint(extension); 4814 if (start && *start == '0') { 4815 return false; 4816 } 4817 4818 // Lookup the available extensions 4819 4820 glGetStringFunc = (PFNGLGETSTRINGPROC)SDL_GL_GetProcAddress("glGetString"); 4821 if (!glGetStringFunc) { 4822 return false; 4823 } 4824 4825 if (isAtLeastGL3((const char *)glGetStringFunc(GL_VERSION))) { 4826 PFNGLGETSTRINGIPROC glGetStringiFunc; 4827 PFNGLGETINTEGERVPROC glGetIntegervFunc; 4828 GLint num_exts = 0; 4829 GLint i; 4830 4831 glGetStringiFunc = (PFNGLGETSTRINGIPROC)SDL_GL_GetProcAddress("glGetStringi"); 4832 glGetIntegervFunc = (PFNGLGETINTEGERVPROC)SDL_GL_GetProcAddress("glGetIntegerv"); 4833 if ((!glGetStringiFunc) || (!glGetIntegervFunc)) { 4834 return false; 4835 } 4836 4837#ifndef GL_NUM_EXTENSIONS 4838#define GL_NUM_EXTENSIONS 0x821D 4839#endif 4840 glGetIntegervFunc(GL_NUM_EXTENSIONS, &num_exts); 4841 for (i = 0; i < num_exts; i++) { 4842 const char *thisext = (const char *)glGetStringiFunc(GL_EXTENSIONS, i); 4843 if (SDL_strcmp(thisext, extension) == 0) { 4844 return true; 4845 } 4846 } 4847 4848 return false; 4849 } 4850 4851 // Try the old way with glGetString(GL_EXTENSIONS) ... 4852 4853 extensions = (const char *)glGetStringFunc(GL_EXTENSIONS); 4854 if (!extensions) { 4855 return false; 4856 } 4857 /* 4858 * It takes a bit of care to be fool-proof about parsing the OpenGL 4859 * extensions string. Don't be fooled by sub-strings, etc. 4860 */ 4861 4862 start = extensions; 4863 4864 for (;;) { 4865 where = SDL_strstr(start, extension); 4866 if (!where) { 4867 break; 4868 } 4869 4870 terminator = where + SDL_strlen(extension); 4871 if (where == extensions || *(where - 1) == ' ') { 4872 if (*terminator == ' ' || *terminator == '\0') { 4873 return true; 4874 } 4875 } 4876 4877 start = terminator; 4878 } 4879 return false; 4880#else 4881 return false; 4882#endif 4883} 4884 4885/* Deduce supported ES profile versions from the supported 4886 ARB_ES*_compatibility extensions. There is no direct query. 4887 4888 This is normally only called when the OpenGL driver supports 4889 {GLX,WGL}_EXT_create_context_es2_profile. 4890 */ 4891void SDL_GL_DeduceMaxSupportedESProfile(int *major, int *minor) 4892{ 4893// THIS REQUIRES AN EXISTING GL CONTEXT THAT HAS BEEN MADE CURRENT. 4894// Please refer to https://bugzilla.libsdl.org/show_bug.cgi?id=3725 for discussion. 4895#if defined(SDL_VIDEO_OPENGL) || defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2) 4896 /* XXX This is fragile; it will break in the event of release of 4897 * new versions of OpenGL ES. 4898 */ 4899 if (SDL_GL_ExtensionSupported("GL_ARB_ES3_2_compatibility")) { 4900 *major = 3; 4901 *minor = 2; 4902 } else if (SDL_GL_ExtensionSupported("GL_ARB_ES3_1_compatibility")) { 4903 *major = 3; 4904 *minor = 1; 4905 } else if (SDL_GL_ExtensionSupported("GL_ARB_ES3_compatibility")) { 4906 *major = 3; 4907 *minor = 0; 4908 } else { 4909 *major = 2; 4910 *minor = 0; 4911 } 4912#endif 4913} 4914 4915void SDL_EGL_SetAttributeCallbacks(SDL_EGLAttribArrayCallback platformAttribCallback, 4916 SDL_EGLIntArrayCallback surfaceAttribCallback, 4917 SDL_EGLIntArrayCallback contextAttribCallback, 4918 void *userdata) 4919{ 4920 if (!_this) { 4921 return; 4922 } 4923 _this->egl_platformattrib_callback = platformAttribCallback; 4924 _this->egl_surfaceattrib_callback = surfaceAttribCallback; 4925 _this->egl_contextattrib_callback = contextAttribCallback; 4926 _this->egl_attrib_callback_userdata = userdata; 4927} 4928 4929void SDL_GL_ResetAttributes(void) 4930{ 4931 if (!_this) { 4932 return; 4933 } 4934 4935 _this->egl_platformattrib_callback = NULL; 4936 _this->egl_surfaceattrib_callback = NULL; 4937 _this->egl_contextattrib_callback = NULL; 4938 _this->egl_attrib_callback_userdata = NULL; 4939 4940 _this->gl_config.red_size = 8; 4941 _this->gl_config.green_size = 8; 4942 _this->gl_config.blue_size = 8; 4943 _this->gl_config.alpha_size = 8; 4944 _this->gl_config.buffer_size = 0; 4945 _this->gl_config.depth_size = 16; 4946 _this->gl_config.stencil_size = 0; 4947 _this->gl_config.double_buffer = 1; 4948 _this->gl_config.accum_red_size = 0; 4949 _this->gl_config.accum_green_size = 0; 4950 _this->gl_config.accum_blue_size = 0; 4951 _this->gl_config.accum_alpha_size = 0; 4952 _this->gl_config.stereo = 0; 4953 _this->gl_config.multisamplebuffers = 0; 4954 _this->gl_config.multisamplesamples = 0; 4955 _this->gl_config.floatbuffers = 0; 4956 _this->gl_config.retained_backing = 1; 4957 _this->gl_config.accelerated = -1; // accelerated or not, both are fine 4958 4959#ifdef SDL_VIDEO_OPENGL 4960 _this->gl_config.major_version = 2; 4961 _this->gl_config.minor_version = 1; 4962 _this->gl_config.profile_mask = 0; 4963#elif defined(SDL_VIDEO_OPENGL_ES2) 4964 _this->gl_config.major_version = 2; 4965 _this->gl_config.minor_version = 0; 4966 _this->gl_config.profile_mask = SDL_GL_CONTEXT_PROFILE_ES; 4967#elif defined(SDL_VIDEO_OPENGL_ES) 4968 _this->gl_config.major_version = 1; 4969 _this->gl_config.minor_version = 1; 4970 _this->gl_config.profile_mask = SDL_GL_CONTEXT_PROFILE_ES; 4971#endif 4972 4973 _this->gl_config.flags = 0; 4974 _this->gl_config.framebuffer_srgb_capable = 0; 4975 _this->gl_config.no_error = 0; 4976 _this->gl_config.release_behavior = SDL_GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH; 4977 _this->gl_config.reset_notification = SDL_GL_CONTEXT_RESET_NO_NOTIFICATION; 4978 4979 _this->gl_config.share_with_current_context = 0; 4980 4981 _this->gl_config.egl_platform = 0; 4982 4983 if (_this->GL_SetDefaultProfileConfig) { 4984 _this->GL_SetDefaultProfileConfig(_this); 4985 } 4986} 4987 4988bool SDL_GL_SetAttribute(SDL_GLAttr attr, int value) 4989{ 4990#if defined(SDL_VIDEO_OPENGL) || defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2) 4991 bool result; 4992 4993 if (!_this) { 4994 return SDL_UninitializedVideo(); 4995 } 4996 result = true; 4997 switch (attr) { 4998 case SDL_GL_RED_SIZE: 4999 _this->gl_config.red_size = value; 5000 break; 5001 case SDL_GL_GREEN_SIZE: 5002 _this->gl_config.green_size = value; 5003 break; 5004 case SDL_GL_BLUE_SIZE: 5005 _this->gl_config.blue_size = value; 5006 break; 5007 case SDL_GL_ALPHA_SIZE: 5008 _this->gl_config.alpha_size = value; 5009 break; 5010 case SDL_GL_DOUBLEBUFFER: 5011 _this->gl_config.double_buffer = value; 5012 break; 5013 case SDL_GL_BUFFER_SIZE: 5014 _this->gl_config.buffer_size = value; 5015 break; 5016 case SDL_GL_DEPTH_SIZE: 5017 _this->gl_config.depth_size = value; 5018 break; 5019 case SDL_GL_STENCIL_SIZE: 5020 _this->gl_config.stencil_size = value; 5021 break; 5022 case SDL_GL_ACCUM_RED_SIZE: 5023 _this->gl_config.accum_red_size = value; 5024 break; 5025 case SDL_GL_ACCUM_GREEN_SIZE: 5026 _this->gl_config.accum_green_size = value; 5027 break; 5028 case SDL_GL_ACCUM_BLUE_SIZE: 5029 _this->gl_config.accum_blue_size = value; 5030 break; 5031 case SDL_GL_ACCUM_ALPHA_SIZE: 5032 _this->gl_config.accum_alpha_size = value; 5033 break; 5034 case SDL_GL_STEREO: 5035 _this->gl_config.stereo = value; 5036 break; 5037 case SDL_GL_MULTISAMPLEBUFFERS: 5038 _this->gl_config.multisamplebuffers = value; 5039 break; 5040 case SDL_GL_MULTISAMPLESAMPLES: 5041 _this->gl_config.multisamplesamples = value; 5042 break; 5043 case SDL_GL_FLOATBUFFERS: 5044 _this->gl_config.floatbuffers = value; 5045 break; 5046 case SDL_GL_ACCELERATED_VISUAL: 5047 _this->gl_config.accelerated = value; 5048 break; 5049 case SDL_GL_RETAINED_BACKING: 5050 _this->gl_config.retained_backing = value; 5051 break; 5052 case SDL_GL_CONTEXT_MAJOR_VERSION: 5053 _this->gl_config.major_version = value; 5054 break; 5055 case SDL_GL_CONTEXT_MINOR_VERSION: 5056 _this->gl_config.minor_version = value; 5057 break; 5058 case SDL_GL_CONTEXT_FLAGS: 5059 if (value & ~(SDL_GL_CONTEXT_DEBUG_FLAG | 5060 SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG | 5061 SDL_GL_CONTEXT_ROBUST_ACCESS_FLAG | 5062 SDL_GL_CONTEXT_RESET_ISOLATION_FLAG)) { 5063 result = SDL_SetError("Unknown OpenGL context flag %d", value); 5064 break; 5065 } 5066 _this->gl_config.flags = value; 5067 break; 5068 case SDL_GL_CONTEXT_PROFILE_MASK: 5069 if (value != 0 && 5070 value != SDL_GL_CONTEXT_PROFILE_CORE && 5071 value != SDL_GL_CONTEXT_PROFILE_COMPATIBILITY && 5072 value != SDL_GL_CONTEXT_PROFILE_ES) { 5073 result = SDL_SetError("Unknown OpenGL context profile %d", value); 5074 break; 5075 } 5076 _this->gl_config.profile_mask = value; 5077 break; 5078 case SDL_GL_SHARE_WITH_CURRENT_CONTEXT: 5079 _this->gl_config.share_with_current_context = value; 5080 break; 5081 case SDL_GL_FRAMEBUFFER_SRGB_CAPABLE: 5082 _this->gl_config.framebuffer_srgb_capable = value; 5083 break; 5084 case SDL_GL_CONTEXT_RELEASE_BEHAVIOR: 5085 _this->gl_config.release_behavior = value; 5086 break; 5087 case SDL_GL_CONTEXT_RESET_NOTIFICATION: 5088 _this->gl_config.reset_notification = value; 5089 break; 5090 case SDL_GL_CONTEXT_NO_ERROR: 5091 _this->gl_config.no_error = value; 5092 break; 5093 case SDL_GL_EGL_PLATFORM: 5094 _this->gl_config.egl_platform = value; 5095 break; 5096 default: 5097 result = SDL_SetError("Unknown OpenGL attribute"); 5098 break; 5099 } 5100 return result; 5101#else 5102 return SDL_Unsupported(); 5103#endif // SDL_VIDEO_OPENGL 5104} 5105 5106bool SDL_GL_GetAttribute(SDL_GLAttr attr, int *value) 5107{ 5108#if defined(SDL_VIDEO_OPENGL) || defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2) 5109 PFNGLGETERRORPROC glGetErrorFunc; 5110 GLenum attrib = 0; 5111 GLenum error = 0; 5112 5113 /* 5114 * Some queries in Core Profile desktop OpenGL 3+ contexts require 5115 * glGetFramebufferAttachmentParameteriv instead of glGetIntegerv. Note that 5116 * the enums we use for the former function don't exist in OpenGL ES 2, and 5117 * the function itself doesn't exist prior to OpenGL 3 and OpenGL ES 2. 5118 */ 5119#ifdef SDL_VIDEO_OPENGL 5120 PFNGLGETSTRINGPROC glGetStringFunc; 5121 PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetFramebufferAttachmentParameterivFunc; 5122 GLenum attachment = GL_BACK_LEFT; 5123 GLenum attachmentattrib = 0; 5124#endif 5125 5126 CHECK_PARAM(!value) { 5127 return SDL_InvalidParamError("value"); 5128 } 5129 5130 // Clear value in any case 5131 *value = 0; 5132 5133 if (!_this) { 5134 return SDL_UninitializedVideo(); 5135 } 5136 5137 switch (attr) { 5138 case SDL_GL_RED_SIZE: 5139#ifdef SDL_VIDEO_OPENGL 5140 attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE; 5141#endif 5142 attrib = GL_RED_BITS; 5143 break; 5144 case SDL_GL_BLUE_SIZE: 5145#ifdef SDL_VIDEO_OPENGL 5146 attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE; 5147#endif 5148 attrib = GL_BLUE_BITS; 5149 break; 5150 case SDL_GL_GREEN_SIZE: 5151#ifdef SDL_VIDEO_OPENGL 5152 attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE; 5153#endif 5154 attrib = GL_GREEN_BITS; 5155 break; 5156 case SDL_GL_ALPHA_SIZE: 5157#ifdef SDL_VIDEO_OPENGL 5158 attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE; 5159#endif 5160 attrib = GL_ALPHA_BITS; 5161 break; 5162 case SDL_GL_DOUBLEBUFFER: 5163#ifdef SDL_VIDEO_OPENGL 5164 attrib = GL_DOUBLEBUFFER; 5165 break; 5166#else 5167 // OpenGL ES 1.0 and above specifications have EGL_SINGLE_BUFFER 5168 // parameter which switches double buffer to single buffer. OpenGL ES 5169 // SDL driver must set proper value after initialization 5170 *value = _this->gl_config.double_buffer; 5171 return true; 5172#endif 5173 case SDL_GL_DEPTH_SIZE: 5174#ifdef SDL_VIDEO_OPENGL 5175 attachment = GL_DEPTH; 5176 attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE; 5177#endif 5178 attrib = GL_DEPTH_BITS; 5179 break; 5180 case SDL_GL_STENCIL_SIZE: 5181#ifdef SDL_VIDEO_OPENGL 5182 attachment = GL_STENCIL; 5183 attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE; 5184#endif 5185 attrib = GL_STENCIL_BITS; 5186 break; 5187#ifdef SDL_VIDEO_OPENGL 5188 case SDL_GL_ACCUM_RED_SIZE: 5189 attrib = GL_ACCUM_RED_BITS; 5190 break; 5191 case SDL_GL_ACCUM_GREEN_SIZE: 5192 attrib = GL_ACCUM_GREEN_BITS; 5193 break; 5194 case SDL_GL_ACCUM_BLUE_SIZE: 5195 attrib = GL_ACCUM_BLUE_BITS; 5196 break; 5197 case SDL_GL_ACCUM_ALPHA_SIZE: 5198 attrib = GL_ACCUM_ALPHA_BITS; 5199 break; 5200 case SDL_GL_STEREO: 5201 attrib = GL_STEREO; 5202 break; 5203#else 5204 case SDL_GL_ACCUM_RED_SIZE: 5205 case SDL_GL_ACCUM_GREEN_SIZE: 5206 case SDL_GL_ACCUM_BLUE_SIZE: 5207 case SDL_GL_ACCUM_ALPHA_SIZE: 5208 case SDL_GL_STEREO: 5209 // none of these are supported in OpenGL ES 5210 *value = 0; 5211 return true; 5212#endif 5213 case SDL_GL_MULTISAMPLEBUFFERS: 5214 attrib = GL_SAMPLE_BUFFERS; 5215 break; 5216 case SDL_GL_MULTISAMPLESAMPLES: 5217 attrib = GL_SAMPLES; 5218 break; 5219 case SDL_GL_CONTEXT_RELEASE_BEHAVIOR: 5220 attrib = GL_CONTEXT_RELEASE_BEHAVIOR; 5221 break; 5222 case SDL_GL_BUFFER_SIZE: 5223 { 5224 int rsize = 0, gsize = 0, bsize = 0, asize = 0; 5225 5226 // There doesn't seem to be a single flag in OpenGL for this! 5227 if (!SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &rsize)) { 5228 return false; 5229 } 5230 if (!SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &gsize)) { 5231 return false; 5232 } 5233 if (!SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &bsize)) { 5234 return false; 5235 } 5236 if (!SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &asize)) { 5237 return false; 5238 } 5239 5240 *value = rsize + gsize + bsize + asize; 5241 return true; 5242 } 5243 case SDL_GL_ACCELERATED_VISUAL: 5244 { 5245 // FIXME: How do we get this information? 5246 *value = (_this->gl_config.accelerated != 0); 5247 return true; 5248 } 5249 case SDL_GL_RETAINED_BACKING: 5250 { 5251 *value = _this->gl_config.retained_backing; 5252 return true; 5253 } 5254 case SDL_GL_CONTEXT_MAJOR_VERSION: 5255 { 5256 *value = _this->gl_config.major_version; 5257 return true; 5258 } 5259 case SDL_GL_CONTEXT_MINOR_VERSION: 5260 { 5261 *value = _this->gl_config.minor_version; 5262 return true; 5263 } 5264 case SDL_GL_CONTEXT_FLAGS: 5265 { 5266 *value = _this->gl_config.flags; 5267 return true; 5268 } 5269 case SDL_GL_CONTEXT_PROFILE_MASK: 5270 { 5271 *value = _this->gl_config.profile_mask; 5272 return true; 5273 } 5274 case SDL_GL_SHARE_WITH_CURRENT_CONTEXT: 5275 { 5276 *value = _this->gl_config.share_with_current_context; 5277 return true; 5278 } 5279 case SDL_GL_FRAMEBUFFER_SRGB_CAPABLE: 5280 { 5281 *value = _this->gl_config.framebuffer_srgb_capable; 5282 return true; 5283 } 5284 case SDL_GL_CONTEXT_NO_ERROR: 5285 { 5286 *value = _this->gl_config.no_error; 5287 return true; 5288 } 5289 case SDL_GL_EGL_PLATFORM: 5290 { 5291 *value = _this->gl_config.egl_platform; 5292 return true; 5293 } 5294 case SDL_GL_FLOATBUFFERS: 5295 { 5296 if (_this->gl_config.HAS_GL_ARB_color_buffer_float) { 5297 attrib = GL_RGBA_FLOAT_MODE_ARB; 5298 break; 5299 } else { 5300 return 0; 5301 } 5302 } 5303 default: 5304 return SDL_SetError("Unknown OpenGL attribute"); 5305 } 5306 5307#ifdef SDL_VIDEO_OPENGL 5308 glGetStringFunc = (PFNGLGETSTRINGPROC)SDL_GL_GetProcAddress("glGetString"); 5309 if (!glGetStringFunc) { 5310 return false; 5311 } 5312 5313 if (attachmentattrib && isAtLeastGL3((const char *)glGetStringFunc(GL_VERSION))) { 5314 // glGetFramebufferAttachmentParameteriv needs to operate on the window framebuffer for this, so bind FBO 0 if necessary. 5315 GLint current_fbo = 0; 5316 PFNGLGETINTEGERVPROC glGetIntegervFunc = (PFNGLGETINTEGERVPROC) SDL_GL_GetProcAddress("glGetIntegerv"); 5317 PFNGLBINDFRAMEBUFFERPROC glBindFramebufferFunc = (PFNGLBINDFRAMEBUFFERPROC)SDL_GL_GetProcAddress("glBindFramebuffer"); 5318 if (glGetIntegervFunc && glBindFramebufferFunc) { 5319 glGetIntegervFunc(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo); 5320 } 5321 5322 glGetFramebufferAttachmentParameterivFunc = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)SDL_GL_GetProcAddress("glGetFramebufferAttachmentParameteriv"); 5323 if (glGetFramebufferAttachmentParameterivFunc) { 5324 if (glBindFramebufferFunc && (current_fbo != 0)) { 5325 glBindFramebufferFunc(GL_DRAW_FRAMEBUFFER, 0); 5326 } 5327 // glGetFramebufferAttachmentParameterivFunc may cause GL_INVALID_OPERATION when querying depth/stencil size if the 5328 // bits is 0. From the GL docs: 5329 // If the value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE, then either no framebuffer is bound to target; 5330 // or a default framebuffer is queried, attachment is GL_DEPTH or GL_STENCIL, and the number of depth or stencil bits, 5331 // respectively, is zero. In this case querying pname GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME will return zero, and all 5332 // other queries will generate an error. 5333 GLint fbo_type = GL_FRAMEBUFFER_DEFAULT; 5334 if (attachment == GL_DEPTH || attachment == GL_STENCIL) { 5335 glGetFramebufferAttachmentParameterivFunc(GL_FRAMEBUFFER, attachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &fbo_type); 5336 } 5337 if (fbo_type != GL_NONE) { 5338 glGetFramebufferAttachmentParameterivFunc(GL_FRAMEBUFFER, attachment, attachmentattrib, (GLint *)value); 5339 } else { 5340 *value = 0; 5341 } 5342 if (glBindFramebufferFunc && (current_fbo != 0)) { 5343 glBindFramebufferFunc(GL_DRAW_FRAMEBUFFER, current_fbo); 5344 } 5345 } else { 5346 return false; 5347 } 5348 } else 5349#endif 5350 { 5351 PFNGLGETINTEGERVPROC glGetIntegervFunc = (PFNGLGETINTEGERVPROC)SDL_GL_GetProcAddress("glGetIntegerv"); 5352 if (glGetIntegervFunc) { 5353 glGetIntegervFunc(attrib, (GLint *)value); 5354 } else { 5355 return false; 5356 } 5357 } 5358 5359 glGetErrorFunc = (PFNGLGETERRORPROC)SDL_GL_GetProcAddress("glGetError"); 5360 if (!glGetErrorFunc) { 5361 return false; 5362 } 5363 5364 error = glGetErrorFunc(); 5365 if (error != GL_NO_ERROR) { 5366 if (error == GL_INVALID_ENUM) { 5367 return SDL_SetError("OpenGL error: GL_INVALID_ENUM"); 5368 } else if (error == GL_INVALID_VALUE) { 5369 return SDL_SetError("OpenGL error: GL_INVALID_VALUE"); 5370 } 5371 return SDL_SetError("OpenGL error: %08X", error); 5372 } 5373 5374 // convert GL_CONTEXT_RELEASE_BEHAVIOR values back to SDL_GL_CONTEXT_RELEASE_BEHAVIOR values 5375 if (attr == SDL_GL_CONTEXT_RELEASE_BEHAVIOR) { 5376 *value = (*value == GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH) ? SDL_GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH : SDL_GL_CONTEXT_RELEASE_BEHAVIOR_NONE; 5377 } 5378 5379 return true; 5380#else 5381 return SDL_Unsupported(); 5382#endif // SDL_VIDEO_OPENGL 5383} 5384 5385#define NOT_AN_OPENGL_WINDOW "The specified window isn't an OpenGL window" 5386 5387SDL_GLContext SDL_GL_CreateContext(SDL_Window *window) 5388{ 5389 SDL_GLContext ctx = NULL; 5390 CHECK_WINDOW_MAGIC(window, NULL); 5391 5392 if (!(window->flags & SDL_WINDOW_OPENGL)) { 5393 SDL_SetError(NOT_AN_OPENGL_WINDOW); 5394 return NULL; 5395 } 5396 5397#if defined(SDL_VIDEO_OPENGL) || defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2) 5398 int srgb_requested = -1; 5399 const char *srgbhint = SDL_GetHint(SDL_HINT_OPENGL_FORCE_SRGB_FRAMEBUFFER); 5400 if (srgbhint && *srgbhint) { 5401 srgb_requested = SDL_GetStringBoolean(srgbhint, false) ? 1 : 0; 5402 } 5403 5404#endif 5405 5406 ctx = _this->GL_CreateContext(_this, window); 5407 5408 // Creating a context is assumed to make it current in the SDL driver. 5409 if (ctx) { 5410 _this->current_glwin = window; 5411 _this->current_glctx = ctx; 5412 SDL_SetTLS(&_this->current_glwin_tls, window, NULL); 5413 SDL_SetTLS(&_this->current_glctx_tls, ctx, NULL); 5414 } 5415 5416#if defined(SDL_VIDEO_OPENGL) || defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2) 5417 // try to force the window framebuffer to the requested sRGB state. 5418 if (srgb_requested != -1) { 5419 PFNGLENABLEPROC glToggleFunc = (PFNGLENABLEPROC) SDL_GL_GetProcAddress(srgb_requested ? "glEnable" : "glDisable"); 5420 PFNGLGETSTRINGPROC glGetStringFunc = (PFNGLGETSTRINGPROC)SDL_GL_GetProcAddress("glGetString"); 5421 if (glToggleFunc && glGetStringFunc) { 5422 bool supported = false; 5423 if (_this->gl_config.profile_mask & SDL_GL_CONTEXT_PROFILE_ES) { 5424 supported = SDL_GL_ExtensionSupported("GL_EXT_sRGB_write_control"); // GL_FRAMEBUFFER_SRGB is not core in any GLES version at the moment. 5425 } else { 5426 supported = isAtLeastGL3((const char *)glGetStringFunc(GL_VERSION)) || // no extensions needed in OpenGL 3+. 5427 SDL_GL_ExtensionSupported("GL_EXT_framebuffer_sRGB") || 5428 SDL_GL_ExtensionSupported("GL_ARB_framebuffer_sRGB"); 5429 } 5430 5431 if (supported) { 5432 glToggleFunc(GL_FRAMEBUFFER_SRGB); 5433 } 5434 } 5435 } 5436#endif 5437 5438 return ctx; 5439} 5440 5441bool SDL_GL_MakeCurrent(SDL_Window *window, SDL_GLContext context) 5442{ 5443 bool result; 5444 5445 if (!_this) { 5446 return SDL_UninitializedVideo(); 5447 } 5448 5449 if (window == SDL_GL_GetCurrentWindow() && 5450 context == SDL_GL_GetCurrentContext()) { 5451 // We're already current. 5452 return true; 5453 } 5454 5455 if (!context) { 5456 window = NULL; 5457 } else if (window) { 5458 CHECK_WINDOW_MAGIC(window, false); 5459 5460 if (!(window->flags & SDL_WINDOW_OPENGL)) { 5461 return SDL_SetError(NOT_AN_OPENGL_WINDOW); 5462 } 5463 } else if (!_this->gl_allow_no_surface) { 5464 return SDL_SetError("Use of OpenGL without a window is not supported on this platform"); 5465 } 5466 5467 result = _this->GL_MakeCurrent(_this, window, context); 5468 if (result) { 5469 _this->current_glwin = window; 5470 _this->current_glctx = context; 5471 SDL_SetTLS(&_this->current_glwin_tls, window, NULL); 5472 SDL_SetTLS(&_this->current_glctx_tls, context, NULL); 5473 } 5474 return result; 5475} 5476 5477SDL_Window *SDL_GL_GetCurrentWindow(void) 5478{ 5479 if (!_this) { 5480 SDL_UninitializedVideo(); 5481 return NULL; 5482 } 5483 return (SDL_Window *)SDL_GetTLS(&_this->current_glwin_tls); 5484} 5485 5486SDL_GLContext SDL_GL_GetCurrentContext(void) 5487{ 5488 if (!_this) { 5489 SDL_UninitializedVideo(); 5490 return NULL; 5491 } 5492 return (SDL_GLContext)SDL_GetTLS(&_this->current_glctx_tls); 5493} 5494 5495SDL_EGLDisplay SDL_EGL_GetCurrentDisplay(void) 5496{ 5497#ifdef SDL_VIDEO_OPENGL_EGL 5498 if (!_this) { 5499 SDL_UninitializedVideo(); 5500 return EGL_NO_DISPLAY; 5501 } 5502 if (!_this->egl_data) { 5503 SDL_SetError("There is no current EGL display"); 5504 return EGL_NO_DISPLAY; 5505 } 5506 return _this->egl_data->egl_display; 5507#else 5508 SDL_SetError("SDL was not built with EGL support"); 5509 return NULL; 5510#endif 5511} 5512 5513SDL_EGLConfig SDL_EGL_GetCurrentConfig(void) 5514{ 5515#ifdef SDL_VIDEO_OPENGL_EGL 5516 if (!_this) { 5517 SDL_UninitializedVideo(); 5518 return NULL; 5519 } 5520 if (!_this->egl_data) { 5521 SDL_SetError("There is no current EGL display"); 5522 return NULL; 5523 } 5524 return _this->egl_data->egl_config; 5525#else 5526 SDL_SetError("SDL was not built with EGL support"); 5527 return NULL; 5528#endif 5529} 5530 5531SDL_EGLConfig SDL_EGL_GetWindowSurface(SDL_Window *window) 5532{ 5533#ifdef SDL_VIDEO_OPENGL_EGL 5534 if (!_this) { 5535 SDL_UninitializedVideo(); 5536 return NULL; 5537 } 5538 if (!_this->egl_data) { 5539 SDL_SetError("There is no current EGL display"); 5540 return NULL; 5541 } 5542 if (_this->GL_GetEGLSurface) { 5543 return _this->GL_GetEGLSurface(_this, window); 5544 } 5545 return NULL; 5546#else 5547 SDL_SetError("SDL was not built with EGL support"); 5548 return NULL; 5549#endif 5550} 5551 5552bool SDL_GL_SetSwapInterval(int interval) 5553{ 5554 if (!_this) { 5555 return SDL_UninitializedVideo(); 5556 } else if (SDL_GL_GetCurrentContext() == NULL) { 5557 return SDL_SetError("No OpenGL context has been made current"); 5558 } else if (_this->GL_SetSwapInterval) { 5559 return _this->GL_SetSwapInterval(_this, interval); 5560 } else { 5561 return SDL_SetError("Setting the swap interval is not supported"); 5562 } 5563} 5564 5565bool SDL_GL_GetSwapInterval(int *interval) 5566{ 5567 CHECK_PARAM(!interval) { 5568 return SDL_InvalidParamError("interval"); 5569 } 5570 5571 *interval = 0; 5572 5573 if (!_this) { 5574 return SDL_SetError("no video driver"); 5575 } else if (SDL_GL_GetCurrentContext() == NULL) { 5576 return SDL_SetError("no current context"); 5577 } else if (_this->GL_GetSwapInterval) { 5578 return _this->GL_GetSwapInterval(_this, interval); 5579 } else { 5580 return SDL_SetError("not implemented"); 5581 } 5582} 5583 5584bool SDL_GL_SwapWindow(SDL_Window *window) 5585{ 5586 CHECK_WINDOW_MAGIC(window, false); 5587 5588 if (!(window->flags & SDL_WINDOW_OPENGL)) { 5589 return SDL_SetError(NOT_AN_OPENGL_WINDOW); 5590 } 5591 5592 if (SDL_GL_GetCurrentWindow() != window) { 5593 return SDL_SetError("The specified window has not been made current"); 5594 } 5595 5596 return _this->GL_SwapWindow(_this, window); 5597} 5598 5599bool SDL_GL_DestroyContext(SDL_GLContext context) 5600{ 5601 CHECK_PARAM(!_this) { 5602 return SDL_UninitializedVideo(); 5603 } 5604 CHECK_PARAM(!context) { 5605 return SDL_InvalidParamError("context"); 5606 } 5607 5608 if (SDL_GL_GetCurrentContext() == context) { 5609 SDL_GL_MakeCurrent(NULL, NULL); 5610 } 5611 5612 return _this->GL_DestroyContext(_this, context); 5613} 5614 5615#if 0 // FIXME 5616/* 5617 * Utility function used by SDL_WM_SetIcon(); flags & 1 for color key, flags 5618 * & 2 for alpha channel. 5619 */ 5620static void CreateMaskFromColorKeyOrAlpha(SDL_Surface *icon, Uint8 *mask, int flags) 5621{ 5622 int x, y; 5623 Uint32 colorkey; 5624#define SET_MASKBIT(icon, x, y, mask) \ 5625 mask[(y * ((icon->w + 7) / 8)) + (x / 8)] &= ~(0x01 << (7 - (x % 8))) 5626 5627 colorkey = icon->format->colorkey; 5628 switch (SDL_BYTESPERPIXEL(icon->format)) { 5629 case 1: 5630 { 5631 Uint8 *pixels; 5632 for (y = 0; y < icon->h; ++y) { 5633 pixels = (Uint8 *) icon->pixels + y * icon->pitch; 5634 for (x = 0; x < icon->w; ++x) { 5635 if (*pixels++ == colorkey) { 5636 SET_MASKBIT(icon, x, y, mask); 5637 } 5638 } 5639 } 5640 } 5641 break; 5642 5643 case 2: 5644 { 5645 Uint16 *pixels; 5646 for (y = 0; y < icon->h; ++y) { 5647 pixels = (Uint16 *) icon->pixels + y * icon->pitch / 2; 5648 for (x = 0; x < icon->w; ++x) { 5649 if ((flags & 1) && *pixels == colorkey) { 5650 SET_MASKBIT(icon, x, y, mask); 5651 } else if ((flags & 2) 5652 && (*pixels & icon->format->Amask) == 0) { 5653 SET_MASKBIT(icon, x, y, mask); 5654 } 5655 pixels++; 5656 } 5657 } 5658 } 5659 break; 5660 5661 case 4: 5662 { 5663 Uint32 *pixels; 5664 for (y = 0; y < icon->h; ++y) { 5665 pixels = (Uint32 *) icon->pixels + y * icon->pitch / 4; 5666 for (x = 0; x < icon->w; ++x) { 5667 if ((flags & 1) && *pixels == colorkey) { 5668 SET_MASKBIT(icon, x, y, mask); 5669 } else if ((flags & 2) 5670 && (*pixels & icon->format->Amask) == 0) { 5671 SET_MASKBIT(icon, x, y, mask); 5672 } 5673 pixels++; 5674 } 5675 } 5676 } 5677 break; 5678 } 5679} 5680 5681/* 5682 * Sets the window manager icon for the display window. 5683 */ 5684void SDL_WM_SetIcon(SDL_Surface *icon, Uint8 *mask) 5685{ 5686 if (icon && _this->SetIcon) { 5687 // Generate a mask if necessary, and create the icon! 5688 if (mask == NULL) { 5689 int mask_len = icon->h * (icon->w + 7) / 8; 5690 int flags = 0; 5691 mask = (Uint8 *) SDL_malloc(mask_len); 5692 if (mask == NULL) { 5693 return; 5694 } 5695 SDL_memset(mask, ~0, mask_len); 5696 if (icon->flags & SDL_SRCCOLORKEY) 5697 flags |= 1; 5698 if (icon->flags & SDL_SRCALPHA) 5699 flags |= 2; 5700 if (flags) { 5701 CreateMaskFromColorKeyOrAlpha(icon, mask, flags); 5702 } 5703 _this->SetIcon(_this, icon, mask); 5704 SDL_free(mask); 5705 } else { 5706 _this->SetIcon(_this, icon, mask); 5707 } 5708 } 5709} 5710#endif 5711 5712SDL_TextInputType SDL_GetTextInputType(SDL_PropertiesID props) 5713{ 5714 return (SDL_TextInputType)SDL_GetNumberProperty(props, SDL_PROP_TEXTINPUT_TYPE_NUMBER, SDL_TEXTINPUT_TYPE_TEXT); 5715} 5716 5717SDL_Capitalization SDL_GetTextInputCapitalization(SDL_PropertiesID props) 5718{ 5719 if (SDL_HasProperty(props, SDL_PROP_TEXTINPUT_CAPITALIZATION_NUMBER)) { 5720 return (SDL_Capitalization)SDL_GetNumberProperty(props, SDL_PROP_TEXTINPUT_CAPITALIZATION_NUMBER, SDL_CAPITALIZE_NONE); 5721 } 5722 5723 switch (SDL_GetTextInputType(props)) { 5724 case SDL_TEXTINPUT_TYPE_TEXT: 5725 return SDL_CAPITALIZE_SENTENCES; 5726 case SDL_TEXTINPUT_TYPE_TEXT_NAME: 5727 return SDL_CAPITALIZE_WORDS; 5728 default: 5729 return SDL_CAPITALIZE_NONE; 5730 } 5731} 5732 5733bool SDL_GetTextInputAutocorrect(SDL_PropertiesID props) 5734{ 5735 return SDL_GetBooleanProperty(props, SDL_PROP_TEXTINPUT_AUTOCORRECT_BOOLEAN, true); 5736} 5737 5738bool SDL_GetTextInputMultiline(SDL_PropertiesID props) 5739{ 5740 if (SDL_HasProperty(props, SDL_PROP_TEXTINPUT_MULTILINE_BOOLEAN)) { 5741 return SDL_GetBooleanProperty(props, SDL_PROP_TEXTINPUT_MULTILINE_BOOLEAN, false); 5742 } 5743 5744 if (SDL_GetHintBoolean(SDL_HINT_RETURN_KEY_HIDES_IME, false)) { 5745 return false; 5746 } else { 5747 return true; 5748 } 5749} 5750 5751static bool AutoShowingScreenKeyboard(void) 5752{ 5753 const char *hint = SDL_GetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD); 5754 if (!hint) { 5755 // Steam will eventually have smarts about whether a keyboard is active, so always request the on-screen keyboard on Steam Deck 5756 hint = SDL_GetHint("SteamDeck"); 5757 } 5758 if (((!hint || SDL_strcasecmp(hint, "auto") == 0) && !SDL_HasKeyboard()) || 5759 SDL_GetStringBoolean(hint, false)) { 5760 return true; 5761 } else { 5762 return false; 5763 } 5764} 5765 5766bool SDL_StartTextInput(SDL_Window *window) 5767{ 5768 return SDL_StartTextInputWithProperties(window, 0); 5769} 5770 5771bool SDL_StartTextInputWithProperties(SDL_Window *window, SDL_PropertiesID props) 5772{ 5773 CHECK_WINDOW_MAGIC(window, false); 5774 5775 if (window->text_input_props) { 5776 SDL_DestroyProperties(window->text_input_props); 5777 window->text_input_props = 0; 5778 } 5779 5780 if (props) { 5781 window->text_input_props = SDL_CreateProperties(); 5782 if (!window->text_input_props) { 5783 return false; 5784 } 5785 if (!SDL_CopyProperties(props, window->text_input_props)) { 5786 return false; 5787 } 5788 } 5789 5790 if (_this->SetTextInputProperties) { 5791 _this->SetTextInputProperties(_this, window, props); 5792 } 5793 5794 // Show the on-screen keyboard, if desired 5795 if (AutoShowingScreenKeyboard() && !SDL_ScreenKeyboardShown(window)) { 5796 if (_this->ShowScreenKeyboard) { 5797 _this->ShowScreenKeyboard(_this, window, props); 5798 } 5799 } 5800 5801 if (!window->text_input_active) { 5802 // Finally start the text input system 5803 if (_this->StartTextInput) { 5804 if (!_this->StartTextInput(_this, window, props)) { 5805 return false; 5806 } 5807 } 5808 window->text_input_active = true; 5809 } 5810 return true; 5811} 5812 5813bool SDL_TextInputActive(SDL_Window *window) 5814{ 5815 CHECK_WINDOW_MAGIC(window, false); 5816 5817 return window->text_input_active; 5818} 5819 5820bool SDL_StopTextInput(SDL_Window *window) 5821{ 5822 CHECK_WINDOW_MAGIC(window, false); 5823 5824 if (window->text_input_active) { 5825 // Stop the text input system 5826 if (_this->StopTextInput) { 5827 _this->StopTextInput(_this, window); 5828 } 5829 window->text_input_active = false; 5830 } 5831 5832 // Hide the on-screen keyboard, if desired 5833 if (AutoShowingScreenKeyboard() && SDL_ScreenKeyboardShown(window)) { 5834 if (_this->HideScreenKeyboard) { 5835 _this->HideScreenKeyboard(_this, window); 5836 } 5837 } 5838 return true; 5839} 5840 5841bool SDL_SetTextInputArea(SDL_Window *window, const SDL_Rect *rect, int cursor) 5842{ 5843 CHECK_WINDOW_MAGIC(window, false); 5844 5845 if (rect) { 5846 SDL_copyp(&window->text_input_rect, rect); 5847 window->text_input_cursor = cursor; 5848 } else { 5849 SDL_zero(window->text_input_rect); 5850 window->text_input_cursor = 0; 5851 } 5852 5853 if (_this && _this->UpdateTextInputArea) { 5854 if (!_this->UpdateTextInputArea(_this, window)) { 5855 return false; 5856 } 5857 } 5858 return true; 5859} 5860 5861bool SDL_GetTextInputArea(SDL_Window *window, SDL_Rect *rect, int *cursor) 5862{ 5863 CHECK_WINDOW_MAGIC(window, false); 5864 5865 if (rect) { 5866 SDL_copyp(rect, &window->text_input_rect); 5867 } 5868 if (cursor) { 5869 *cursor = window->text_input_cursor; 5870 } 5871 return true; 5872} 5873 5874bool SDL_ClearComposition(SDL_Window *window) 5875{ 5876 CHECK_WINDOW_MAGIC(window, false); 5877 5878 if (_this->ClearComposition) { 5879 return _this->ClearComposition(_this, window); 5880 } 5881 return true; 5882} 5883 5884bool SDL_HasScreenKeyboardSupport(void) 5885{ 5886 if (_this && _this->HasScreenKeyboardSupport) { 5887 return _this->HasScreenKeyboardSupport(_this); 5888 } 5889 return false; 5890} 5891 5892bool SDL_ScreenKeyboardShown(SDL_Window *window) 5893{ 5894 CHECK_WINDOW_MAGIC(window, false); 5895 5896 return _this->screen_keyboard_shown; 5897} 5898 5899void SDL_SendScreenKeyboardShown(void) 5900{ 5901 if (_this->screen_keyboard_shown) { 5902 return; 5903 } 5904 5905 _this->screen_keyboard_shown = true; 5906 5907 if (SDL_EventEnabled(SDL_EVENT_SCREEN_KEYBOARD_SHOWN)) { 5908 SDL_Event event; 5909 event.type = SDL_EVENT_SCREEN_KEYBOARD_SHOWN; 5910 event.common.timestamp = 0; 5911 SDL_PushEvent(&event); 5912 } 5913} 5914 5915void SDL_SendScreenKeyboardHidden(void) 5916{ 5917 if (!_this->screen_keyboard_shown) { 5918 return; 5919 } 5920 5921 _this->screen_keyboard_shown = false; 5922 5923 if (SDL_EventEnabled(SDL_EVENT_SCREEN_KEYBOARD_HIDDEN)) { 5924 SDL_Event event; 5925 event.type = SDL_EVENT_SCREEN_KEYBOARD_HIDDEN; 5926 event.common.timestamp = 0; 5927 SDL_PushEvent(&event); 5928 } 5929} 5930 5931int SDL_GetMessageBoxCount(void) 5932{ 5933 return SDL_GetAtomicInt(&SDL_messagebox_count); 5934} 5935 5936bool SDL_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID) 5937{ 5938 int dummybutton; 5939 bool result = false; 5940 bool show_cursor_prev; 5941 SDL_Window *current_window; 5942 SDL_MessageBoxData mbdata; 5943 5944 CHECK_PARAM(!messageboxdata) { 5945 return SDL_InvalidParamError("messageboxdata"); 5946 } 5947 CHECK_PARAM(messageboxdata->numbuttons < 0) { 5948 return SDL_SetError("Invalid number of buttons"); 5949 } 5950 5951 // in case either the title or message was a pointer from SDL_GetError(), make a copy 5952 // now, as we'll likely overwrite error state in here. 5953 bool titleisstack = false, msgisstack = false; 5954 char *titlecpy = NULL; 5955 char *msgcpy = NULL; 5956 if (messageboxdata->title) { 5957 const size_t slen = SDL_strlen(messageboxdata->title) + 1; 5958 titlecpy = SDL_small_alloc(char, slen, &titleisstack); 5959 if (!titlecpy) { 5960 return false; 5961 } 5962 SDL_memcpy(titlecpy, messageboxdata->title, slen); 5963 } 5964 5965 if (messageboxdata->message) { 5966 const size_t slen = SDL_strlen(messageboxdata->message) + 1; 5967 msgcpy = SDL_small_alloc(char, slen, &msgisstack); 5968 if (!msgcpy) { 5969 SDL_small_free(titlecpy, titleisstack); 5970 return false; 5971 } 5972 SDL_memcpy(msgcpy, messageboxdata->message, slen); 5973 } 5974 5975 (void)SDL_AtomicIncRef(&SDL_messagebox_count); 5976 5977 current_window = SDL_GetKeyboardFocus(); 5978 SDL_UpdateMouseCapture(false); 5979 SDL_SetRelativeMouseMode(false); 5980 show_cursor_prev = SDL_CursorVisible(); 5981 SDL_ShowCursor(); 5982 SDL_ResetKeyboard(); 5983 5984 if (!buttonID) { 5985 buttonID = &dummybutton; 5986 } 5987 5988 SDL_memcpy(&mbdata, messageboxdata, sizeof(*messageboxdata)); 5989 mbdata.title = titlecpy; 5990 if (!mbdata.title) { 5991 mbdata.title = ""; 5992 } 5993 mbdata.message = msgcpy; 5994 if (!mbdata.message) { 5995 mbdata.message = ""; 5996 } 5997 messageboxdata = &mbdata; 5998 5999 SDL_ClearError(); 6000 6001 if (_this && _this->ShowMessageBox) { 6002 result = _this->ShowMessageBox(_this, messageboxdata, buttonID); 6003 } else { 6004 // It's completely fine to call this function before video is initialized 6005 const char *driver_name = SDL_GetHint(SDL_HINT_VIDEO_DRIVER); 6006 if (driver_name && *driver_name != 0) { 6007 const char *driver_attempt = driver_name; 6008 while (driver_attempt && (*driver_attempt != 0) && !result) { 6009 const char *driver_attempt_end = SDL_strchr(driver_attempt, ','); 6010 size_t driver_attempt_len = (driver_attempt_end) ? (driver_attempt_end - driver_attempt) 6011 : SDL_strlen(driver_attempt); 6012 for (int i = 0; bootstrap[i]; ++i) { 6013 if (bootstrap[i]->ShowMessageBox && (driver_attempt_len == SDL_strlen(bootstrap[i]->name)) && 6014 (SDL_strncasecmp(bootstrap[i]->name, driver_attempt, driver_attempt_len) == 0)) { 6015 if (bootstrap[i]->ShowMessageBox(messageboxdata, buttonID)) { 6016 result = true; 6017 } 6018 break; 6019 } 6020 } 6021 6022 driver_attempt = (driver_attempt_end) ? (driver_attempt_end + 1) : NULL; 6023 } 6024 } else { 6025 for (int i = 0; bootstrap[i]; ++i) { 6026 if (bootstrap[i]->ShowMessageBox && bootstrap[i]->ShowMessageBox(messageboxdata, buttonID)) { 6027 result = true; 6028 break; 6029 } 6030 } 6031 } 6032 } 6033 6034 if (!result) { 6035 const char *error = SDL_GetError(); 6036 6037 if (!*error) { 6038 SDL_SetError("No message system available"); 6039 } 6040 } else { 6041 SDL_ClearError(); 6042 } 6043 6044 (void)SDL_AtomicDecRef(&SDL_messagebox_count); 6045 6046 if (current_window) { 6047 SDL_RaiseWindow(current_window); 6048 } 6049 6050 if (!show_cursor_prev) { 6051 SDL_HideCursor(); 6052 } 6053 SDL_UpdateRelativeMouseMode(); 6054 SDL_UpdateMouseCapture(false); 6055 6056 SDL_small_free(msgcpy, msgisstack); 6057 SDL_small_free(titlecpy, titleisstack); 6058 6059 return result; 6060} 6061 6062bool SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags flags, const char *title, const char *message, SDL_Window *window) 6063{ 6064#if defined(SDL_PLATFORM_3DS) 6065 errorConf errCnf; 6066 bool hasGpuRight; 6067 6068 // If the video subsystem has not been initialised, set up graphics temporarily 6069 hasGpuRight = gspHasGpuRight(); 6070 if (!hasGpuRight) 6071 gfxInitDefault(); 6072 6073 errorInit(&errCnf, ERROR_TEXT_WORD_WRAP, CFG_LANGUAGE_EN); 6074 errorText(&errCnf, message); 6075 errorDisp(&errCnf); 6076 6077 if (!hasGpuRight) 6078 gfxExit(); 6079 6080 return true; 6081#else 6082 SDL_MessageBoxData data; 6083 SDL_MessageBoxButtonData button; 6084 6085 SDL_zero(data); 6086 data.flags = flags; 6087 data.title = title; 6088 data.message = message; 6089 data.numbuttons = 1; 6090 data.buttons = &button; 6091 data.window = window; 6092 6093 SDL_zero(button); 6094 button.flags |= SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT; 6095 button.flags |= SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT; 6096 button.text = "OK"; 6097 6098 return SDL_ShowMessageBox(&data, NULL); 6099#endif 6100} 6101 6102bool SDL_ShouldAllowTopmost(void) 6103{ 6104 return SDL_GetHintBoolean(SDL_HINT_WINDOW_ALLOW_TOPMOST, true); 6105} 6106 6107bool SDL_ShowWindowSystemMenu(SDL_Window *window, int x, int y) 6108{ 6109 CHECK_WINDOW_MAGIC(window, false); 6110 CHECK_WINDOW_NOT_POPUP(window, false); 6111 6112 if (_this->ShowWindowSystemMenu) { 6113 _this->ShowWindowSystemMenu(window, x, y); 6114 return true; 6115 } 6116 6117 return SDL_Unsupported(); 6118} 6119 6120bool SDL_SetWindowHitTest(SDL_Window *window, SDL_HitTest callback, void *callback_data) 6121{ 6122 CHECK_WINDOW_MAGIC(window, false); 6123 6124 if (!_this->SetWindowHitTest) { 6125 return SDL_Unsupported(); 6126 } 6127 6128 window->hit_test = callback; 6129 window->hit_test_data = callback_data; 6130 6131 return _this->SetWindowHitTest(window, callback != NULL); 6132} 6133 6134bool SDL_SetWindowShape(SDL_Window *window, SDL_Surface *shape) 6135{ 6136 SDL_PropertiesID props; 6137 SDL_Surface *surface; 6138 6139 CHECK_WINDOW_MAGIC(window, false); 6140 6141 if (!(window->flags & SDL_WINDOW_TRANSPARENT)) { 6142 return SDL_SetError("Window must be created with SDL_WINDOW_TRANSPARENT"); 6143 } 6144 6145 props = SDL_GetWindowProperties(window); 6146 if (!props) { 6147 return false; 6148 } 6149 6150 if (shape) { 6151 surface = SDL_ConvertSurface(shape, SDL_PIXELFORMAT_ARGB32); 6152 if (!surface) { 6153 return false; 6154 } 6155 } else { 6156 surface = NULL; 6157 } 6158 6159 if (!SDL_SetSurfaceProperty(props, SDL_PROP_WINDOW_SHAPE_POINTER, surface)) { 6160 return false; 6161 } 6162 6163 if (_this->UpdateWindowShape) { 6164 if (!_this->UpdateWindowShape(_this, window, surface)) { 6165 return false; 6166 } 6167 } 6168 return true; 6169} 6170 6171/* 6172 * Functions used by iOS application delegates 6173 */ 6174void SDL_OnApplicationWillTerminate(void) 6175{ 6176 SDL_SendAppEvent(SDL_EVENT_TERMINATING); 6177} 6178 6179void SDL_OnApplicationDidReceiveMemoryWarning(void) 6180{ 6181 SDL_SendAppEvent(SDL_EVENT_LOW_MEMORY); 6182} 6183 6184void SDL_OnApplicationWillEnterBackground(void) 6185{ 6186 if (_this) { 6187 SDL_Window *window; 6188 for (window = _this->windows; window; window = window->next) { 6189 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0); 6190 } 6191 SDL_SetKeyboardFocus(NULL); 6192 } 6193 SDL_SendAppEvent(SDL_EVENT_WILL_ENTER_BACKGROUND); 6194} 6195 6196void SDL_OnApplicationDidEnterBackground(void) 6197{ 6198 SDL_SendAppEvent(SDL_EVENT_DID_ENTER_BACKGROUND); 6199} 6200 6201void SDL_OnApplicationWillEnterForeground(void) 6202{ 6203 SDL_SendAppEvent(SDL_EVENT_WILL_ENTER_FOREGROUND); 6204} 6205 6206void SDL_OnApplicationDidEnterForeground(void) 6207{ 6208 SDL_SendAppEvent(SDL_EVENT_DID_ENTER_FOREGROUND); 6209 6210 if (_this) { 6211 SDL_Window *window; 6212 for (window = _this->windows; window; window = window->next) { 6213 SDL_SetKeyboardFocus(window); 6214 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0); 6215 } 6216 } 6217} 6218 6219#define NOT_A_VULKAN_WINDOW "The specified window isn't a Vulkan window" 6220 6221bool SDL_Vulkan_LoadLibrary(const char *path) 6222{ 6223 bool result; 6224 6225 if (!_this) { 6226 return SDL_UninitializedVideo(); 6227 } 6228 if (_this->vulkan_config.loader_loaded) { 6229 if (path && SDL_strcmp(path, _this->vulkan_config.loader_path) != 0) { 6230 return SDL_SetError("Vulkan loader library already loaded"); 6231 } 6232 result = true; 6233 } else { 6234 if (!_this->Vulkan_LoadLibrary) { 6235 return SDL_DllNotSupported("Vulkan"); 6236 } 6237 result = _this->Vulkan_LoadLibrary(_this, path); 6238 } 6239 if (result) { 6240 _this->vulkan_config.loader_loaded++; 6241 } 6242 return result; 6243} 6244 6245SDL_FunctionPointer SDL_Vulkan_GetVkGetInstanceProcAddr(void) 6246{ 6247 if (!_this) { 6248 SDL_UninitializedVideo(); 6249 return NULL; 6250 } 6251 if (!_this->vulkan_config.loader_loaded) { 6252 SDL_SetError("No Vulkan loader has been loaded"); 6253 return NULL; 6254 } 6255 return (SDL_FunctionPointer)_this->vulkan_config.vkGetInstanceProcAddr; 6256} 6257 6258void SDL_Vulkan_UnloadLibrary(void) 6259{ 6260 if (!_this) { 6261 SDL_UninitializedVideo(); 6262 return; 6263 } 6264 if (_this->vulkan_config.loader_loaded > 0) { 6265 if (--_this->vulkan_config.loader_loaded > 0) { 6266 return; 6267 } 6268 if (_this->Vulkan_UnloadLibrary) { 6269 _this->Vulkan_UnloadLibrary(_this); 6270 } 6271 } 6272} 6273 6274char const * const *SDL_Vulkan_GetInstanceExtensions(Uint32 *count) 6275{ 6276 return _this->Vulkan_GetInstanceExtensions(_this, count); 6277} 6278 6279bool SDL_Vulkan_CreateSurface(SDL_Window *window, 6280 VkInstance instance, 6281 const struct VkAllocationCallbacks *allocator, 6282 VkSurfaceKHR *surface) 6283{ 6284 CHECK_WINDOW_MAGIC(window, false); 6285 6286 if (!_this->Vulkan_CreateSurface) { 6287 SDL_Unsupported(); 6288 return false; 6289 } 6290 6291 CHECK_PARAM(!instance) { 6292 return SDL_InvalidParamError("instance"); 6293 } 6294 6295 CHECK_PARAM(!surface) { 6296 return SDL_InvalidParamError("surface"); 6297 } 6298 6299 if (!(window->flags & SDL_WINDOW_VULKAN)) { 6300 // No problem, we can convert to Vulkan 6301 if (window->flags & SDL_WINDOW_OPENGL) { 6302 window->flags &= ~SDL_WINDOW_OPENGL; 6303 SDL_GL_UnloadLibrary(); 6304 } 6305 if (window->flags & SDL_WINDOW_METAL) { 6306 window->flags &= ~SDL_WINDOW_METAL; 6307 // Nothing more to do for Metal. 6308 } 6309 if (SDL_Vulkan_LoadLibrary(NULL)) { 6310 window->flags |= SDL_WINDOW_VULKAN; 6311 } else { 6312 return SDL_SetError("failed to load Vulkan library"); 6313 } 6314 } 6315 6316 return _this->Vulkan_CreateSurface(_this, window, instance, allocator, surface); 6317} 6318 6319void SDL_Vulkan_DestroySurface(VkInstance instance, 6320 VkSurfaceKHR surface, 6321 const struct VkAllocationCallbacks *allocator) 6322{ 6323 if (_this && instance && surface && _this->Vulkan_DestroySurface) { 6324 _this->Vulkan_DestroySurface(_this, instance, surface, allocator); 6325 } 6326} 6327 6328bool SDL_Vulkan_GetPresentationSupport(VkInstance instance, 6329 VkPhysicalDevice physicalDevice, 6330 Uint32 queueFamilyIndex) 6331{ 6332 CHECK_PARAM(!_this) { 6333 SDL_UninitializedVideo(); 6334 return false; 6335 } 6336 6337 CHECK_PARAM(!instance) { 6338 SDL_InvalidParamError("instance"); 6339 return false; 6340 } 6341 6342 CHECK_PARAM(!physicalDevice) { 6343 SDL_InvalidParamError("physicalDevice"); 6344 return false; 6345 } 6346 6347 if (_this->Vulkan_GetPresentationSupport) { 6348 return _this->Vulkan_GetPresentationSupport(_this, instance, physicalDevice, queueFamilyIndex); 6349 } 6350 6351 /* If the backend does not have this function then it does not have a 6352 * WSI function to query it; in other words it's not necessary to check 6353 * as it is always supported. 6354 */ 6355 return true; 6356} 6357 6358SDL_MetalView SDL_Metal_CreateView(SDL_Window *window) 6359{ 6360 CHECK_WINDOW_MAGIC(window, NULL); 6361 6362 if (!_this->Metal_CreateView) { 6363 SDL_Unsupported(); 6364 return NULL; 6365 } 6366 6367 if (!(window->flags & SDL_WINDOW_METAL)) { 6368 // No problem, we can convert to Metal 6369 if (window->flags & SDL_WINDOW_OPENGL) { 6370 window->flags &= ~SDL_WINDOW_OPENGL; 6371 SDL_GL_UnloadLibrary(); 6372 } 6373 if (window->flags & SDL_WINDOW_VULKAN) { 6374 window->flags &= ~SDL_WINDOW_VULKAN; 6375 SDL_Vulkan_UnloadLibrary(); 6376 } 6377 window->flags |= SDL_WINDOW_METAL; 6378 } 6379 6380 return _this->Metal_CreateView(_this, window); 6381} 6382 6383void SDL_Metal_DestroyView(SDL_MetalView view) 6384{ 6385 if (_this && view && _this->Metal_DestroyView) { 6386 _this->Metal_DestroyView(_this, view); 6387 } 6388} 6389 6390void *SDL_Metal_GetLayer(SDL_MetalView view) 6391{ 6392 if (_this && _this->Metal_GetLayer) { 6393 CHECK_PARAM(!view) { 6394 SDL_InvalidParamError("view"); 6395 return NULL; 6396 } 6397 return _this->Metal_GetLayer(_this, view); 6398 } else { 6399 SDL_SetError("Metal is not supported."); 6400 return NULL; 6401 } 6402} 6403 6404#if defined(SDL_VIDEO_DRIVER_X11) || defined(SDL_VIDEO_DRIVER_WAYLAND) || defined(SDL_VIDEO_DRIVER_EMSCRIPTEN) 6405const char *SDL_GetCSSCursorName(SDL_SystemCursor id, const char **fallback_name) 6406{ 6407 // Reference: https://www.w3.org/TR/css-ui-4/#cursor 6408 // Also in: https://www.freedesktop.org/wiki/Specifications/cursor-spec/ 6409 switch (id) { 6410 case SDL_SYSTEM_CURSOR_DEFAULT: 6411 return "default"; 6412 6413 case SDL_SYSTEM_CURSOR_TEXT: 6414 return "text"; 6415 6416 case SDL_SYSTEM_CURSOR_WAIT: 6417 return "wait"; 6418 6419 case SDL_SYSTEM_CURSOR_CROSSHAIR: 6420 return "crosshair"; 6421 6422 case SDL_SYSTEM_CURSOR_PROGRESS: 6423 return "progress"; 6424 6425 case SDL_SYSTEM_CURSOR_NWSE_RESIZE: 6426 if (fallback_name) { 6427 // only a single arrow 6428 *fallback_name = "nw-resize"; 6429 } 6430 return "nwse-resize"; 6431 6432 case SDL_SYSTEM_CURSOR_NESW_RESIZE: 6433 if (fallback_name) { 6434 // only a single arrow 6435 *fallback_name = "ne-resize"; 6436 } 6437 return "nesw-resize"; 6438 6439 case SDL_SYSTEM_CURSOR_EW_RESIZE: 6440 if (fallback_name) { 6441 *fallback_name = "col-resize"; 6442 } 6443 return "ew-resize"; 6444 6445 case SDL_SYSTEM_CURSOR_NS_RESIZE: 6446 if (fallback_name) { 6447 *fallback_name = "row-resize"; 6448 } 6449 return "ns-resize"; 6450 6451 case SDL_SYSTEM_CURSOR_MOVE: 6452 return "all-scroll"; 6453 6454 case SDL_SYSTEM_CURSOR_NOT_ALLOWED: 6455 return "not-allowed"; 6456 6457 case SDL_SYSTEM_CURSOR_POINTER: 6458 return "pointer"; 6459 6460 case SDL_SYSTEM_CURSOR_NW_RESIZE: 6461 return "nw-resize"; 6462 6463 case SDL_SYSTEM_CURSOR_N_RESIZE: 6464 return "n-resize"; 6465 6466 case SDL_SYSTEM_CURSOR_NE_RESIZE: 6467 return "ne-resize"; 6468 6469 case SDL_SYSTEM_CURSOR_E_RESIZE: 6470 return "e-resize"; 6471 6472 case SDL_SYSTEM_CURSOR_SE_RESIZE: 6473 return "se-resize"; 6474 6475 case SDL_SYSTEM_CURSOR_S_RESIZE: 6476 return "s-resize"; 6477 6478 case SDL_SYSTEM_CURSOR_SW_RESIZE: 6479 return "sw-resize"; 6480 6481 case SDL_SYSTEM_CURSOR_W_RESIZE: 6482 return "w-resize"; 6483 6484 default: 6485 return "default"; 6486 } 6487} 6488#endif 6489[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.