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