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