Simple Directmedia Layer
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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