Atlas - SDL_video.c

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