Simple Directmedia Layer
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(¢er, 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, ¤t_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