1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2021 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#include "../SDL_internal.h"
22
23/* The high-level video driver subsystem */
24
25#include "SDL.h"
26#include "SDL_video.h"
27#include "SDL_sysvideo.h"
28#include "SDL_blit.h"
29#include "SDL_pixels_c.h"
30#include "SDL_rect_c.h"
31#include "../events/SDL_events_c.h"
32#include "../timer/SDL_timer_c.h"
33
34#include "SDL_syswm.h"
35
36#if SDL_VIDEO_OPENGL
37#include "SDL_opengl.h"
38#endif /* SDL_VIDEO_OPENGL */
39
40#if SDL_VIDEO_OPENGL_ES && !SDL_VIDEO_OPENGL
41#include "SDL_opengles.h"
42#endif /* SDL_VIDEO_OPENGL_ES && !SDL_VIDEO_OPENGL */
43
44/* GL and GLES2 headers conflict on Linux 32 bits */
45#if SDL_VIDEO_OPENGL_ES2 && !SDL_VIDEO_OPENGL
46#include "SDL_opengles2.h"
47#endif /* SDL_VIDEO_OPENGL_ES2 && !SDL_VIDEO_OPENGL */
48
49#if !SDL_VIDEO_OPENGL
50#ifndef GL_CONTEXT_RELEASE_BEHAVIOR_KHR
51#define GL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x82FB
52#endif
53#endif
54
55#ifdef __EMSCRIPTEN__
56#include <emscripten.h>
57#endif
58
59/* Available video drivers */
60static VideoBootStrap *bootstrap[] = {
61#if SDL_VIDEO_DRIVER_COCOA
62 &COCOA_bootstrap,
63#endif
64#if SDL_VIDEO_DRIVER_X11
65 &X11_bootstrap,
66#endif
67#if SDL_VIDEO_DRIVER_WAYLAND
68 &Wayland_bootstrap,
69#endif
70#if SDL_VIDEO_DRIVER_VIVANTE
71 &VIVANTE_bootstrap,
72#endif
73#if SDL_VIDEO_DRIVER_DIRECTFB
74 &DirectFB_bootstrap,
75#endif
76#if SDL_VIDEO_DRIVER_WINDOWS
77 &WINDOWS_bootstrap,
78#endif
79#if SDL_VIDEO_DRIVER_WINRT
80 &WINRT_bootstrap,
81#endif
82#if SDL_VIDEO_DRIVER_HAIKU
83 &HAIKU_bootstrap,
84#endif
85#if SDL_VIDEO_DRIVER_PANDORA
86 &PND_bootstrap,
87#endif
88#if SDL_VIDEO_DRIVER_UIKIT
89 &UIKIT_bootstrap,
90#endif
91#if SDL_VIDEO_DRIVER_ANDROID
92 &Android_bootstrap,
93#endif
94#if SDL_VIDEO_DRIVER_PSP
95 &PSP_bootstrap,
96#endif
97#if SDL_VIDEO_DRIVER_VITA
98 &VITA_bootstrap,
99#endif
100#if SDL_VIDEO_DRIVER_KMSDRM
101 &KMSDRM_bootstrap,
102#endif
103#if SDL_VIDEO_DRIVER_RPI
104 &RPI_bootstrap,
105#endif
106#if SDL_VIDEO_DRIVER_NACL
107 &NACL_bootstrap,
108#endif
109#if SDL_VIDEO_DRIVER_EMSCRIPTEN
110 &Emscripten_bootstrap,
111#endif
112#if SDL_VIDEO_DRIVER_QNX
113 &QNX_bootstrap,
114#endif
115#if SDL_VIDEO_DRIVER_OFFSCREEN
116 &OFFSCREEN_bootstrap,
117#endif
118#if SDL_VIDEO_DRIVER_OS2
119 &OS2DIVE_bootstrap,
120 &OS2VMAN_bootstrap,
121#endif
122#if SDL_VIDEO_DRIVER_DUMMY
123 &DUMMY_bootstrap,
124#endif
125 NULL
126};
127
128static SDL_VideoDevice *_this = NULL;
129
130#define CHECK_WINDOW_MAGIC(window, retval) \
131 if (!_this) { \
132 SDL_UninitializedVideo(); \
133 return retval; \
134 } \
135 SDL_assert(window && window->magic == &_this->window_magic); \
136 if (!window || window->magic != &_this->window_magic) { \
137 SDL_SetError("Invalid window"); \
138 return retval; \
139 }
140
141#define CHECK_DISPLAY_INDEX(displayIndex, retval) \
142 if (!_this) { \
143 SDL_UninitializedVideo(); \
144 return retval; \
145 } \
146 SDL_assert(_this->displays != NULL); \
147 SDL_assert(displayIndex >= 0 && displayIndex < _this->num_displays); \
148 if (displayIndex < 0 || displayIndex >= _this->num_displays) { \
149 SDL_SetError("displayIndex must be in the range 0 - %d", \
150 _this->num_displays - 1); \
151 return retval; \
152 }
153
154#define FULLSCREEN_MASK (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN)
155
156#ifdef __MACOSX__
157/* Support for Mac OS X fullscreen spaces */
158extern SDL_bool Cocoa_IsWindowInFullscreenSpace(SDL_Window * window);
159extern SDL_bool Cocoa_SetWindowFullscreenSpace(SDL_Window * window, SDL_bool state);
160#endif
161
162
163/* Support for framebuffer emulation using an accelerated renderer */
164
165#define SDL_WINDOWTEXTUREDATA "_SDL_WindowTextureData"
166
167typedef struct {
168 SDL_Renderer *renderer;
169 SDL_Texture *texture;
170 void *pixels;
171 int pitch;
172 int bytes_per_pixel;
173} SDL_WindowTextureData;
174
175static SDL_bool
176ShouldUseTextureFramebuffer()
177{
178 const char *hint;
179
180 /* If there's no native framebuffer support then there's no option */
181 if (!_this->CreateWindowFramebuffer) {
182 return SDL_TRUE;
183 }
184
185 /* If this is the dummy driver there is no texture support */
186 if (_this->is_dummy) {
187 return SDL_FALSE;
188 }
189
190 /* If the user has specified a software renderer we can't use a
191 texture framebuffer, or renderer creation will go recursive.
192 */
193 hint = SDL_GetHint(SDL_HINT_RENDER_DRIVER);
194 if (hint && SDL_strcasecmp(hint, "software") == 0) {
195 return SDL_FALSE;
196 }
197
198 /* See if the user or application wants a specific behavior */
199 hint = SDL_GetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION);
200 if (hint) {
201 if (*hint == '0' || SDL_strcasecmp(hint, "false") == 0) {
202 return SDL_FALSE;
203 } else {
204 return SDL_TRUE;
205 }
206 }
207
208 /* Each platform has different performance characteristics */
209#if defined(__WIN32__)
210 /* GDI BitBlt() is way faster than Direct3D dynamic textures right now.
211 */
212 return SDL_FALSE;
213
214#elif defined(__MACOSX__)
215 /* Mac OS X uses OpenGL as the native fast path (for cocoa and X11) */
216 return SDL_TRUE;
217
218#elif defined(__LINUX__)
219 /* Properly configured OpenGL drivers are faster than MIT-SHM */
220#if SDL_VIDEO_OPENGL
221 /* Ugh, find a way to cache this value! */
222 {
223 SDL_Window *window;
224 SDL_GLContext context;
225 SDL_bool hasAcceleratedOpenGL = SDL_FALSE;
226
227 window = SDL_CreateWindow("OpenGL test", -32, -32, 32, 32, SDL_WINDOW_OPENGL|SDL_WINDOW_HIDDEN);
228 if (window) {
229 context = SDL_GL_CreateContext(window);
230 if (context) {
231 const GLubyte *(APIENTRY * glGetStringFunc) (GLenum);
232 const char *vendor = NULL;
233
234 glGetStringFunc = SDL_GL_GetProcAddress("glGetString");
235 if (glGetStringFunc) {
236 vendor = (const char *) glGetStringFunc(GL_VENDOR);
237 }
238 /* Add more vendors here at will... */
239 if (vendor &&
240 (SDL_strstr(vendor, "ATI Technologies") ||
241 SDL_strstr(vendor, "NVIDIA"))) {
242 hasAcceleratedOpenGL = SDL_TRUE;
243 }
244 SDL_GL_DeleteContext(context);
245 }
246 SDL_DestroyWindow(window);
247 }
248 return hasAcceleratedOpenGL;
249 }
250#elif SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
251 /* Let's be optimistic about this! */
252 return SDL_TRUE;
253#else
254 return SDL_FALSE;
255#endif
256
257#else
258 /* Play it safe, assume that if there is a framebuffer driver that it's
259 optimized for the current platform.
260 */
261 return SDL_FALSE;
262#endif
263}
264
265static int
266SDL_CreateWindowTexture(SDL_VideoDevice *unused, SDL_Window * window, Uint32 * format, void ** pixels, int *pitch)
267{
268 SDL_WindowTextureData *data;
269
270 data = SDL_GetWindowData(window, SDL_WINDOWTEXTUREDATA);
271 if (!data) {
272 SDL_Renderer *renderer = NULL;
273 int i;
274 const char *hint = SDL_GetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION);
275
276 /* Check to see if there's a specific driver requested */
277 if (hint && *hint != '0' && *hint != '1' &&
278 SDL_strcasecmp(hint, "true") != 0 &&
279 SDL_strcasecmp(hint, "false") != 0 &&
280 SDL_strcasecmp(hint, "software") != 0) {
281 for (i = 0; i < SDL_GetNumRenderDrivers(); ++i) {
282 SDL_RendererInfo info;
283 SDL_GetRenderDriverInfo(i, &info);
284 if (SDL_strcasecmp(info.name, hint) == 0) {
285 renderer = SDL_CreateRenderer(window, i, 0);
286 break;
287 }
288 }
289 }
290
291 if (!renderer) {
292 for (i = 0; i < SDL_GetNumRenderDrivers(); ++i) {
293 SDL_RendererInfo info;
294 SDL_GetRenderDriverInfo(i, &info);
295 if (SDL_strcmp(info.name, "software") != 0) {
296 renderer = SDL_CreateRenderer(window, i, 0);
297 if (renderer) {
298 break;
299 }
300 }
301 }
302 }
303 if (!renderer) {
304 return SDL_SetError("No hardware accelerated renderers available");
305 }
306
307 /* Create the data after we successfully create the renderer (bug #1116) */
308 data = (SDL_WindowTextureData *)SDL_calloc(1, sizeof(*data));
309 if (!data) {
310 SDL_DestroyRenderer(renderer);
311 return SDL_OutOfMemory();
312 }
313 SDL_SetWindowData(window, SDL_WINDOWTEXTUREDATA, data);
314
315 data->renderer = renderer;
316 }
317
318 /* Free any old texture and pixel data */
319 if (data->texture) {
320 SDL_DestroyTexture(data->texture);
321 data->texture = NULL;
322 }
323 SDL_free(data->pixels);
324 data->pixels = NULL;
325
326 {
327 SDL_RendererInfo info;
328 Uint32 i;
329
330 if (SDL_GetRendererInfo(data->renderer, &info) < 0) {
331 return -1;
332 }
333
334 /* Find the first format without an alpha channel */
335 *format = info.texture_formats[0];
336
337 for (i = 0; i < info.num_texture_formats; ++i) {
338 if (!SDL_ISPIXELFORMAT_FOURCC(info.texture_formats[i]) &&
339 !SDL_ISPIXELFORMAT_ALPHA(info.texture_formats[i])) {
340 *format = info.texture_formats[i];
341 break;
342 }
343 }
344 }
345
346 data->texture = SDL_CreateTexture(data->renderer, *format,
347 SDL_TEXTUREACCESS_STREAMING,
348 window->w, window->h);
349 if (!data->texture) {
350 return -1;
351 }
352
353 /* Create framebuffer data */
354 data->bytes_per_pixel = SDL_BYTESPERPIXEL(*format);
355 data->pitch = (((window->w * data->bytes_per_pixel) + 3) & ~3);
356
357 {
358 /* Make static analysis happy about potential malloc(0) calls. */
359 const size_t allocsize = window->h * data->pitch;
360 data->pixels = SDL_malloc((allocsize > 0) ? allocsize : 1);
361 if (!data->pixels) {
362 return SDL_OutOfMemory();
363 }
364 }
365
366 *pixels = data->pixels;
367 *pitch = data->pitch;
368
369 /* Make sure we're not double-scaling the viewport */
370 SDL_RenderSetViewport(data->renderer, NULL);
371
372 return 0;
373}
374
375static int
376SDL_UpdateWindowTexture(SDL_VideoDevice *unused, SDL_Window * window, const SDL_Rect * rects, int numrects)
377{
378 SDL_WindowTextureData *data;
379 SDL_Rect rect;
380 void *src;
381
382 data = SDL_GetWindowData(window, SDL_WINDOWTEXTUREDATA);
383 if (!data || !data->texture) {
384 return SDL_SetError("No window texture data");
385 }
386
387 /* Update a single rect that contains subrects for best DMA performance */
388 if (SDL_GetSpanEnclosingRect(window->w, window->h, numrects, rects, &rect)) {
389 src = (void *)((Uint8 *)data->pixels +
390 rect.y * data->pitch +
391 rect.x * data->bytes_per_pixel);
392 if (SDL_UpdateTexture(data->texture, &rect, src, data->pitch) < 0) {
393 return -1;
394 }
395
396 if (SDL_RenderCopy(data->renderer, data->texture, NULL, NULL) < 0) {
397 return -1;
398 }
399
400 SDL_RenderPresent(data->renderer);
401 }
402 return 0;
403}
404
405static void
406SDL_DestroyWindowTexture(SDL_VideoDevice *unused, SDL_Window * window)
407{
408 SDL_WindowTextureData *data;
409
410 data = SDL_SetWindowData(window, SDL_WINDOWTEXTUREDATA, NULL);
411 if (!data) {
412 return;
413 }
414 if (data->texture) {
415 SDL_DestroyTexture(data->texture);
416 }
417 if (data->renderer) {
418 SDL_DestroyRenderer(data->renderer);
419 }
420 SDL_free(data->pixels);
421 SDL_free(data);
422}
423
424
425static int
426cmpmodes(const void *A, const void *B)
427{
428 const SDL_DisplayMode *a = (const SDL_DisplayMode *) A;
429 const SDL_DisplayMode *b = (const SDL_DisplayMode *) B;
430 if (a == b) {
431 return 0;
432 } else if (a->w != b->w) {
433 return b->w - a->w;
434 } else if (a->h != b->h) {
435 return b->h - a->h;
436 } else if (SDL_BITSPERPIXEL(a->format) != SDL_BITSPERPIXEL(b->format)) {
437 return SDL_BITSPERPIXEL(b->format) - SDL_BITSPERPIXEL(a->format);
438 } else if (SDL_PIXELLAYOUT(a->format) != SDL_PIXELLAYOUT(b->format)) {
439 return SDL_PIXELLAYOUT(b->format) - SDL_PIXELLAYOUT(a->format);
440 } else if (a->refresh_rate != b->refresh_rate) {
441 return b->refresh_rate - a->refresh_rate;
442 }
443 return 0;
444}
445
446static int
447SDL_UninitializedVideo()
448{
449 return SDL_SetError("Video subsystem has not been initialized");
450}
451
452int
453SDL_GetNumVideoDrivers(void)
454{
455 return SDL_arraysize(bootstrap) - 1;
456}
457
458const char *
459SDL_GetVideoDriver(int index)
460{
461 if (index >= 0 && index < SDL_GetNumVideoDrivers()) {
462 return bootstrap[index]->name;
463 }
464 return NULL;
465}
466
467/*
468 * Initialize the video and event subsystems -- determine native pixel format
469 */
470int
471SDL_VideoInit(const char *driver_name)
472{
473 SDL_VideoDevice *video;
474 int index;
475 int i;
476
477 /* Check to make sure we don't overwrite '_this' */
478 if (_this != NULL) {
479 SDL_VideoQuit();
480 }
481
482#if !SDL_TIMERS_DISABLED
483 SDL_TicksInit();
484#endif
485
486 /* Start the event loop */
487 if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0 ||
488 SDL_KeyboardInit() < 0 ||
489 SDL_MouseInit() < 0 ||
490 SDL_TouchInit() < 0) {
491 return -1;
492 }
493
494 /* Select the proper video driver */
495 index = 0;
496 video = NULL;
497 if (driver_name == NULL) {
498 driver_name = SDL_getenv("SDL_VIDEODRIVER");
499 }
500 if (driver_name != NULL) {
501 for (i = 0; bootstrap[i]; ++i) {
502 if (SDL_strncasecmp(bootstrap[i]->name, driver_name, SDL_strlen(driver_name)) == 0) {
503 video = bootstrap[i]->create(index);
504 break;
505 }
506 }
507 } else {
508 for (i = 0; bootstrap[i]; ++i) {
509 video = bootstrap[i]->create(index);
510 if (video != NULL) {
511 break;
512 }
513 }
514 }
515 if (video == NULL) {
516 if (driver_name) {
517 return SDL_SetError("%s not available", driver_name);
518 }
519 return SDL_SetError("No available video device");
520 }
521 _this = video;
522 _this->name = bootstrap[i]->name;
523 _this->next_object_id = 1;
524
525
526 /* Set some very sane GL defaults */
527 _this->gl_config.driver_loaded = 0;
528 _this->gl_config.dll_handle = NULL;
529 SDL_GL_ResetAttributes();
530
531 _this->current_glwin_tls = SDL_TLSCreate();
532 _this->current_glctx_tls = SDL_TLSCreate();
533
534 /* Initialize the video subsystem */
535 if (_this->VideoInit(_this) < 0) {
536 SDL_VideoQuit();
537 return -1;
538 }
539
540 /* Make sure some displays were added */
541 if (_this->num_displays == 0) {
542 SDL_VideoQuit();
543 return SDL_SetError("The video driver did not add any displays");
544 }
545
546 /* Add the renderer framebuffer emulation if desired */
547 if (ShouldUseTextureFramebuffer()) {
548 _this->CreateWindowFramebuffer = SDL_CreateWindowTexture;
549 _this->UpdateWindowFramebuffer = SDL_UpdateWindowTexture;
550 _this->DestroyWindowFramebuffer = SDL_DestroyWindowTexture;
551 }
552
553 /* Disable the screen saver by default. This is a change from <= 2.0.1,
554 but most things using SDL are games or media players; you wouldn't
555 want a screensaver to trigger if you're playing exclusively with a
556 joystick, or passively watching a movie. Things that use SDL but
557 function more like a normal desktop app should explicitly reenable the
558 screensaver. */
559 if (!SDL_GetHintBoolean(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, SDL_FALSE)) {
560 SDL_DisableScreenSaver();
561 }
562
563 /* If we don't use a screen keyboard, turn on text input by default,
564 otherwise programs that expect to get text events without enabling
565 UNICODE input won't get any events.
566
567 Actually, come to think of it, you needed to call SDL_EnableUNICODE(1)
568 in SDL 1.2 before you got text input events. Hmm...
569 */
570 if (!SDL_HasScreenKeyboardSupport()) {
571 SDL_StartTextInput();
572 }
573
574 /* We're ready to go! */
575 return 0;
576}
577
578const char *
579SDL_GetCurrentVideoDriver()
580{
581 if (!_this) {
582 SDL_UninitializedVideo();
583 return NULL;
584 }
585 return _this->name;
586}
587
588SDL_VideoDevice *
589SDL_GetVideoDevice(void)
590{
591 return _this;
592}
593
594int
595SDL_AddBasicVideoDisplay(const SDL_DisplayMode * desktop_mode)
596{
597 SDL_VideoDisplay display;
598
599 SDL_zero(display);
600 if (desktop_mode) {
601 display.desktop_mode = *desktop_mode;
602 }
603 display.current_mode = display.desktop_mode;
604
605 return SDL_AddVideoDisplay(&display, SDL_FALSE);
606}
607
608int
609SDL_AddVideoDisplay(const SDL_VideoDisplay * display, SDL_bool send_event)
610{
611 SDL_VideoDisplay *displays;
612 int index = -1;
613
614 displays =
615 SDL_realloc(_this->displays,
616 (_this->num_displays + 1) * sizeof(*displays));
617 if (displays) {
618 index = _this->num_displays++;
619 displays[index] = *display;
620 displays[index].device = _this;
621 _this->displays = displays;
622
623 if (display->name) {
624 displays[index].name = SDL_strdup(display->name);
625 } else {
626 char name[32];
627
628 SDL_itoa(index, name, 10);
629 displays[index].name = SDL_strdup(name);
630 }
631
632 if (send_event) {
633 SDL_SendDisplayEvent(&_this->displays[index], SDL_DISPLAYEVENT_CONNECTED, 0);
634 }
635 } else {
636 SDL_OutOfMemory();
637 }
638 return index;
639}
640
641void
642SDL_DelVideoDisplay(int index)
643{
644 if (index < 0 || index >= _this->num_displays) {
645 return;
646 }
647
648 SDL_SendDisplayEvent(&_this->displays[index], SDL_DISPLAYEVENT_DISCONNECTED, 0);
649
650 if (index < (_this->num_displays - 1)) {
651 SDL_memmove(&_this->displays[index], &_this->displays[index+1], (_this->num_displays - index - 1)*sizeof(_this->displays[index]));
652 }
653 --_this->num_displays;
654}
655
656int
657SDL_GetNumVideoDisplays(void)
658{
659 if (!_this) {
660 SDL_UninitializedVideo();
661 return 0;
662 }
663 return _this->num_displays;
664}
665
666int
667SDL_GetIndexOfDisplay(SDL_VideoDisplay *display)
668{
669 int displayIndex;
670
671 for (displayIndex = 0; displayIndex < _this->num_displays; ++displayIndex) {
672 if (display == &_this->displays[displayIndex]) {
673 return displayIndex;
674 }
675 }
676
677 /* Couldn't find the display, just use index 0 */
678 return 0;
679}
680
681void *
682SDL_GetDisplayDriverData(int displayIndex)
683{
684 CHECK_DISPLAY_INDEX(displayIndex, NULL);
685
686 return _this->displays[displayIndex].driverdata;
687}
688
689SDL_bool
690SDL_IsVideoContextExternal(void)
691{
692 return SDL_GetHintBoolean(SDL_HINT_VIDEO_EXTERNAL_CONTEXT, SDL_FALSE);
693}
694
695const char *
696SDL_GetDisplayName(int displayIndex)
697{
698 CHECK_DISPLAY_INDEX(displayIndex, NULL);
699
700 return _this->displays[displayIndex].name;
701}
702
703int
704SDL_GetDisplayBounds(int displayIndex, SDL_Rect * rect)
705{
706 CHECK_DISPLAY_INDEX(displayIndex, -1);
707
708 if (rect) {
709 SDL_VideoDisplay *display = &_this->displays[displayIndex];
710
711 if (_this->GetDisplayBounds) {
712 if (_this->GetDisplayBounds(_this, display, rect) == 0) {
713 return 0;
714 }
715 }
716
717 /* Assume that the displays are left to right */
718 if (displayIndex == 0) {
719 rect->x = 0;
720 rect->y = 0;
721 } else {
722 SDL_GetDisplayBounds(displayIndex-1, rect);
723 rect->x += rect->w;
724 }
725 rect->w = display->current_mode.w;
726 rect->h = display->current_mode.h;
727 }
728 return 0; /* !!! FIXME: should this be an error if (rect==NULL) ? */
729}
730
731static int
732ParseDisplayUsableBoundsHint(SDL_Rect *rect)
733{
734 const char *hint = SDL_GetHint(SDL_HINT_DISPLAY_USABLE_BOUNDS);
735 return hint && (SDL_sscanf(hint, "%d,%d,%d,%d", &rect->x, &rect->y, &rect->w, &rect->h) == 4);
736}
737
738int
739SDL_GetDisplayUsableBounds(int displayIndex, SDL_Rect * rect)
740{
741 CHECK_DISPLAY_INDEX(displayIndex, -1);
742
743 if (rect) {
744 SDL_VideoDisplay *display = &_this->displays[displayIndex];
745
746 if ((displayIndex == 0) && ParseDisplayUsableBoundsHint(rect)) {
747 return 0;
748 }
749
750 if (_this->GetDisplayUsableBounds) {
751 if (_this->GetDisplayUsableBounds(_this, display, rect) == 0) {
752 return 0;
753 }
754 }
755
756 /* Oh well, just give the entire display bounds. */
757 return SDL_GetDisplayBounds(displayIndex, rect);
758 }
759 return 0; /* !!! FIXME: should this be an error if (rect==NULL) ? */
760}
761
762int
763SDL_GetDisplayDPI(int displayIndex, float * ddpi, float * hdpi, float * vdpi)
764{
765 SDL_VideoDisplay *display;
766
767 CHECK_DISPLAY_INDEX(displayIndex, -1);
768
769 display = &_this->displays[displayIndex];
770
771 if (_this->GetDisplayDPI) {
772 if (_this->GetDisplayDPI(_this, display, ddpi, hdpi, vdpi) == 0) {
773 return 0;
774 }
775 } else {
776 return SDL_Unsupported();
777 }
778
779 return -1;
780}
781
782SDL_DisplayOrientation
783SDL_GetDisplayOrientation(int displayIndex)
784{
785 SDL_VideoDisplay *display;
786
787 CHECK_DISPLAY_INDEX(displayIndex, SDL_ORIENTATION_UNKNOWN);
788
789 display = &_this->displays[displayIndex];
790 return display->orientation;
791}
792
793SDL_bool
794SDL_AddDisplayMode(SDL_VideoDisplay * display, const SDL_DisplayMode * mode)
795{
796 SDL_DisplayMode *modes;
797 int i, nmodes;
798
799 /* Make sure we don't already have the mode in the list */
800 modes = display->display_modes;
801 nmodes = display->num_display_modes;
802 for (i = 0; i < nmodes; ++i) {
803 if (cmpmodes(mode, &modes[i]) == 0) {
804 return SDL_FALSE;
805 }
806 }
807
808 /* Go ahead and add the new mode */
809 if (nmodes == display->max_display_modes) {
810 modes =
811 SDL_realloc(modes,
812 (display->max_display_modes + 32) * sizeof(*modes));
813 if (!modes) {
814 return SDL_FALSE;
815 }
816 display->display_modes = modes;
817 display->max_display_modes += 32;
818 }
819 modes[nmodes] = *mode;
820 display->num_display_modes++;
821
822 /* Re-sort video modes */
823 SDL_qsort(display->display_modes, display->num_display_modes,
824 sizeof(SDL_DisplayMode), cmpmodes);
825
826 return SDL_TRUE;
827}
828
829static int
830SDL_GetNumDisplayModesForDisplay(SDL_VideoDisplay * display)
831{
832 if (!display->num_display_modes && _this->GetDisplayModes) {
833 _this->GetDisplayModes(_this, display);
834 SDL_qsort(display->display_modes, display->num_display_modes,
835 sizeof(SDL_DisplayMode), cmpmodes);
836 }
837 return display->num_display_modes;
838}
839
840int
841SDL_GetNumDisplayModes(int displayIndex)
842{
843 CHECK_DISPLAY_INDEX(displayIndex, -1);
844
845 return SDL_GetNumDisplayModesForDisplay(&_this->displays[displayIndex]);
846}
847
848int
849SDL_GetDisplayMode(int displayIndex, int index, SDL_DisplayMode * mode)
850{
851 SDL_VideoDisplay *display;
852
853 CHECK_DISPLAY_INDEX(displayIndex, -1);
854
855 display = &_this->displays[displayIndex];
856 if (index < 0 || index >= SDL_GetNumDisplayModesForDisplay(display)) {
857 return SDL_SetError("index must be in the range of 0 - %d",
858 SDL_GetNumDisplayModesForDisplay(display) - 1);
859 }
860 if (mode) {
861 *mode = display->display_modes[index];
862 }
863 return 0;
864}
865
866int
867SDL_GetDesktopDisplayMode(int displayIndex, SDL_DisplayMode * mode)
868{
869 SDL_VideoDisplay *display;
870
871 CHECK_DISPLAY_INDEX(displayIndex, -1);
872
873 display = &_this->displays[displayIndex];
874 if (mode) {
875 *mode = display->desktop_mode;
876 }
877 return 0;
878}
879
880int
881SDL_GetCurrentDisplayMode(int displayIndex, SDL_DisplayMode * mode)
882{
883 SDL_VideoDisplay *display;
884
885 CHECK_DISPLAY_INDEX(displayIndex, -1);
886
887 display = &_this->displays[displayIndex];
888 if (mode) {
889 *mode = display->current_mode;
890 }
891 return 0;
892}
893
894static SDL_DisplayMode *
895SDL_GetClosestDisplayModeForDisplay(SDL_VideoDisplay * display,
896 const SDL_DisplayMode * mode,
897 SDL_DisplayMode * closest)
898{
899 Uint32 target_format;
900 int target_refresh_rate;
901 int i;
902 SDL_DisplayMode *current, *match;
903
904 if (!mode || !closest) {
905 SDL_SetError("Missing desired mode or closest mode parameter");
906 return NULL;
907 }
908
909 /* Default to the desktop format */
910 if (mode->format) {
911 target_format = mode->format;
912 } else {
913 target_format = display->desktop_mode.format;
914 }
915
916 /* Default to the desktop refresh rate */
917 if (mode->refresh_rate) {
918 target_refresh_rate = mode->refresh_rate;
919 } else {
920 target_refresh_rate = display->desktop_mode.refresh_rate;
921 }
922
923 match = NULL;
924 for (i = 0; i < SDL_GetNumDisplayModesForDisplay(display); ++i) {
925 current = &display->display_modes[i];
926
927 if (current->w && (current->w < mode->w)) {
928 /* Out of sorted modes large enough here */
929 break;
930 }
931 if (current->h && (current->h < mode->h)) {
932 if (current->w && (current->w == mode->w)) {
933 /* Out of sorted modes large enough here */
934 break;
935 }
936 /* Wider, but not tall enough, due to a different
937 aspect ratio. This mode must be skipped, but closer
938 modes may still follow. */
939 continue;
940 }
941 if (!match || current->w < match->w || current->h < match->h) {
942 match = current;
943 continue;
944 }
945 if (current->format != match->format) {
946 /* Sorted highest depth to lowest */
947 if (current->format == target_format ||
948 (SDL_BITSPERPIXEL(current->format) >=
949 SDL_BITSPERPIXEL(target_format)
950 && SDL_PIXELTYPE(current->format) ==
951 SDL_PIXELTYPE(target_format))) {
952 match = current;
953 }
954 continue;
955 }
956 if (current->refresh_rate != match->refresh_rate) {
957 /* Sorted highest refresh to lowest */
958 if (current->refresh_rate >= target_refresh_rate) {
959 match = current;
960 }
961 }
962 }
963 if (match) {
964 if (match->format) {
965 closest->format = match->format;
966 } else {
967 closest->format = mode->format;
968 }
969 if (match->w && match->h) {
970 closest->w = match->w;
971 closest->h = match->h;
972 } else {
973 closest->w = mode->w;
974 closest->h = mode->h;
975 }
976 if (match->refresh_rate) {
977 closest->refresh_rate = match->refresh_rate;
978 } else {
979 closest->refresh_rate = mode->refresh_rate;
980 }
981 closest->driverdata = match->driverdata;
982
983 /*
984 * Pick some reasonable defaults if the app and driver don't
985 * care
986 */
987 if (!closest->format) {
988 closest->format = SDL_PIXELFORMAT_RGB888;
989 }
990 if (!closest->w) {
991 closest->w = 640;
992 }
993 if (!closest->h) {
994 closest->h = 480;
995 }
996 return closest;
997 }
998 return NULL;
999}
1000
1001SDL_DisplayMode *
1002SDL_GetClosestDisplayMode(int displayIndex,
1003 const SDL_DisplayMode * mode,
1004 SDL_DisplayMode * closest)
1005{
1006 SDL_VideoDisplay *display;
1007
1008 CHECK_DISPLAY_INDEX(displayIndex, NULL);
1009
1010 display = &_this->displays[displayIndex];
1011 return SDL_GetClosestDisplayModeForDisplay(display, mode, closest);
1012}
1013
1014static int
1015SDL_SetDisplayModeForDisplay(SDL_VideoDisplay * display, const SDL_DisplayMode * mode)
1016{
1017 SDL_DisplayMode display_mode;
1018 SDL_DisplayMode current_mode;
1019
1020 if (mode) {
1021 display_mode = *mode;
1022
1023 /* Default to the current mode */
1024 if (!display_mode.format) {
1025 display_mode.format = display->current_mode.format;
1026 }
1027 if (!display_mode.w) {
1028 display_mode.w = display->current_mode.w;
1029 }
1030 if (!display_mode.h) {
1031 display_mode.h = display->current_mode.h;
1032 }
1033 if (!display_mode.refresh_rate) {
1034 display_mode.refresh_rate = display->current_mode.refresh_rate;
1035 }
1036
1037 /* Get a good video mode, the closest one possible */
1038 if (!SDL_GetClosestDisplayModeForDisplay(display, &display_mode, &display_mode)) {
1039 return SDL_SetError("No video mode large enough for %dx%d",
1040 display_mode.w, display_mode.h);
1041 }
1042 } else {
1043 display_mode = display->desktop_mode;
1044 }
1045
1046 /* See if there's anything left to do */
1047 current_mode = display->current_mode;
1048 if (SDL_memcmp(&display_mode, &current_mode, sizeof(display_mode)) == 0) {
1049 return 0;
1050 }
1051
1052 /* Actually change the display mode */
1053 if (!_this->SetDisplayMode) {
1054 return SDL_SetError("SDL video driver doesn't support changing display mode");
1055 }
1056 if (_this->SetDisplayMode(_this, display, &display_mode) < 0) {
1057 return -1;
1058 }
1059 display->current_mode = display_mode;
1060 return 0;
1061}
1062
1063SDL_VideoDisplay *
1064SDL_GetDisplay(int displayIndex)
1065{
1066 CHECK_DISPLAY_INDEX(displayIndex, NULL);
1067
1068 return &_this->displays[displayIndex];
1069}
1070
1071int
1072SDL_GetWindowDisplayIndex(SDL_Window * window)
1073{
1074 int displayIndex;
1075 int i, dist;
1076 int closest = -1;
1077 int closest_dist = 0x7FFFFFFF;
1078 SDL_Point center;
1079 SDL_Point delta;
1080 SDL_Rect rect;
1081
1082 CHECK_WINDOW_MAGIC(window, -1);
1083
1084 if (SDL_WINDOWPOS_ISUNDEFINED(window->x) ||
1085 SDL_WINDOWPOS_ISCENTERED(window->x)) {
1086 displayIndex = (window->x & 0xFFFF);
1087 if (displayIndex >= _this->num_displays) {
1088 displayIndex = 0;
1089 }
1090 return displayIndex;
1091 }
1092 if (SDL_WINDOWPOS_ISUNDEFINED(window->y) ||
1093 SDL_WINDOWPOS_ISCENTERED(window->y)) {
1094 displayIndex = (window->y & 0xFFFF);
1095 if (displayIndex >= _this->num_displays) {
1096 displayIndex = 0;
1097 }
1098 return displayIndex;
1099 }
1100
1101 /* Find the display containing the window */
1102 for (i = 0; i < _this->num_displays; ++i) {
1103 SDL_VideoDisplay *display = &_this->displays[i];
1104
1105 if (display->fullscreen_window == window) {
1106 return i;
1107 }
1108 }
1109 center.x = window->x + window->w / 2;
1110 center.y = window->y + window->h / 2;
1111 for (i = 0; i < _this->num_displays; ++i) {
1112 SDL_GetDisplayBounds(i, &rect);
1113 if (SDL_EnclosePoints(&center, 1, &rect, NULL)) {
1114 return i;
1115 }
1116
1117 delta.x = center.x - (rect.x + rect.w / 2);
1118 delta.y = center.y - (rect.y + rect.h / 2);
1119 dist = (delta.x*delta.x + delta.y*delta.y);
1120 if (dist < closest_dist) {
1121 closest = i;
1122 closest_dist = dist;
1123 }
1124 }
1125 if (closest < 0) {
1126 SDL_SetError("Couldn't find any displays");
1127 }
1128 return closest;
1129}
1130
1131SDL_VideoDisplay *
1132SDL_GetDisplayForWindow(SDL_Window *window)
1133{
1134 int displayIndex = SDL_GetWindowDisplayIndex(window);
1135 if (displayIndex >= 0) {
1136 return &_this->displays[displayIndex];
1137 } else {
1138 return NULL;
1139 }
1140}
1141
1142int
1143SDL_SetWindowDisplayMode(SDL_Window * window, const SDL_DisplayMode * mode)
1144{
1145 CHECK_WINDOW_MAGIC(window, -1);
1146
1147 if (mode) {
1148 window->fullscreen_mode = *mode;
1149 } else {
1150 SDL_zero(window->fullscreen_mode);
1151 }
1152
1153 if (FULLSCREEN_VISIBLE(window) && (window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP) {
1154 SDL_DisplayMode fullscreen_mode;
1155 if (SDL_GetWindowDisplayMode(window, &fullscreen_mode) == 0) {
1156 SDL_SetDisplayModeForDisplay(SDL_GetDisplayForWindow(window), &fullscreen_mode);
1157 }
1158 }
1159 return 0;
1160}
1161
1162int
1163SDL_GetWindowDisplayMode(SDL_Window * window, SDL_DisplayMode * mode)
1164{
1165 SDL_DisplayMode fullscreen_mode;
1166 SDL_VideoDisplay *display;
1167
1168 CHECK_WINDOW_MAGIC(window, -1);
1169
1170 if (!mode) {
1171 return SDL_InvalidParamError("mode");
1172 }
1173
1174 fullscreen_mode = window->fullscreen_mode;
1175 if (!fullscreen_mode.w) {
1176 fullscreen_mode.w = window->windowed.w;
1177 }
1178 if (!fullscreen_mode.h) {
1179 fullscreen_mode.h = window->windowed.h;
1180 }
1181
1182 display = SDL_GetDisplayForWindow(window);
1183
1184 /* if in desktop size mode, just return the size of the desktop */
1185 if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
1186 fullscreen_mode = display->desktop_mode;
1187 } else if (!SDL_GetClosestDisplayModeForDisplay(SDL_GetDisplayForWindow(window),
1188 &fullscreen_mode,
1189 &fullscreen_mode)) {
1190 return SDL_SetError("Couldn't find display mode match");
1191 }
1192
1193 if (mode) {
1194 *mode = fullscreen_mode;
1195 }
1196 return 0;
1197}
1198
1199Uint32
1200SDL_GetWindowPixelFormat(SDL_Window * window)
1201{
1202 SDL_VideoDisplay *display;
1203
1204 CHECK_WINDOW_MAGIC(window, SDL_PIXELFORMAT_UNKNOWN);
1205
1206 display = SDL_GetDisplayForWindow(window);
1207 return display->current_mode.format;
1208}
1209
1210static void
1211SDL_RestoreMousePosition(SDL_Window *window)
1212{
1213 int x, y;
1214
1215 if (window == SDL_GetMouseFocus()) {
1216 SDL_GetMouseState(&x, &y);
1217 SDL_WarpMouseInWindow(window, x, y);
1218 }
1219}
1220
1221#if __WINRT__
1222extern Uint32 WINRT_DetectWindowFlags(SDL_Window * window);
1223#endif
1224
1225static int
1226SDL_UpdateFullscreenMode(SDL_Window * window, SDL_bool fullscreen)
1227{
1228 SDL_VideoDisplay *display;
1229 SDL_Window *other;
1230
1231 CHECK_WINDOW_MAGIC(window,-1);
1232
1233 /* if we are in the process of hiding don't go back to fullscreen */
1234 if (window->is_hiding && fullscreen) {
1235 return 0;
1236 }
1237
1238#ifdef __MACOSX__
1239 /* if the window is going away and no resolution change is necessary,
1240 do nothing, or else we may trigger an ugly double-transition
1241 */
1242 if (SDL_strcmp(_this->name, "cocoa") == 0) { /* don't do this for X11, etc */
1243 if (window->is_destroying && (window->last_fullscreen_flags & FULLSCREEN_MASK) == SDL_WINDOW_FULLSCREEN_DESKTOP)
1244 return 0;
1245
1246 /* If we're switching between a fullscreen Space and "normal" fullscreen, we need to get back to normal first. */
1247 if (fullscreen && ((window->last_fullscreen_flags & FULLSCREEN_MASK) == SDL_WINDOW_FULLSCREEN_DESKTOP) && ((window->flags & FULLSCREEN_MASK) == SDL_WINDOW_FULLSCREEN)) {
1248 if (!Cocoa_SetWindowFullscreenSpace(window, SDL_FALSE)) {
1249 return -1;
1250 }
1251 } else if (fullscreen && ((window->last_fullscreen_flags & FULLSCREEN_MASK) == SDL_WINDOW_FULLSCREEN) && ((window->flags & FULLSCREEN_MASK) == SDL_WINDOW_FULLSCREEN_DESKTOP)) {
1252 display = SDL_GetDisplayForWindow(window);
1253 SDL_SetDisplayModeForDisplay(display, NULL);
1254 if (_this->SetWindowFullscreen) {
1255 _this->SetWindowFullscreen(_this, window, display, SDL_FALSE);
1256 }
1257 }
1258
1259 if (Cocoa_SetWindowFullscreenSpace(window, fullscreen)) {
1260 if (Cocoa_IsWindowInFullscreenSpace(window) != fullscreen) {
1261 return -1;
1262 }
1263 window->last_fullscreen_flags = window->flags;
1264 return 0;
1265 }
1266 }
1267#elif __WINRT__ && (NTDDI_VERSION < NTDDI_WIN10)
1268 /* HACK: WinRT 8.x apps can't choose whether or not they are fullscreen
1269 or not. The user can choose this, via OS-provided UI, but this can't
1270 be set programmatically.
1271
1272 Just look at what SDL's WinRT video backend code detected with regards
1273 to fullscreen (being active, or not), and figure out a return/error code
1274 from that.
1275 */
1276 if (fullscreen == !(WINRT_DetectWindowFlags(window) & FULLSCREEN_MASK)) {
1277 /* Uh oh, either:
1278 1. fullscreen was requested, and we're already windowed
1279 2. windowed-mode was requested, and we're already fullscreen
1280
1281 WinRT 8.x can't resolve either programmatically, so we're
1282 giving up.
1283 */
1284 return -1;
1285 } else {
1286 /* Whatever was requested, fullscreen or windowed mode, is already
1287 in-place.
1288 */
1289 return 0;
1290 }
1291#endif
1292
1293 display = SDL_GetDisplayForWindow(window);
1294
1295 if (fullscreen) {
1296 /* Hide any other fullscreen windows */
1297 if (display->fullscreen_window &&
1298 display->fullscreen_window != window) {
1299 SDL_MinimizeWindow(display->fullscreen_window);
1300 }
1301 }
1302
1303 /* See if anything needs to be done now */
1304 if ((display->fullscreen_window == window) == fullscreen) {
1305 if ((window->last_fullscreen_flags & FULLSCREEN_MASK) == (window->flags & FULLSCREEN_MASK)) {
1306 return 0;
1307 }
1308 }
1309
1310 /* See if there are any fullscreen windows */
1311 for (other = _this->windows; other; other = other->next) {
1312 SDL_bool setDisplayMode = SDL_FALSE;
1313
1314 if (other == window) {
1315 setDisplayMode = fullscreen;
1316 } else if (FULLSCREEN_VISIBLE(other) &&
1317 SDL_GetDisplayForWindow(other) == display) {
1318 setDisplayMode = SDL_TRUE;
1319 }
1320
1321 if (setDisplayMode) {
1322 SDL_DisplayMode fullscreen_mode;
1323
1324 SDL_zero(fullscreen_mode);
1325
1326 if (SDL_GetWindowDisplayMode(other, &fullscreen_mode) == 0) {
1327 SDL_bool resized = SDL_TRUE;
1328
1329 if (other->w == fullscreen_mode.w && other->h == fullscreen_mode.h) {
1330 resized = SDL_FALSE;
1331 }
1332
1333 /* only do the mode change if we want exclusive fullscreen */
1334 if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP) {
1335 if (SDL_SetDisplayModeForDisplay(display, &fullscreen_mode) < 0) {
1336 return -1;
1337 }
1338 } else {
1339 if (SDL_SetDisplayModeForDisplay(display, NULL) < 0) {
1340 return -1;
1341 }
1342 }
1343
1344 if (_this->SetWindowFullscreen) {
1345 _this->SetWindowFullscreen(_this, other, display, SDL_TRUE);
1346 }
1347 display->fullscreen_window = other;
1348
1349 /* Generate a mode change event here */
1350 if (resized) {
1351#ifndef ANDROID
1352 // Android may not resize the window to exactly what our fullscreen mode is, especially on
1353 // windowed Android environments like the Chromebook or Samsung DeX. Given this, we shouldn't
1354 // use fullscreen_mode.w and fullscreen_mode.h, but rather get our current native size. As such,
1355 // Android's SetWindowFullscreen will generate the window event for us with the proper final size.
1356
1357 SDL_SendWindowEvent(other, SDL_WINDOWEVENT_RESIZED,
1358 fullscreen_mode.w, fullscreen_mode.h);
1359#endif
1360 } else {
1361 SDL_OnWindowResized(other);
1362 }
1363
1364 SDL_RestoreMousePosition(other);
1365
1366 window->last_fullscreen_flags = window->flags;
1367 return 0;
1368 }
1369 }
1370 }
1371
1372 /* Nope, restore the desktop mode */
1373 SDL_SetDisplayModeForDisplay(display, NULL);
1374
1375 if (_this->SetWindowFullscreen) {
1376 _this->SetWindowFullscreen(_this, window, display, SDL_FALSE);
1377 }
1378 display->fullscreen_window = NULL;
1379
1380 /* Generate a mode change event here */
1381 SDL_OnWindowResized(window);
1382
1383 /* Restore the cursor position */
1384 SDL_RestoreMousePosition(window);
1385
1386 window->last_fullscreen_flags = window->flags;
1387 return 0;
1388}
1389
1390#define CREATE_FLAGS \
1391 (SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_SKIP_TASKBAR | SDL_WINDOW_POPUP_MENU | SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_VULKAN | SDL_WINDOW_MINIMIZED | SDL_WINDOW_METAL)
1392
1393static SDL_INLINE SDL_bool
1394IsAcceptingDragAndDrop(void)
1395{
1396 if ((SDL_GetEventState(SDL_DROPFILE) == SDL_ENABLE) ||
1397 (SDL_GetEventState(SDL_DROPTEXT) == SDL_ENABLE)) {
1398 return SDL_TRUE;
1399 }
1400 return SDL_FALSE;
1401}
1402
1403/* prepare a newly-created window */
1404static SDL_INLINE void
1405PrepareDragAndDropSupport(SDL_Window *window)
1406{
1407 if (_this->AcceptDragAndDrop) {
1408 _this->AcceptDragAndDrop(window, IsAcceptingDragAndDrop());
1409 }
1410}
1411
1412/* toggle d'n'd for all existing windows. */
1413void
1414SDL_ToggleDragAndDropSupport(void)
1415{
1416 if (_this && _this->AcceptDragAndDrop) {
1417 const SDL_bool enable = IsAcceptingDragAndDrop();
1418 SDL_Window *window;
1419 for (window = _this->windows; window; window = window->next) {
1420 _this->AcceptDragAndDrop(window, enable);
1421 }
1422 }
1423}
1424
1425static void
1426SDL_FinishWindowCreation(SDL_Window *window, Uint32 flags)
1427{
1428 PrepareDragAndDropSupport(window);
1429
1430 if (flags & SDL_WINDOW_MAXIMIZED) {
1431 SDL_MaximizeWindow(window);
1432 }
1433 if (flags & SDL_WINDOW_MINIMIZED) {
1434 SDL_MinimizeWindow(window);
1435 }
1436 if (flags & SDL_WINDOW_FULLSCREEN) {
1437 SDL_SetWindowFullscreen(window, flags);
1438 }
1439 if (flags & SDL_WINDOW_MOUSE_GRABBED) {
1440 /* We must specifically call SDL_SetWindowGrab() and not
1441 SDL_SetWindowMouseGrab() here because older applications may use
1442 this flag plus SDL_HINT_GRAB_KEYBOARD to indicate that they want
1443 the keyboard grabbed too and SDL_SetWindowMouseGrab() won't do that.
1444 */
1445 SDL_SetWindowGrab(window, SDL_TRUE);
1446 }
1447 if (flags & SDL_WINDOW_KEYBOARD_GRABBED) {
1448 SDL_SetWindowKeyboardGrab(window, SDL_TRUE);
1449 }
1450 if (!(flags & SDL_WINDOW_HIDDEN)) {
1451 SDL_ShowWindow(window);
1452 }
1453}
1454
1455SDL_Window *
1456SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags)
1457{
1458 SDL_Window *window;
1459 Uint32 graphics_flags = flags & (SDL_WINDOW_OPENGL | SDL_WINDOW_METAL | SDL_WINDOW_VULKAN);
1460
1461 if (!_this) {
1462 /* Initialize the video system if needed */
1463 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
1464 return NULL;
1465 }
1466 }
1467
1468 if ((((flags & SDL_WINDOW_UTILITY) != 0) + ((flags & SDL_WINDOW_TOOLTIP) != 0) + ((flags & SDL_WINDOW_POPUP_MENU) != 0)) > 1) {
1469 SDL_SetError("Conflicting window flags specified");
1470 return NULL;
1471 }
1472
1473 /* Some platforms can't create zero-sized windows */
1474 if (w < 1) {
1475 w = 1;
1476 }
1477 if (h < 1) {
1478 h = 1;
1479 }
1480
1481 /* Some platforms blow up if the windows are too large. Raise it later? */
1482 if ((w > 16384) || (h > 16384)) {
1483 SDL_SetError("Window is too large.");
1484 return NULL;
1485 }
1486
1487 /* Some platforms have certain graphics backends enabled by default */
1488 if (!_this->is_dummy && !graphics_flags && !SDL_IsVideoContextExternal()) {
1489#if (SDL_VIDEO_OPENGL && __MACOSX__) || (__IPHONEOS__ && !TARGET_OS_MACCATALYST) || __ANDROID__ || __NACL__
1490 flags |= SDL_WINDOW_OPENGL;
1491#endif
1492#if SDL_VIDEO_METAL && (TARGET_OS_MACCATALYST || __MACOSX__ || __IPHONEOS__)
1493 flags |= SDL_WINDOW_METAL;
1494#endif
1495 }
1496
1497 if (flags & SDL_WINDOW_OPENGL) {
1498 if (!_this->GL_CreateContext) {
1499 SDL_SetError("OpenGL support is either not configured in SDL "
1500 "or not available in current SDL video driver "
1501 "(%s) or platform", _this->name);
1502 return NULL;
1503 }
1504 if (SDL_GL_LoadLibrary(NULL) < 0) {
1505 return NULL;
1506 }
1507 }
1508
1509 if (flags & SDL_WINDOW_VULKAN) {
1510 if (!_this->Vulkan_CreateSurface) {
1511 SDL_SetError("Vulkan support is either not configured in SDL "
1512 "or not available in current SDL video driver "
1513 "(%s) or platform", _this->name);
1514 return NULL;
1515 }
1516 if (graphics_flags & SDL_WINDOW_OPENGL) {
1517 SDL_SetError("Vulkan and OpenGL not supported on same window");
1518 return NULL;
1519 }
1520 if (SDL_Vulkan_LoadLibrary(NULL) < 0) {
1521 return NULL;
1522 }
1523 }
1524
1525 if (flags & SDL_WINDOW_METAL) {
1526 if (!_this->Metal_CreateView) {
1527 SDL_SetError("Metal support is either not configured in SDL "
1528 "or not available in current SDL video driver "
1529 "(%s) or platform", _this->name);
1530 return NULL;
1531 }
1532 /* 'flags' may have default flags appended, don't check against that. */
1533 if (graphics_flags & SDL_WINDOW_OPENGL) {
1534 SDL_SetError("Metal and OpenGL not supported on same window");
1535 return NULL;
1536 }
1537 if (graphics_flags & SDL_WINDOW_VULKAN) {
1538 SDL_SetError("Metal and Vulkan not supported on same window. "
1539 "To use MoltenVK, set SDL_WINDOW_VULKAN only.");
1540 return NULL;
1541 }
1542 }
1543
1544 /* Unless the user has specified the high-DPI disabling hint, respect the
1545 * SDL_WINDOW_ALLOW_HIGHDPI flag.
1546 */
1547 if (flags & SDL_WINDOW_ALLOW_HIGHDPI) {
1548 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_HIGHDPI_DISABLED, SDL_FALSE)) {
1549 flags &= ~SDL_WINDOW_ALLOW_HIGHDPI;
1550 }
1551 }
1552
1553 window = (SDL_Window *)SDL_calloc(1, sizeof(*window));
1554 if (!window) {
1555 SDL_OutOfMemory();
1556 return NULL;
1557 }
1558 window->magic = &_this->window_magic;
1559 window->id = _this->next_object_id++;
1560 window->x = x;
1561 window->y = y;
1562 window->w = w;
1563 window->h = h;
1564 if (SDL_WINDOWPOS_ISUNDEFINED(x) || SDL_WINDOWPOS_ISUNDEFINED(y) ||
1565 SDL_WINDOWPOS_ISCENTERED(x) || SDL_WINDOWPOS_ISCENTERED(y)) {
1566 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
1567 int displayIndex;
1568 SDL_Rect bounds;
1569
1570 displayIndex = SDL_GetIndexOfDisplay(display);
1571 SDL_GetDisplayBounds(displayIndex, &bounds);
1572 if (SDL_WINDOWPOS_ISUNDEFINED(x) || SDL_WINDOWPOS_ISCENTERED(x)) {
1573 window->x = bounds.x + (bounds.w - w) / 2;
1574 }
1575 if (SDL_WINDOWPOS_ISUNDEFINED(y) || SDL_WINDOWPOS_ISCENTERED(y)) {
1576 window->y = bounds.y + (bounds.h - h) / 2;
1577 }
1578 }
1579 window->windowed.x = window->x;
1580 window->windowed.y = window->y;
1581 window->windowed.w = window->w;
1582 window->windowed.h = window->h;
1583
1584 if (flags & SDL_WINDOW_FULLSCREEN) {
1585 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
1586 int displayIndex;
1587 SDL_Rect bounds;
1588
1589 displayIndex = SDL_GetIndexOfDisplay(display);
1590 SDL_GetDisplayBounds(displayIndex, &bounds);
1591
1592 window->x = bounds.x;
1593 window->y = bounds.y;
1594 window->w = bounds.w;
1595 window->h = bounds.h;
1596 }
1597
1598 window->flags = ((flags & CREATE_FLAGS) | SDL_WINDOW_HIDDEN);
1599 window->last_fullscreen_flags = window->flags;
1600 window->opacity = 1.0f;
1601 window->brightness = 1.0f;
1602 window->next = _this->windows;
1603 window->is_destroying = SDL_FALSE;
1604
1605 if (_this->windows) {
1606 _this->windows->prev = window;
1607 }
1608 _this->windows = window;
1609
1610 if (_this->CreateSDLWindow && _this->CreateSDLWindow(_this, window) < 0) {
1611 SDL_DestroyWindow(window);
1612 return NULL;
1613 }
1614
1615 /* Clear minimized if not on windows, only windows handles it at create rather than FinishWindowCreation,
1616 * but it's important or window focus will get broken on windows!
1617 */
1618#if !defined(__WIN32__)
1619 if (window->flags & SDL_WINDOW_MINIMIZED) {
1620 window->flags &= ~SDL_WINDOW_MINIMIZED;
1621 }
1622#endif
1623
1624#if __WINRT__ && (NTDDI_VERSION < NTDDI_WIN10)
1625 /* HACK: WinRT 8.x apps can't choose whether or not they are fullscreen
1626 or not. The user can choose this, via OS-provided UI, but this can't
1627 be set programmatically.
1628
1629 Just look at what SDL's WinRT video backend code detected with regards
1630 to fullscreen (being active, or not), and figure out a return/error code
1631 from that.
1632 */
1633 flags = window->flags;
1634#endif
1635
1636 if (title) {
1637 SDL_SetWindowTitle(window, title);
1638 }
1639 SDL_FinishWindowCreation(window, flags);
1640
1641 /* If the window was created fullscreen, make sure the mode code matches */
1642 SDL_UpdateFullscreenMode(window, FULLSCREEN_VISIBLE(window));
1643
1644 return window;
1645}
1646
1647SDL_Window *
1648SDL_CreateWindowFrom(const void *data)
1649{
1650 SDL_Window *window;
1651
1652 if (!_this) {
1653 SDL_UninitializedVideo();
1654 return NULL;
1655 }
1656 if (!_this->CreateSDLWindowFrom) {
1657 SDL_Unsupported();
1658 return NULL;
1659 }
1660 window = (SDL_Window *)SDL_calloc(1, sizeof(*window));
1661 if (!window) {
1662 SDL_OutOfMemory();
1663 return NULL;
1664 }
1665 window->magic = &_this->window_magic;
1666 window->id = _this->next_object_id++;
1667 window->flags = SDL_WINDOW_FOREIGN;
1668 window->last_fullscreen_flags = window->flags;
1669 window->is_destroying = SDL_FALSE;
1670 window->opacity = 1.0f;
1671 window->brightness = 1.0f;
1672 window->next = _this->windows;
1673 if (_this->windows) {
1674 _this->windows->prev = window;
1675 }
1676 _this->windows = window;
1677
1678 if (_this->CreateSDLWindowFrom(_this, window, data) < 0) {
1679 SDL_DestroyWindow(window);
1680 return NULL;
1681 }
1682
1683 PrepareDragAndDropSupport(window);
1684
1685 return window;
1686}
1687
1688int
1689SDL_RecreateWindow(SDL_Window * window, Uint32 flags)
1690{
1691 SDL_bool loaded_opengl = SDL_FALSE;
1692 SDL_bool need_gl_unload = SDL_FALSE;
1693 SDL_bool need_gl_load = SDL_FALSE;
1694 SDL_bool loaded_vulkan = SDL_FALSE;
1695 SDL_bool need_vulkan_unload = SDL_FALSE;
1696 SDL_bool need_vulkan_load = SDL_FALSE;
1697
1698 if ((flags & SDL_WINDOW_OPENGL) && !_this->GL_CreateContext) {
1699 return SDL_SetError("OpenGL support is either not configured in SDL "
1700 "or not available in current SDL video driver "
1701 "(%s) or platform", _this->name);
1702 }
1703
1704 if (window->flags & SDL_WINDOW_FOREIGN) {
1705 /* Can't destroy and re-create foreign windows, hrm */
1706 flags |= SDL_WINDOW_FOREIGN;
1707 } else {
1708 flags &= ~SDL_WINDOW_FOREIGN;
1709 }
1710
1711 /* Restore video mode, etc. */
1712 SDL_HideWindow(window);
1713
1714 /* Tear down the old native window */
1715 if (window->surface) {
1716 window->surface->flags &= ~SDL_DONTFREE;
1717 SDL_FreeSurface(window->surface);
1718 window->surface = NULL;
1719 window->surface_valid = SDL_FALSE;
1720 }
1721 if (_this->DestroyWindowFramebuffer) {
1722 _this->DestroyWindowFramebuffer(_this, window);
1723 }
1724 if (_this->DestroyWindow && !(flags & SDL_WINDOW_FOREIGN)) {
1725 _this->DestroyWindow(_this, window);
1726 }
1727
1728 if ((window->flags & SDL_WINDOW_OPENGL) != (flags & SDL_WINDOW_OPENGL)) {
1729 if (flags & SDL_WINDOW_OPENGL) {
1730 need_gl_load = SDL_TRUE;
1731 } else {
1732 need_gl_unload = SDL_TRUE;
1733 }
1734 } else if (window->flags & SDL_WINDOW_OPENGL) {
1735 need_gl_unload = SDL_TRUE;
1736 need_gl_load = SDL_TRUE;
1737 }
1738
1739 if ((window->flags & SDL_WINDOW_METAL) != (flags & SDL_WINDOW_METAL)) {
1740 if (flags & SDL_WINDOW_METAL) {
1741 need_gl_load = SDL_TRUE;
1742 } else {
1743 need_gl_unload = SDL_TRUE;
1744 }
1745 } else if (window->flags & SDL_WINDOW_METAL) {
1746 need_gl_unload = SDL_TRUE;
1747 need_gl_load = SDL_TRUE;
1748 }
1749
1750 if ((window->flags & SDL_WINDOW_VULKAN) != (flags & SDL_WINDOW_VULKAN)) {
1751 if (flags & SDL_WINDOW_VULKAN) {
1752 need_vulkan_load = SDL_TRUE;
1753 } else {
1754 need_vulkan_unload = SDL_TRUE;
1755 }
1756 } else if (window->flags & SDL_WINDOW_VULKAN) {
1757 need_vulkan_unload = SDL_TRUE;
1758 need_vulkan_load = SDL_TRUE;
1759 }
1760
1761 if ((flags & SDL_WINDOW_VULKAN) && (flags & SDL_WINDOW_OPENGL)) {
1762 SDL_SetError("Vulkan and OpenGL not supported on same window");
1763 return -1;
1764 }
1765
1766 if ((flags & SDL_WINDOW_METAL) && (flags & SDL_WINDOW_OPENGL)) {
1767 SDL_SetError("Metal and OpenGL not supported on same window");
1768 return -1;
1769 }
1770
1771 if ((flags & SDL_WINDOW_METAL) && (flags & SDL_WINDOW_VULKAN)) {
1772 SDL_SetError("Metal and Vulkan not supported on same window");
1773 return -1;
1774 }
1775
1776 if (need_gl_unload) {
1777 SDL_GL_UnloadLibrary();
1778 }
1779
1780 if (need_vulkan_unload) {
1781 SDL_Vulkan_UnloadLibrary();
1782 }
1783
1784 if (need_gl_load) {
1785 if (SDL_GL_LoadLibrary(NULL) < 0) {
1786 return -1;
1787 }
1788 loaded_opengl = SDL_TRUE;
1789 }
1790
1791 if (need_vulkan_load) {
1792 if (SDL_Vulkan_LoadLibrary(NULL) < 0) {
1793 return -1;
1794 }
1795 loaded_vulkan = SDL_TRUE;
1796 }
1797
1798 window->flags = ((flags & CREATE_FLAGS) | SDL_WINDOW_HIDDEN);
1799 window->last_fullscreen_flags = window->flags;
1800 window->is_destroying = SDL_FALSE;
1801
1802 if (_this->CreateSDLWindow && !(flags & SDL_WINDOW_FOREIGN)) {
1803 if (_this->CreateSDLWindow(_this, window) < 0) {
1804 if (loaded_opengl) {
1805 SDL_GL_UnloadLibrary();
1806 window->flags &= ~SDL_WINDOW_OPENGL;
1807 }
1808 if (loaded_vulkan) {
1809 SDL_Vulkan_UnloadLibrary();
1810 window->flags &= ~SDL_WINDOW_VULKAN;
1811 }
1812 return -1;
1813 }
1814 }
1815
1816 if (flags & SDL_WINDOW_FOREIGN) {
1817 window->flags |= SDL_WINDOW_FOREIGN;
1818 }
1819
1820 if (_this->SetWindowTitle && window->title) {
1821 _this->SetWindowTitle(_this, window);
1822 }
1823
1824 if (_this->SetWindowIcon && window->icon) {
1825 _this->SetWindowIcon(_this, window, window->icon);
1826 }
1827
1828 if (window->hit_test) {
1829 _this->SetWindowHitTest(window, SDL_TRUE);
1830 }
1831
1832 SDL_FinishWindowCreation(window, flags);
1833
1834 return 0;
1835}
1836
1837SDL_bool
1838SDL_HasWindows(void)
1839{
1840 return (_this && _this->windows != NULL);
1841}
1842
1843Uint32
1844SDL_GetWindowID(SDL_Window * window)
1845{
1846 CHECK_WINDOW_MAGIC(window, 0);
1847
1848 return window->id;
1849}
1850
1851SDL_Window *
1852SDL_GetWindowFromID(Uint32 id)
1853{
1854 SDL_Window *window;
1855
1856 if (!_this) {
1857 return NULL;
1858 }
1859 for (window = _this->windows; window; window = window->next) {
1860 if (window->id == id) {
1861 return window;
1862 }
1863 }
1864 return NULL;
1865}
1866
1867Uint32
1868SDL_GetWindowFlags(SDL_Window * window)
1869{
1870 CHECK_WINDOW_MAGIC(window, 0);
1871
1872 return window->flags;
1873}
1874
1875void
1876SDL_SetWindowTitle(SDL_Window * window, const char *title)
1877{
1878 CHECK_WINDOW_MAGIC(window,);
1879
1880 if (title == window->title) {
1881 return;
1882 }
1883 SDL_free(window->title);
1884
1885 window->title = SDL_strdup(title ? title : "");
1886
1887 if (_this->SetWindowTitle) {
1888 _this->SetWindowTitle(_this, window);
1889 }
1890}
1891
1892const char *
1893SDL_GetWindowTitle(SDL_Window * window)
1894{
1895 CHECK_WINDOW_MAGIC(window, "");
1896
1897 return window->title ? window->title : "";
1898}
1899
1900void
1901SDL_SetWindowIcon(SDL_Window * window, SDL_Surface * icon)
1902{
1903 CHECK_WINDOW_MAGIC(window,);
1904
1905 if (!icon) {
1906 return;
1907 }
1908
1909 SDL_FreeSurface(window->icon);
1910
1911 /* Convert the icon into ARGB8888 */
1912 window->icon = SDL_ConvertSurfaceFormat(icon, SDL_PIXELFORMAT_ARGB8888, 0);
1913 if (!window->icon) {
1914 return;
1915 }
1916
1917 if (_this->SetWindowIcon) {
1918 _this->SetWindowIcon(_this, window, window->icon);
1919 }
1920}
1921
1922void*
1923SDL_SetWindowData(SDL_Window * window, const char *name, void *userdata)
1924{
1925 SDL_WindowUserData *prev, *data;
1926
1927 CHECK_WINDOW_MAGIC(window, NULL);
1928
1929 /* Input validation */
1930 if (name == NULL || name[0] == '\0') {
1931 SDL_InvalidParamError("name");
1932 return NULL;
1933 }
1934
1935 /* See if the named data already exists */
1936 prev = NULL;
1937 for (data = window->data; data; prev = data, data = data->next) {
1938 if (data->name && SDL_strcmp(data->name, name) == 0) {
1939 void *last_value = data->data;
1940
1941 if (userdata) {
1942 /* Set the new value */
1943 data->data = userdata;
1944 } else {
1945 /* Delete this value */
1946 if (prev) {
1947 prev->next = data->next;
1948 } else {
1949 window->data = data->next;
1950 }
1951 SDL_free(data->name);
1952 SDL_free(data);
1953 }
1954 return last_value;
1955 }
1956 }
1957
1958 /* Add new data to the window */
1959 if (userdata) {
1960 data = (SDL_WindowUserData *)SDL_malloc(sizeof(*data));
1961 data->name = SDL_strdup(name);
1962 data->data = userdata;
1963 data->next = window->data;
1964 window->data = data;
1965 }
1966 return NULL;
1967}
1968
1969void *
1970SDL_GetWindowData(SDL_Window * window, const char *name)
1971{
1972 SDL_WindowUserData *data;
1973
1974 CHECK_WINDOW_MAGIC(window, NULL);
1975
1976 /* Input validation */
1977 if (name == NULL || name[0] == '\0') {
1978 SDL_InvalidParamError("name");
1979 return NULL;
1980 }
1981
1982 for (data = window->data; data; data = data->next) {
1983 if (data->name && SDL_strcmp(data->name, name) == 0) {
1984 return data->data;
1985 }
1986 }
1987 return NULL;
1988}
1989
1990void
1991SDL_SetWindowPosition(SDL_Window * window, int x, int y)
1992{
1993 CHECK_WINDOW_MAGIC(window,);
1994
1995 if (SDL_WINDOWPOS_ISCENTERED(x) || SDL_WINDOWPOS_ISCENTERED(y)) {
1996 int displayIndex = (x & 0xFFFF);
1997 SDL_Rect bounds;
1998 if (displayIndex >= _this->num_displays) {
1999 displayIndex = 0;
2000 }
2001
2002 SDL_zero(bounds);
2003
2004 SDL_GetDisplayBounds(displayIndex, &bounds);
2005 if (SDL_WINDOWPOS_ISCENTERED(x)) {
2006 x = bounds.x + (bounds.w - window->w) / 2;
2007 }
2008 if (SDL_WINDOWPOS_ISCENTERED(y)) {
2009 y = bounds.y + (bounds.h - window->h) / 2;
2010 }
2011 }
2012
2013 if ((window->flags & SDL_WINDOW_FULLSCREEN)) {
2014 if (!SDL_WINDOWPOS_ISUNDEFINED(x)) {
2015 window->windowed.x = x;
2016 }
2017 if (!SDL_WINDOWPOS_ISUNDEFINED(y)) {
2018 window->windowed.y = y;
2019 }
2020 } else {
2021 if (!SDL_WINDOWPOS_ISUNDEFINED(x)) {
2022 window->x = x;
2023 }
2024 if (!SDL_WINDOWPOS_ISUNDEFINED(y)) {
2025 window->y = y;
2026 }
2027
2028 if (_this->SetWindowPosition) {
2029 _this->SetWindowPosition(_this, window);
2030 }
2031 }
2032}
2033
2034void
2035SDL_GetWindowPosition(SDL_Window * window, int *x, int *y)
2036{
2037 CHECK_WINDOW_MAGIC(window,);
2038
2039 /* Fullscreen windows are always at their display's origin */
2040 if (window->flags & SDL_WINDOW_FULLSCREEN) {
2041 int displayIndex;
2042
2043 if (x) {
2044 *x = 0;
2045 }
2046 if (y) {
2047 *y = 0;
2048 }
2049
2050 /* Find the window's monitor and update to the
2051 monitor offset. */
2052 displayIndex = SDL_GetWindowDisplayIndex(window);
2053 if (displayIndex >= 0) {
2054 SDL_Rect bounds;
2055
2056 SDL_zero(bounds);
2057
2058 SDL_GetDisplayBounds(displayIndex, &bounds);
2059 if (x) {
2060 *x = bounds.x;
2061 }
2062 if (y) {
2063 *y = bounds.y;
2064 }
2065 }
2066 } else {
2067 if (x) {
2068 *x = window->x;
2069 }
2070 if (y) {
2071 *y = window->y;
2072 }
2073 }
2074}
2075
2076void
2077SDL_SetWindowBordered(SDL_Window * window, SDL_bool bordered)
2078{
2079 CHECK_WINDOW_MAGIC(window,);
2080 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
2081 const int want = (bordered != SDL_FALSE); /* normalize the flag. */
2082 const int have = ((window->flags & SDL_WINDOW_BORDERLESS) == 0);
2083 if ((want != have) && (_this->SetWindowBordered)) {
2084 if (want) {
2085 window->flags &= ~SDL_WINDOW_BORDERLESS;
2086 } else {
2087 window->flags |= SDL_WINDOW_BORDERLESS;
2088 }
2089 _this->SetWindowBordered(_this, window, (SDL_bool) want);
2090 }
2091 }
2092}
2093
2094void
2095SDL_SetWindowResizable(SDL_Window * window, SDL_bool resizable)
2096{
2097 CHECK_WINDOW_MAGIC(window,);
2098 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
2099 const int want = (resizable != SDL_FALSE); /* normalize the flag. */
2100 const int have = ((window->flags & SDL_WINDOW_RESIZABLE) != 0);
2101 if ((want != have) && (_this->SetWindowResizable)) {
2102 if (want) {
2103 window->flags |= SDL_WINDOW_RESIZABLE;
2104 } else {
2105 window->flags &= ~SDL_WINDOW_RESIZABLE;
2106 }
2107 _this->SetWindowResizable(_this, window, (SDL_bool) want);
2108 }
2109 }
2110}
2111
2112void
2113SDL_SetWindowSize(SDL_Window * window, int w, int h)
2114{
2115 CHECK_WINDOW_MAGIC(window,);
2116 if (w <= 0) {
2117 SDL_InvalidParamError("w");
2118 return;
2119 }
2120 if (h <= 0) {
2121 SDL_InvalidParamError("h");
2122 return;
2123 }
2124
2125 /* Make sure we don't exceed any window size limits */
2126 if (window->min_w && w < window->min_w) {
2127 w = window->min_w;
2128 }
2129 if (window->max_w && w > window->max_w) {
2130 w = window->max_w;
2131 }
2132 if (window->min_h && h < window->min_h) {
2133 h = window->min_h;
2134 }
2135 if (window->max_h && h > window->max_h) {
2136 h = window->max_h;
2137 }
2138
2139 window->windowed.w = w;
2140 window->windowed.h = h;
2141
2142 if (window->flags & SDL_WINDOW_FULLSCREEN) {
2143 if (FULLSCREEN_VISIBLE(window) && (window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP) {
2144 window->last_fullscreen_flags = 0;
2145 SDL_UpdateFullscreenMode(window, SDL_TRUE);
2146 }
2147 } else {
2148 window->w = w;
2149 window->h = h;
2150 if (_this->SetWindowSize) {
2151 _this->SetWindowSize(_this, window);
2152 }
2153 if (window->w == w && window->h == h) {
2154 /* We didn't get a SDL_WINDOWEVENT_RESIZED event (by design) */
2155 SDL_OnWindowResized(window);
2156 }
2157 }
2158}
2159
2160void
2161SDL_GetWindowSize(SDL_Window * window, int *w, int *h)
2162{
2163 CHECK_WINDOW_MAGIC(window,);
2164 if (w) {
2165 *w = window->w;
2166 }
2167 if (h) {
2168 *h = window->h;
2169 }
2170}
2171
2172int
2173SDL_GetWindowBordersSize(SDL_Window * window, int *top, int *left, int *bottom, int *right)
2174{
2175 int dummy = 0;
2176
2177 if (!top) { top = &dummy; }
2178 if (!left) { left = &dummy; }
2179 if (!right) { right = &dummy; }
2180 if (!bottom) { bottom = &dummy; }
2181
2182 /* Always initialize, so applications don't have to care */
2183 *top = *left = *bottom = *right = 0;
2184
2185 CHECK_WINDOW_MAGIC(window, -1);
2186
2187 if (!_this->GetWindowBordersSize) {
2188 return SDL_Unsupported();
2189 }
2190
2191 return _this->GetWindowBordersSize(_this, window, top, left, bottom, right);
2192}
2193
2194void
2195SDL_SetWindowMinimumSize(SDL_Window * window, int min_w, int min_h)
2196{
2197 CHECK_WINDOW_MAGIC(window,);
2198 if (min_w <= 0) {
2199 SDL_InvalidParamError("min_w");
2200 return;
2201 }
2202 if (min_h <= 0) {
2203 SDL_InvalidParamError("min_h");
2204 return;
2205 }
2206
2207 if ((window->max_w && min_w > window->max_w) ||
2208 (window->max_h && min_h > window->max_h)) {
2209 SDL_SetError("SDL_SetWindowMinimumSize(): Tried to set minimum size larger than maximum size");
2210 return;
2211 }
2212
2213 window->min_w = min_w;
2214 window->min_h = min_h;
2215
2216 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
2217 if (_this->SetWindowMinimumSize) {
2218 _this->SetWindowMinimumSize(_this, window);
2219 }
2220 /* Ensure that window is not smaller than minimal size */
2221 SDL_SetWindowSize(window, SDL_max(window->w, window->min_w), SDL_max(window->h, window->min_h));
2222 }
2223}
2224
2225void
2226SDL_GetWindowMinimumSize(SDL_Window * window, int *min_w, int *min_h)
2227{
2228 CHECK_WINDOW_MAGIC(window,);
2229 if (min_w) {
2230 *min_w = window->min_w;
2231 }
2232 if (min_h) {
2233 *min_h = window->min_h;
2234 }
2235}
2236
2237void
2238SDL_SetWindowMaximumSize(SDL_Window * window, int max_w, int max_h)
2239{
2240 CHECK_WINDOW_MAGIC(window,);
2241 if (max_w <= 0) {
2242 SDL_InvalidParamError("max_w");
2243 return;
2244 }
2245 if (max_h <= 0) {
2246 SDL_InvalidParamError("max_h");
2247 return;
2248 }
2249
2250 if (max_w < window->min_w || max_h < window->min_h) {
2251 SDL_SetError("SDL_SetWindowMaximumSize(): Tried to set maximum size smaller than minimum size");
2252 return;
2253 }
2254
2255 window->max_w = max_w;
2256 window->max_h = max_h;
2257
2258 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
2259 if (_this->SetWindowMaximumSize) {
2260 _this->SetWindowMaximumSize(_this, window);
2261 }
2262 /* Ensure that window is not larger than maximal size */
2263 SDL_SetWindowSize(window, SDL_min(window->w, window->max_w), SDL_min(window->h, window->max_h));
2264 }
2265}
2266
2267void
2268SDL_GetWindowMaximumSize(SDL_Window * window, int *max_w, int *max_h)
2269{
2270 CHECK_WINDOW_MAGIC(window,);
2271 if (max_w) {
2272 *max_w = window->max_w;
2273 }
2274 if (max_h) {
2275 *max_h = window->max_h;
2276 }
2277}
2278
2279void
2280SDL_ShowWindow(SDL_Window * window)
2281{
2282 CHECK_WINDOW_MAGIC(window,);
2283
2284 if (window->flags & SDL_WINDOW_SHOWN) {
2285 return;
2286 }
2287
2288 if (_this->ShowWindow) {
2289 _this->ShowWindow(_this, window);
2290 }
2291 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_SHOWN, 0, 0);
2292}
2293
2294void
2295SDL_HideWindow(SDL_Window * window)
2296{
2297 CHECK_WINDOW_MAGIC(window,);
2298
2299 if (!(window->flags & SDL_WINDOW_SHOWN)) {
2300 return;
2301 }
2302
2303 window->is_hiding = SDL_TRUE;
2304 SDL_UpdateFullscreenMode(window, SDL_FALSE);
2305
2306 if (_this->HideWindow) {
2307 _this->HideWindow(_this, window);
2308 }
2309 window->is_hiding = SDL_FALSE;
2310 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
2311}
2312
2313void
2314SDL_RaiseWindow(SDL_Window * window)
2315{
2316 CHECK_WINDOW_MAGIC(window,);
2317
2318 if (!(window->flags & SDL_WINDOW_SHOWN)) {
2319 return;
2320 }
2321 if (_this->RaiseWindow) {
2322 _this->RaiseWindow(_this, window);
2323 }
2324}
2325
2326void
2327SDL_MaximizeWindow(SDL_Window * window)
2328{
2329 CHECK_WINDOW_MAGIC(window,);
2330
2331 if (window->flags & SDL_WINDOW_MAXIMIZED) {
2332 return;
2333 }
2334
2335 /* !!! FIXME: should this check if the window is resizable? */
2336
2337 if (_this->MaximizeWindow) {
2338 _this->MaximizeWindow(_this, window);
2339 }
2340}
2341
2342static SDL_bool
2343CanMinimizeWindow(SDL_Window * window)
2344{
2345 if (!_this->MinimizeWindow) {
2346 return SDL_FALSE;
2347 }
2348 return SDL_TRUE;
2349}
2350
2351void
2352SDL_MinimizeWindow(SDL_Window * window)
2353{
2354 CHECK_WINDOW_MAGIC(window,);
2355
2356 if (window->flags & SDL_WINDOW_MINIMIZED) {
2357 return;
2358 }
2359
2360 if (!CanMinimizeWindow(window)) {
2361 return;
2362 }
2363
2364 SDL_UpdateFullscreenMode(window, SDL_FALSE);
2365
2366 if (_this->MinimizeWindow) {
2367 _this->MinimizeWindow(_this, window);
2368 }
2369}
2370
2371void
2372SDL_RestoreWindow(SDL_Window * window)
2373{
2374 CHECK_WINDOW_MAGIC(window,);
2375
2376 if (!(window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) {
2377 return;
2378 }
2379
2380 if (_this->RestoreWindow) {
2381 _this->RestoreWindow(_this, window);
2382 }
2383}
2384
2385int
2386SDL_SetWindowFullscreen(SDL_Window * window, Uint32 flags)
2387{
2388 Uint32 oldflags;
2389 CHECK_WINDOW_MAGIC(window, -1);
2390
2391 flags &= FULLSCREEN_MASK;
2392
2393 if (flags == (window->flags & FULLSCREEN_MASK)) {
2394 return 0;
2395 }
2396
2397 /* clear the previous flags and OR in the new ones */
2398 oldflags = window->flags & FULLSCREEN_MASK;
2399 window->flags &= ~FULLSCREEN_MASK;
2400 window->flags |= flags;
2401
2402 if (SDL_UpdateFullscreenMode(window, FULLSCREEN_VISIBLE(window)) == 0) {
2403 return 0;
2404 }
2405
2406 window->flags &= ~FULLSCREEN_MASK;
2407 window->flags |= oldflags;
2408 return -1;
2409}
2410
2411static SDL_Surface *
2412SDL_CreateWindowFramebuffer(SDL_Window * window)
2413{
2414 Uint32 format;
2415 void *pixels;
2416 int pitch;
2417 int bpp;
2418 Uint32 Rmask, Gmask, Bmask, Amask;
2419
2420 if (!_this->CreateWindowFramebuffer || !_this->UpdateWindowFramebuffer) {
2421 return NULL;
2422 }
2423
2424 if (_this->CreateWindowFramebuffer(_this, window, &format, &pixels, &pitch) < 0) {
2425 return NULL;
2426 }
2427
2428 if (window->surface) {
2429 return window->surface;
2430 }
2431
2432 if (!SDL_PixelFormatEnumToMasks(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) {
2433 return NULL;
2434 }
2435
2436 return SDL_CreateRGBSurfaceFrom(pixels, window->w, window->h, bpp, pitch, Rmask, Gmask, Bmask, Amask);
2437}
2438
2439SDL_Surface *
2440SDL_GetWindowSurface(SDL_Window * window)
2441{
2442 CHECK_WINDOW_MAGIC(window, NULL);
2443
2444 if (!window->surface_valid) {
2445 if (window->surface) {
2446 window->surface->flags &= ~SDL_DONTFREE;
2447 SDL_FreeSurface(window->surface);
2448 window->surface = NULL;
2449 }
2450 window->surface = SDL_CreateWindowFramebuffer(window);
2451 if (window->surface) {
2452 window->surface_valid = SDL_TRUE;
2453 window->surface->flags |= SDL_DONTFREE;
2454 }
2455 }
2456 return window->surface;
2457}
2458
2459int
2460SDL_UpdateWindowSurface(SDL_Window * window)
2461{
2462 SDL_Rect full_rect;
2463
2464 CHECK_WINDOW_MAGIC(window, -1);
2465
2466 full_rect.x = 0;
2467 full_rect.y = 0;
2468 full_rect.w = window->w;
2469 full_rect.h = window->h;
2470 return SDL_UpdateWindowSurfaceRects(window, &full_rect, 1);
2471}
2472
2473int
2474SDL_UpdateWindowSurfaceRects(SDL_Window * window, const SDL_Rect * rects,
2475 int numrects)
2476{
2477 CHECK_WINDOW_MAGIC(window, -1);
2478
2479 if (!window->surface_valid) {
2480 return SDL_SetError("Window surface is invalid, please call SDL_GetWindowSurface() to get a new surface");
2481 }
2482
2483 return _this->UpdateWindowFramebuffer(_this, window, rects, numrects);
2484}
2485
2486int
2487SDL_SetWindowBrightness(SDL_Window * window, float brightness)
2488{
2489 Uint16 ramp[256];
2490 int status;
2491
2492 CHECK_WINDOW_MAGIC(window, -1);
2493
2494 SDL_CalculateGammaRamp(brightness, ramp);
2495 status = SDL_SetWindowGammaRamp(window, ramp, ramp, ramp);
2496 if (status == 0) {
2497 window->brightness = brightness;
2498 }
2499 return status;
2500}
2501
2502float
2503SDL_GetWindowBrightness(SDL_Window * window)
2504{
2505 CHECK_WINDOW_MAGIC(window, 1.0f);
2506
2507 return window->brightness;
2508}
2509
2510int
2511SDL_SetWindowOpacity(SDL_Window * window, float opacity)
2512{
2513 int retval;
2514 CHECK_WINDOW_MAGIC(window, -1);
2515
2516 if (!_this->SetWindowOpacity) {
2517 return SDL_Unsupported();
2518 }
2519
2520 if (opacity < 0.0f) {
2521 opacity = 0.0f;
2522 } else if (opacity > 1.0f) {
2523 opacity = 1.0f;
2524 }
2525
2526 retval = _this->SetWindowOpacity(_this, window, opacity);
2527 if (retval == 0) {
2528 window->opacity = opacity;
2529 }
2530
2531 return retval;
2532}
2533
2534int
2535SDL_GetWindowOpacity(SDL_Window * window, float * out_opacity)
2536{
2537 CHECK_WINDOW_MAGIC(window, -1);
2538
2539 if (out_opacity) {
2540 *out_opacity = window->opacity;
2541 }
2542
2543 return 0;
2544}
2545
2546int
2547SDL_SetWindowModalFor(SDL_Window * modal_window, SDL_Window * parent_window)
2548{
2549 CHECK_WINDOW_MAGIC(modal_window, -1);
2550 CHECK_WINDOW_MAGIC(parent_window, -1);
2551
2552 if (!_this->SetWindowModalFor) {
2553 return SDL_Unsupported();
2554 }
2555
2556 return _this->SetWindowModalFor(_this, modal_window, parent_window);
2557}
2558
2559int
2560SDL_SetWindowInputFocus(SDL_Window * window)
2561{
2562 CHECK_WINDOW_MAGIC(window, -1);
2563
2564 if (!_this->SetWindowInputFocus) {
2565 return SDL_Unsupported();
2566 }
2567
2568 return _this->SetWindowInputFocus(_this, window);
2569}
2570
2571
2572int
2573SDL_SetWindowGammaRamp(SDL_Window * window, const Uint16 * red,
2574 const Uint16 * green,
2575 const Uint16 * blue)
2576{
2577 CHECK_WINDOW_MAGIC(window, -1);
2578
2579 if (!_this->SetWindowGammaRamp) {
2580 return SDL_Unsupported();
2581 }
2582
2583 if (!window->gamma) {
2584 if (SDL_GetWindowGammaRamp(window, NULL, NULL, NULL) < 0) {
2585 return -1;
2586 }
2587 SDL_assert(window->gamma != NULL);
2588 }
2589
2590 if (red) {
2591 SDL_memcpy(&window->gamma[0*256], red, 256*sizeof(Uint16));
2592 }
2593 if (green) {
2594 SDL_memcpy(&window->gamma[1*256], green, 256*sizeof(Uint16));
2595 }
2596 if (blue) {
2597 SDL_memcpy(&window->gamma[2*256], blue, 256*sizeof(Uint16));
2598 }
2599 if (window->flags & SDL_WINDOW_INPUT_FOCUS) {
2600 return _this->SetWindowGammaRamp(_this, window, window->gamma);
2601 } else {
2602 return 0;
2603 }
2604}
2605
2606int
2607SDL_GetWindowGammaRamp(SDL_Window * window, Uint16 * red,
2608 Uint16 * green,
2609 Uint16 * blue)
2610{
2611 CHECK_WINDOW_MAGIC(window, -1);
2612
2613 if (!window->gamma) {
2614 int i;
2615
2616 window->gamma = (Uint16 *)SDL_malloc(256*6*sizeof(Uint16));
2617 if (!window->gamma) {
2618 return SDL_OutOfMemory();
2619 }
2620 window->saved_gamma = window->gamma + 3*256;
2621
2622 if (_this->GetWindowGammaRamp) {
2623 if (_this->GetWindowGammaRamp(_this, window, window->gamma) < 0) {
2624 return -1;
2625 }
2626 } else {
2627 /* Create an identity gamma ramp */
2628 for (i = 0; i < 256; ++i) {
2629 Uint16 value = (Uint16)((i << 8) | i);
2630
2631 window->gamma[0*256+i] = value;
2632 window->gamma[1*256+i] = value;
2633 window->gamma[2*256+i] = value;
2634 }
2635 }
2636 SDL_memcpy(window->saved_gamma, window->gamma, 3*256*sizeof(Uint16));
2637 }
2638
2639 if (red) {
2640 SDL_memcpy(red, &window->gamma[0*256], 256*sizeof(Uint16));
2641 }
2642 if (green) {
2643 SDL_memcpy(green, &window->gamma[1*256], 256*sizeof(Uint16));
2644 }
2645 if (blue) {
2646 SDL_memcpy(blue, &window->gamma[2*256], 256*sizeof(Uint16));
2647 }
2648 return 0;
2649}
2650
2651void
2652SDL_UpdateWindowGrab(SDL_Window * window)
2653{
2654 SDL_bool keyboard_grabbed, mouse_grabbed;
2655
2656 if (window->flags & SDL_WINDOW_INPUT_FOCUS) {
2657 if (SDL_GetMouse()->relative_mode || (window->flags & SDL_WINDOW_MOUSE_GRABBED)) {
2658 mouse_grabbed = SDL_TRUE;
2659 } else {
2660 mouse_grabbed = SDL_FALSE;
2661 }
2662
2663 if (window->flags & SDL_WINDOW_KEYBOARD_GRABBED) {
2664 keyboard_grabbed = SDL_TRUE;
2665 } else {
2666 keyboard_grabbed = SDL_FALSE;
2667 }
2668 } else {
2669 mouse_grabbed = SDL_FALSE;
2670 keyboard_grabbed = SDL_FALSE;
2671 }
2672
2673 if (mouse_grabbed || keyboard_grabbed) {
2674 if (_this->grabbed_window && (_this->grabbed_window != window)) {
2675 /* stealing a grab from another window! */
2676 _this->grabbed_window->flags &= ~(SDL_WINDOW_MOUSE_GRABBED | SDL_WINDOW_KEYBOARD_GRABBED);
2677 if (_this->SetWindowMouseGrab) {
2678 _this->SetWindowMouseGrab(_this, _this->grabbed_window, SDL_FALSE);
2679 }
2680 if (_this->SetWindowKeyboardGrab) {
2681 _this->SetWindowKeyboardGrab(_this, _this->grabbed_window, SDL_FALSE);
2682 }
2683 }
2684 _this->grabbed_window = window;
2685 } else if (_this->grabbed_window == window) {
2686 _this->grabbed_window = NULL; /* ungrabbing input. */
2687 }
2688
2689 if (_this->SetWindowMouseGrab) {
2690 _this->SetWindowMouseGrab(_this, window, mouse_grabbed);
2691 }
2692 if (_this->SetWindowKeyboardGrab) {
2693 _this->SetWindowKeyboardGrab(_this, window, keyboard_grabbed);
2694 }
2695}
2696
2697void
2698SDL_SetWindowGrab(SDL_Window * window, SDL_bool grabbed)
2699{
2700 CHECK_WINDOW_MAGIC(window,);
2701
2702 SDL_SetWindowMouseGrab(window, grabbed);
2703
2704 if (SDL_GetHintBoolean(SDL_HINT_GRAB_KEYBOARD, SDL_FALSE)) {
2705 SDL_SetWindowKeyboardGrab(window, grabbed);
2706 }
2707}
2708
2709void
2710SDL_SetWindowKeyboardGrab(SDL_Window * window, SDL_bool grabbed)
2711{
2712 CHECK_WINDOW_MAGIC(window,);
2713
2714 if (!!grabbed == !!(window->flags & SDL_WINDOW_KEYBOARD_GRABBED)) {
2715 return;
2716 }
2717 if (grabbed) {
2718 window->flags |= SDL_WINDOW_KEYBOARD_GRABBED;
2719 } else {
2720 window->flags &= ~SDL_WINDOW_KEYBOARD_GRABBED;
2721 }
2722 SDL_UpdateWindowGrab(window);
2723}
2724
2725void
2726SDL_SetWindowMouseGrab(SDL_Window * window, SDL_bool grabbed)
2727{
2728 CHECK_WINDOW_MAGIC(window,);
2729
2730 if (!!grabbed == !!(window->flags & SDL_WINDOW_MOUSE_GRABBED)) {
2731 return;
2732 }
2733 if (grabbed) {
2734 window->flags |= SDL_WINDOW_MOUSE_GRABBED;
2735 } else {
2736 window->flags &= ~SDL_WINDOW_MOUSE_GRABBED;
2737 }
2738 SDL_UpdateWindowGrab(window);
2739}
2740
2741SDL_bool
2742SDL_GetWindowGrab(SDL_Window * window)
2743{
2744 CHECK_WINDOW_MAGIC(window, SDL_FALSE);
2745 SDL_assert(!_this->grabbed_window ||
2746 ((_this->grabbed_window->flags & SDL_WINDOW_MOUSE_GRABBED) != 0) ||
2747 ((_this->grabbed_window->flags & SDL_WINDOW_KEYBOARD_GRABBED) != 0));
2748 return window == _this->grabbed_window;
2749}
2750
2751SDL_bool
2752SDL_GetWindowKeyboardGrab(SDL_Window * window)
2753{
2754 CHECK_WINDOW_MAGIC(window, SDL_FALSE);
2755 return window == _this->grabbed_window &&
2756 ((_this->grabbed_window->flags & SDL_WINDOW_KEYBOARD_GRABBED) != 0);
2757}
2758
2759SDL_bool
2760SDL_GetWindowMouseGrab(SDL_Window * window)
2761{
2762 CHECK_WINDOW_MAGIC(window, SDL_FALSE);
2763 return window == _this->grabbed_window &&
2764 ((_this->grabbed_window->flags & SDL_WINDOW_MOUSE_GRABBED) != 0);
2765}
2766
2767SDL_Window *
2768SDL_GetGrabbedWindow(void)
2769{
2770 SDL_assert(!_this->grabbed_window ||
2771 ((_this->grabbed_window->flags & SDL_WINDOW_MOUSE_GRABBED) != 0) ||
2772 ((_this->grabbed_window->flags & SDL_WINDOW_KEYBOARD_GRABBED) != 0));
2773 return _this->grabbed_window;
2774}
2775
2776void
2777SDL_OnWindowShown(SDL_Window * window)
2778{
2779 SDL_OnWindowRestored(window);
2780}
2781
2782void
2783SDL_OnWindowHidden(SDL_Window * window)
2784{
2785 SDL_UpdateFullscreenMode(window, SDL_FALSE);
2786}
2787
2788void
2789SDL_OnWindowResized(SDL_Window * window)
2790{
2791 window->surface_valid = SDL_FALSE;
2792 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_SIZE_CHANGED, window->w, window->h);
2793}
2794
2795void
2796SDL_OnWindowMinimized(SDL_Window * window)
2797{
2798 SDL_UpdateFullscreenMode(window, SDL_FALSE);
2799}
2800
2801void
2802SDL_OnWindowRestored(SDL_Window * window)
2803{
2804 /*
2805 * FIXME: Is this fine to just remove this, or should it be preserved just
2806 * for the fullscreen case? In principle it seems like just hiding/showing
2807 * windows shouldn't affect the stacking order; maybe the right fix is to
2808 * re-decouple OnWindowShown and OnWindowRestored.
2809 */
2810 /*SDL_RaiseWindow(window);*/
2811
2812 if (FULLSCREEN_VISIBLE(window)) {
2813 SDL_UpdateFullscreenMode(window, SDL_TRUE);
2814 }
2815}
2816
2817void
2818SDL_OnWindowEnter(SDL_Window * window)
2819{
2820 if (_this->OnWindowEnter) {
2821 _this->OnWindowEnter(_this, window);
2822 }
2823}
2824
2825void
2826SDL_OnWindowLeave(SDL_Window * window)
2827{
2828}
2829
2830void
2831SDL_OnWindowFocusGained(SDL_Window * window)
2832{
2833 SDL_Mouse *mouse = SDL_GetMouse();
2834
2835 if (window->gamma && _this->SetWindowGammaRamp) {
2836 _this->SetWindowGammaRamp(_this, window, window->gamma);
2837 }
2838
2839 if (mouse && mouse->relative_mode) {
2840 SDL_SetMouseFocus(window);
2841 SDL_WarpMouseInWindow(window, window->w/2, window->h/2);
2842 }
2843
2844 SDL_UpdateWindowGrab(window);
2845}
2846
2847static SDL_bool
2848ShouldMinimizeOnFocusLoss(SDL_Window * window)
2849{
2850 if (!(window->flags & SDL_WINDOW_FULLSCREEN) || window->is_destroying) {
2851 return SDL_FALSE;
2852 }
2853
2854#ifdef __MACOSX__
2855 if (SDL_strcmp(_this->name, "cocoa") == 0) { /* don't do this for X11, etc */
2856 if (Cocoa_IsWindowInFullscreenSpace(window)) {
2857 return SDL_FALSE;
2858 }
2859 }
2860#endif
2861
2862#ifdef __ANDROID__
2863 {
2864 extern SDL_bool Android_JNI_ShouldMinimizeOnFocusLoss(void);
2865 if (! Android_JNI_ShouldMinimizeOnFocusLoss()) {
2866 return SDL_FALSE;
2867 }
2868 }
2869#endif
2870
2871 return SDL_GetHintBoolean(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, SDL_FALSE);
2872}
2873
2874void
2875SDL_OnWindowFocusLost(SDL_Window * window)
2876{
2877 if (window->gamma && _this->SetWindowGammaRamp) {
2878 _this->SetWindowGammaRamp(_this, window, window->saved_gamma);
2879 }
2880
2881 SDL_UpdateWindowGrab(window);
2882
2883 if (ShouldMinimizeOnFocusLoss(window)) {
2884 SDL_MinimizeWindow(window);
2885 }
2886}
2887
2888/* !!! FIXME: is this different than SDL_GetKeyboardFocus()?
2889 !!! FIXME: Also, SDL_GetKeyboardFocus() is O(1), this isn't. */
2890SDL_Window *
2891SDL_GetFocusWindow(void)
2892{
2893 SDL_Window *window;
2894
2895 if (!_this) {
2896 return NULL;
2897 }
2898 for (window = _this->windows; window; window = window->next) {
2899 if (window->flags & SDL_WINDOW_INPUT_FOCUS) {
2900 return window;
2901 }
2902 }
2903 return NULL;
2904}
2905
2906void
2907SDL_DestroyWindow(SDL_Window * window)
2908{
2909 SDL_VideoDisplay *display;
2910
2911 CHECK_WINDOW_MAGIC(window,);
2912
2913 window->is_destroying = SDL_TRUE;
2914
2915 /* Restore video mode, etc. */
2916 SDL_HideWindow(window);
2917
2918 /* Make sure this window no longer has focus */
2919 if (SDL_GetKeyboardFocus() == window) {
2920 SDL_SetKeyboardFocus(NULL);
2921 }
2922 if (SDL_GetMouseFocus() == window) {
2923 SDL_SetMouseFocus(NULL);
2924 }
2925
2926 /* make no context current if this is the current context window. */
2927 if (window->flags & SDL_WINDOW_OPENGL) {
2928 if (_this->current_glwin == window) {
2929 SDL_GL_MakeCurrent(window, NULL);
2930 }
2931 }
2932
2933 if (window->surface) {
2934 window->surface->flags &= ~SDL_DONTFREE;
2935 SDL_FreeSurface(window->surface);
2936 window->surface = NULL;
2937 window->surface_valid = SDL_FALSE;
2938 }
2939 if (_this->DestroyWindowFramebuffer) {
2940 _this->DestroyWindowFramebuffer(_this, window);
2941 }
2942 if (_this->DestroyWindow) {
2943 _this->DestroyWindow(_this, window);
2944 }
2945 if (window->flags & SDL_WINDOW_OPENGL) {
2946 SDL_GL_UnloadLibrary();
2947 }
2948 if (window->flags & SDL_WINDOW_VULKAN) {
2949 SDL_Vulkan_UnloadLibrary();
2950 }
2951
2952 display = SDL_GetDisplayForWindow(window);
2953 if (display->fullscreen_window == window) {
2954 display->fullscreen_window = NULL;
2955 }
2956
2957 /* Now invalidate magic */
2958 window->magic = NULL;
2959
2960 /* Free memory associated with the window */
2961 SDL_free(window->title);
2962 SDL_FreeSurface(window->icon);
2963 SDL_free(window->gamma);
2964 while (window->data) {
2965 SDL_WindowUserData *data = window->data;
2966
2967 window->data = data->next;
2968 SDL_free(data->name);
2969 SDL_free(data);
2970 }
2971
2972 /* Unlink the window from the list */
2973 if (window->next) {
2974 window->next->prev = window->prev;
2975 }
2976 if (window->prev) {
2977 window->prev->next = window->next;
2978 } else {
2979 _this->windows = window->next;
2980 }
2981
2982 SDL_free(window);
2983}
2984
2985SDL_bool
2986SDL_IsScreenSaverEnabled()
2987{
2988 if (!_this) {
2989 return SDL_TRUE;
2990 }
2991 return _this->suspend_screensaver ? SDL_FALSE : SDL_TRUE;
2992}
2993
2994void
2995SDL_EnableScreenSaver()
2996{
2997 if (!_this) {
2998 return;
2999 }
3000 if (!_this->suspend_screensaver) {
3001 return;
3002 }
3003 _this->suspend_screensaver = SDL_FALSE;
3004 if (_this->SuspendScreenSaver) {
3005 _this->SuspendScreenSaver(_this);
3006 }
3007}
3008
3009void
3010SDL_DisableScreenSaver()
3011{
3012 if (!_this) {
3013 return;
3014 }
3015 if (_this->suspend_screensaver) {
3016 return;
3017 }
3018 _this->suspend_screensaver = SDL_TRUE;
3019 if (_this->SuspendScreenSaver) {
3020 _this->SuspendScreenSaver(_this);
3021 }
3022}
3023
3024void
3025SDL_VideoQuit(void)
3026{
3027 int i, j;
3028
3029 if (!_this) {
3030 return;
3031 }
3032
3033 /* Halt event processing before doing anything else */
3034 SDL_TouchQuit();
3035 SDL_MouseQuit();
3036 SDL_KeyboardQuit();
3037 SDL_QuitSubSystem(SDL_INIT_EVENTS);
3038
3039 SDL_EnableScreenSaver();
3040
3041 /* Clean up the system video */
3042 while (_this->windows) {
3043 SDL_DestroyWindow(_this->windows);
3044 }
3045 _this->VideoQuit(_this);
3046
3047 for (i = 0; i < _this->num_displays; ++i) {
3048 SDL_VideoDisplay *display = &_this->displays[i];
3049 for (j = display->num_display_modes; j--;) {
3050 SDL_free(display->display_modes[j].driverdata);
3051 display->display_modes[j].driverdata = NULL;
3052 }
3053 SDL_free(display->display_modes);
3054 display->display_modes = NULL;
3055 SDL_free(display->desktop_mode.driverdata);
3056 display->desktop_mode.driverdata = NULL;
3057 SDL_free(display->driverdata);
3058 display->driverdata = NULL;
3059 }
3060 if (_this->displays) {
3061 for (i = 0; i < _this->num_displays; ++i) {
3062 SDL_free(_this->displays[i].name);
3063 }
3064 SDL_free(_this->displays);
3065 _this->displays = NULL;
3066 _this->num_displays = 0;
3067 }
3068 SDL_free(_this->clipboard_text);
3069 _this->clipboard_text = NULL;
3070 _this->free(_this);
3071 _this = NULL;
3072}
3073
3074int
3075SDL_GL_LoadLibrary(const char *path)
3076{
3077 int retval;
3078
3079 if (!_this) {
3080 return SDL_UninitializedVideo();
3081 }
3082 if (_this->gl_config.driver_loaded) {
3083 if (path && SDL_strcmp(path, _this->gl_config.driver_path) != 0) {
3084 return SDL_SetError("OpenGL library already loaded");
3085 }
3086 retval = 0;
3087 } else {
3088 if (!_this->GL_LoadLibrary) {
3089 return SDL_SetError("No dynamic GL support in current SDL video driver (%s)", _this->name);
3090 }
3091 retval = _this->GL_LoadLibrary(_this, path);
3092 }
3093 if (retval == 0) {
3094 ++_this->gl_config.driver_loaded;
3095 } else {
3096 if (_this->GL_UnloadLibrary) {
3097 _this->GL_UnloadLibrary(_this);
3098 }
3099 }
3100 return (retval);
3101}
3102
3103void *
3104SDL_GL_GetProcAddress(const char *proc)
3105{
3106 void *func;
3107
3108 if (!_this) {
3109 SDL_UninitializedVideo();
3110 return NULL;
3111 }
3112 func = NULL;
3113 if (_this->GL_GetProcAddress) {
3114 if (_this->gl_config.driver_loaded) {
3115 func = _this->GL_GetProcAddress(_this, proc);
3116 } else {
3117 SDL_SetError("No GL driver has been loaded");
3118 }
3119 } else {
3120 SDL_SetError("No dynamic GL support in current SDL video driver (%s)", _this->name);
3121 }
3122 return func;
3123}
3124
3125void
3126SDL_GL_UnloadLibrary(void)
3127{
3128 if (!_this) {
3129 SDL_UninitializedVideo();
3130 return;
3131 }
3132 if (_this->gl_config.driver_loaded > 0) {
3133 if (--_this->gl_config.driver_loaded > 0) {
3134 return;
3135 }
3136 if (_this->GL_UnloadLibrary) {
3137 _this->GL_UnloadLibrary(_this);
3138 }
3139 }
3140}
3141
3142#if SDL_VIDEO_OPENGL || SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
3143static SDL_INLINE SDL_bool
3144isAtLeastGL3(const char *verstr)
3145{
3146 return (verstr && (SDL_atoi(verstr) >= 3));
3147}
3148#endif
3149
3150SDL_bool
3151SDL_GL_ExtensionSupported(const char *extension)
3152{
3153#if SDL_VIDEO_OPENGL || SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
3154 const GLubyte *(APIENTRY * glGetStringFunc) (GLenum);
3155 const char *extensions;
3156 const char *start;
3157 const char *where, *terminator;
3158
3159 /* Extension names should not have spaces. */
3160 where = SDL_strchr(extension, ' ');
3161 if (where || *extension == '\0') {
3162 return SDL_FALSE;
3163 }
3164 /* See if there's an environment variable override */
3165 start = SDL_getenv(extension);
3166 if (start && *start == '0') {
3167 return SDL_FALSE;
3168 }
3169
3170 /* Lookup the available extensions */
3171
3172 glGetStringFunc = SDL_GL_GetProcAddress("glGetString");
3173 if (!glGetStringFunc) {
3174 return SDL_FALSE;
3175 }
3176
3177 if (isAtLeastGL3((const char *) glGetStringFunc(GL_VERSION))) {
3178 const GLubyte *(APIENTRY * glGetStringiFunc) (GLenum, GLuint);
3179 void (APIENTRY * glGetIntegervFunc) (GLenum pname, GLint * params);
3180 GLint num_exts = 0;
3181 GLint i;
3182
3183 glGetStringiFunc = SDL_GL_GetProcAddress("glGetStringi");
3184 glGetIntegervFunc = SDL_GL_GetProcAddress("glGetIntegerv");
3185 if ((!glGetStringiFunc) || (!glGetIntegervFunc)) {
3186 return SDL_FALSE;
3187 }
3188
3189 #ifndef GL_NUM_EXTENSIONS
3190 #define GL_NUM_EXTENSIONS 0x821D
3191 #endif
3192 glGetIntegervFunc(GL_NUM_EXTENSIONS, &num_exts);
3193 for (i = 0; i < num_exts; i++) {
3194 const char *thisext = (const char *) glGetStringiFunc(GL_EXTENSIONS, i);
3195 if (SDL_strcmp(thisext, extension) == 0) {
3196 return SDL_TRUE;
3197 }
3198 }
3199
3200 return SDL_FALSE;
3201 }
3202
3203 /* Try the old way with glGetString(GL_EXTENSIONS) ... */
3204
3205 extensions = (const char *) glGetStringFunc(GL_EXTENSIONS);
3206 if (!extensions) {
3207 return SDL_FALSE;
3208 }
3209 /*
3210 * It takes a bit of care to be fool-proof about parsing the OpenGL
3211 * extensions string. Don't be fooled by sub-strings, etc.
3212 */
3213
3214 start = extensions;
3215
3216 for (;;) {
3217 where = SDL_strstr(start, extension);
3218 if (!where)
3219 break;
3220
3221 terminator = where + SDL_strlen(extension);
3222 if (where == extensions || *(where - 1) == ' ')
3223 if (*terminator == ' ' || *terminator == '\0')
3224 return SDL_TRUE;
3225
3226 start = terminator;
3227 }
3228 return SDL_FALSE;
3229#else
3230 return SDL_FALSE;
3231#endif
3232}
3233
3234/* Deduce supported ES profile versions from the supported
3235 ARB_ES*_compatibility extensions. There is no direct query.
3236
3237 This is normally only called when the OpenGL driver supports
3238 {GLX,WGL}_EXT_create_context_es2_profile.
3239 */
3240void
3241SDL_GL_DeduceMaxSupportedESProfile(int* major, int* minor)
3242{
3243/* THIS REQUIRES AN EXISTING GL CONTEXT THAT HAS BEEN MADE CURRENT. */
3244/* Please refer to https://bugzilla.libsdl.org/show_bug.cgi?id=3725 for discussion. */
3245#if SDL_VIDEO_OPENGL || SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
3246 /* XXX This is fragile; it will break in the event of release of
3247 * new versions of OpenGL ES.
3248 */
3249 if (SDL_GL_ExtensionSupported("GL_ARB_ES3_2_compatibility")) {
3250 *major = 3;
3251 *minor = 2;
3252 } else if (SDL_GL_ExtensionSupported("GL_ARB_ES3_1_compatibility")) {
3253 *major = 3;
3254 *minor = 1;
3255 } else if (SDL_GL_ExtensionSupported("GL_ARB_ES3_compatibility")) {
3256 *major = 3;
3257 *minor = 0;
3258 } else {
3259 *major = 2;
3260 *minor = 0;
3261 }
3262#endif
3263}
3264
3265void
3266SDL_GL_ResetAttributes()
3267{
3268 if (!_this) {
3269 return;
3270 }
3271
3272 _this->gl_config.red_size = 3;
3273 _this->gl_config.green_size = 3;
3274 _this->gl_config.blue_size = 2;
3275 _this->gl_config.alpha_size = 0;
3276 _this->gl_config.buffer_size = 0;
3277 _this->gl_config.depth_size = 16;
3278 _this->gl_config.stencil_size = 0;
3279 _this->gl_config.double_buffer = 1;
3280 _this->gl_config.accum_red_size = 0;
3281 _this->gl_config.accum_green_size = 0;
3282 _this->gl_config.accum_blue_size = 0;
3283 _this->gl_config.accum_alpha_size = 0;
3284 _this->gl_config.stereo = 0;
3285 _this->gl_config.multisamplebuffers = 0;
3286 _this->gl_config.multisamplesamples = 0;
3287 _this->gl_config.retained_backing = 1;
3288 _this->gl_config.accelerated = -1; /* accelerated or not, both are fine */
3289
3290#if SDL_VIDEO_OPENGL
3291 _this->gl_config.major_version = 2;
3292 _this->gl_config.minor_version = 1;
3293 _this->gl_config.profile_mask = 0;
3294#elif SDL_VIDEO_OPENGL_ES2
3295 _this->gl_config.major_version = 2;
3296 _this->gl_config.minor_version = 0;
3297 _this->gl_config.profile_mask = SDL_GL_CONTEXT_PROFILE_ES;
3298#elif SDL_VIDEO_OPENGL_ES
3299 _this->gl_config.major_version = 1;
3300 _this->gl_config.minor_version = 1;
3301 _this->gl_config.profile_mask = SDL_GL_CONTEXT_PROFILE_ES;
3302#endif
3303
3304 if (_this->GL_DefaultProfileConfig) {
3305 _this->GL_DefaultProfileConfig(_this, &_this->gl_config.profile_mask,
3306 &_this->gl_config.major_version,
3307 &_this->gl_config.minor_version);
3308 }
3309
3310 _this->gl_config.flags = 0;
3311 _this->gl_config.framebuffer_srgb_capable = 0;
3312 _this->gl_config.no_error = 0;
3313 _this->gl_config.release_behavior = SDL_GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH;
3314 _this->gl_config.reset_notification = SDL_GL_CONTEXT_RESET_NO_NOTIFICATION;
3315
3316 _this->gl_config.share_with_current_context = 0;
3317}
3318
3319int
3320SDL_GL_SetAttribute(SDL_GLattr attr, int value)
3321{
3322#if SDL_VIDEO_OPENGL || SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
3323 int retval;
3324
3325 if (!_this) {
3326 return SDL_UninitializedVideo();
3327 }
3328 retval = 0;
3329 switch (attr) {
3330 case SDL_GL_RED_SIZE:
3331 _this->gl_config.red_size = value;
3332 break;
3333 case SDL_GL_GREEN_SIZE:
3334 _this->gl_config.green_size = value;
3335 break;
3336 case SDL_GL_BLUE_SIZE:
3337 _this->gl_config.blue_size = value;
3338 break;
3339 case SDL_GL_ALPHA_SIZE:
3340 _this->gl_config.alpha_size = value;
3341 break;
3342 case SDL_GL_DOUBLEBUFFER:
3343 _this->gl_config.double_buffer = value;
3344 break;
3345 case SDL_GL_BUFFER_SIZE:
3346 _this->gl_config.buffer_size = value;
3347 break;
3348 case SDL_GL_DEPTH_SIZE:
3349 _this->gl_config.depth_size = value;
3350 break;
3351 case SDL_GL_STENCIL_SIZE:
3352 _this->gl_config.stencil_size = value;
3353 break;
3354 case SDL_GL_ACCUM_RED_SIZE:
3355 _this->gl_config.accum_red_size = value;
3356 break;
3357 case SDL_GL_ACCUM_GREEN_SIZE:
3358 _this->gl_config.accum_green_size = value;
3359 break;
3360 case SDL_GL_ACCUM_BLUE_SIZE:
3361 _this->gl_config.accum_blue_size = value;
3362 break;
3363 case SDL_GL_ACCUM_ALPHA_SIZE:
3364 _this->gl_config.accum_alpha_size = value;
3365 break;
3366 case SDL_GL_STEREO:
3367 _this->gl_config.stereo = value;
3368 break;
3369 case SDL_GL_MULTISAMPLEBUFFERS:
3370 _this->gl_config.multisamplebuffers = value;
3371 break;
3372 case SDL_GL_MULTISAMPLESAMPLES:
3373 _this->gl_config.multisamplesamples = value;
3374 break;
3375 case SDL_GL_ACCELERATED_VISUAL:
3376 _this->gl_config.accelerated = value;
3377 break;
3378 case SDL_GL_RETAINED_BACKING:
3379 _this->gl_config.retained_backing = value;
3380 break;
3381 case SDL_GL_CONTEXT_MAJOR_VERSION:
3382 _this->gl_config.major_version = value;
3383 break;
3384 case SDL_GL_CONTEXT_MINOR_VERSION:
3385 _this->gl_config.minor_version = value;
3386 break;
3387 case SDL_GL_CONTEXT_EGL:
3388 /* FIXME: SDL_GL_CONTEXT_EGL to be deprecated in SDL 2.1 */
3389 if (value != 0) {
3390 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
3391 } else {
3392 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
3393 };
3394 break;
3395 case SDL_GL_CONTEXT_FLAGS:
3396 if (value & ~(SDL_GL_CONTEXT_DEBUG_FLAG |
3397 SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG |
3398 SDL_GL_CONTEXT_ROBUST_ACCESS_FLAG |
3399 SDL_GL_CONTEXT_RESET_ISOLATION_FLAG)) {
3400 retval = SDL_SetError("Unknown OpenGL context flag %d", value);
3401 break;
3402 }
3403 _this->gl_config.flags = value;
3404 break;
3405 case SDL_GL_CONTEXT_PROFILE_MASK:
3406 if (value != 0 &&
3407 value != SDL_GL_CONTEXT_PROFILE_CORE &&
3408 value != SDL_GL_CONTEXT_PROFILE_COMPATIBILITY &&
3409 value != SDL_GL_CONTEXT_PROFILE_ES) {
3410 retval = SDL_SetError("Unknown OpenGL context profile %d", value);
3411 break;
3412 }
3413 _this->gl_config.profile_mask = value;
3414 break;
3415 case SDL_GL_SHARE_WITH_CURRENT_CONTEXT:
3416 _this->gl_config.share_with_current_context = value;
3417 break;
3418 case SDL_GL_FRAMEBUFFER_SRGB_CAPABLE:
3419 _this->gl_config.framebuffer_srgb_capable = value;
3420 break;
3421 case SDL_GL_CONTEXT_RELEASE_BEHAVIOR:
3422 _this->gl_config.release_behavior = value;
3423 break;
3424 case SDL_GL_CONTEXT_RESET_NOTIFICATION:
3425 _this->gl_config.reset_notification = value;
3426 break;
3427 case SDL_GL_CONTEXT_NO_ERROR:
3428 _this->gl_config.no_error = value;
3429 break;
3430 default:
3431 retval = SDL_SetError("Unknown OpenGL attribute");
3432 break;
3433 }
3434 return retval;
3435#else
3436 return SDL_Unsupported();
3437#endif /* SDL_VIDEO_OPENGL */
3438}
3439
3440int
3441SDL_GL_GetAttribute(SDL_GLattr attr, int *value)
3442{
3443#if SDL_VIDEO_OPENGL || SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
3444 GLenum (APIENTRY *glGetErrorFunc) (void);
3445 GLenum attrib = 0;
3446 GLenum error = 0;
3447
3448 /*
3449 * Some queries in Core Profile desktop OpenGL 3+ contexts require
3450 * glGetFramebufferAttachmentParameteriv instead of glGetIntegerv. Note that
3451 * the enums we use for the former function don't exist in OpenGL ES 2, and
3452 * the function itself doesn't exist prior to OpenGL 3 and OpenGL ES 2.
3453 */
3454#if SDL_VIDEO_OPENGL
3455 const GLubyte *(APIENTRY *glGetStringFunc) (GLenum name);
3456 void (APIENTRY *glGetFramebufferAttachmentParameterivFunc) (GLenum target, GLenum attachment, GLenum pname, GLint* params);
3457 GLenum attachment = GL_BACK_LEFT;
3458 GLenum attachmentattrib = 0;
3459#endif
3460
3461 if (!value) {
3462 return SDL_InvalidParamError("value");
3463 }
3464
3465 /* Clear value in any case */
3466 *value = 0;
3467
3468 if (!_this) {
3469 return SDL_UninitializedVideo();
3470 }
3471
3472 switch (attr) {
3473 case SDL_GL_RED_SIZE:
3474#if SDL_VIDEO_OPENGL
3475 attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE;
3476#endif
3477 attrib = GL_RED_BITS;
3478 break;
3479 case SDL_GL_BLUE_SIZE:
3480#if SDL_VIDEO_OPENGL
3481 attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE;
3482#endif
3483 attrib = GL_BLUE_BITS;
3484 break;
3485 case SDL_GL_GREEN_SIZE:
3486#if SDL_VIDEO_OPENGL
3487 attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE;
3488#endif
3489 attrib = GL_GREEN_BITS;
3490 break;
3491 case SDL_GL_ALPHA_SIZE:
3492#if SDL_VIDEO_OPENGL
3493 attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE;
3494#endif
3495 attrib = GL_ALPHA_BITS;
3496 break;
3497 case SDL_GL_DOUBLEBUFFER:
3498#if SDL_VIDEO_OPENGL
3499 attrib = GL_DOUBLEBUFFER;
3500 break;
3501#else
3502 /* OpenGL ES 1.0 and above specifications have EGL_SINGLE_BUFFER */
3503 /* parameter which switches double buffer to single buffer. OpenGL ES */
3504 /* SDL driver must set proper value after initialization */
3505 *value = _this->gl_config.double_buffer;
3506 return 0;
3507#endif
3508 case SDL_GL_DEPTH_SIZE:
3509#if SDL_VIDEO_OPENGL
3510 attachment = GL_DEPTH;
3511 attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE;
3512#endif
3513 attrib = GL_DEPTH_BITS;
3514 break;
3515 case SDL_GL_STENCIL_SIZE:
3516#if SDL_VIDEO_OPENGL
3517 attachment = GL_STENCIL;
3518 attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE;
3519#endif
3520 attrib = GL_STENCIL_BITS;
3521 break;
3522#if SDL_VIDEO_OPENGL
3523 case SDL_GL_ACCUM_RED_SIZE:
3524 attrib = GL_ACCUM_RED_BITS;
3525 break;
3526 case SDL_GL_ACCUM_GREEN_SIZE:
3527 attrib = GL_ACCUM_GREEN_BITS;
3528 break;
3529 case SDL_GL_ACCUM_BLUE_SIZE:
3530 attrib = GL_ACCUM_BLUE_BITS;
3531 break;
3532 case SDL_GL_ACCUM_ALPHA_SIZE:
3533 attrib = GL_ACCUM_ALPHA_BITS;
3534 break;
3535 case SDL_GL_STEREO:
3536 attrib = GL_STEREO;
3537 break;
3538#else
3539 case SDL_GL_ACCUM_RED_SIZE:
3540 case SDL_GL_ACCUM_GREEN_SIZE:
3541 case SDL_GL_ACCUM_BLUE_SIZE:
3542 case SDL_GL_ACCUM_ALPHA_SIZE:
3543 case SDL_GL_STEREO:
3544 /* none of these are supported in OpenGL ES */
3545 *value = 0;
3546 return 0;
3547#endif
3548 case SDL_GL_MULTISAMPLEBUFFERS:
3549 attrib = GL_SAMPLE_BUFFERS;
3550 break;
3551 case SDL_GL_MULTISAMPLESAMPLES:
3552 attrib = GL_SAMPLES;
3553 break;
3554 case SDL_GL_CONTEXT_RELEASE_BEHAVIOR:
3555#if SDL_VIDEO_OPENGL
3556 attrib = GL_CONTEXT_RELEASE_BEHAVIOR;
3557#else
3558 attrib = GL_CONTEXT_RELEASE_BEHAVIOR_KHR;
3559#endif
3560 break;
3561 case SDL_GL_BUFFER_SIZE:
3562 {
3563 int rsize = 0, gsize = 0, bsize = 0, asize = 0;
3564
3565 /* There doesn't seem to be a single flag in OpenGL for this! */
3566 if (SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &rsize) < 0) {
3567 return -1;
3568 }
3569 if (SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &gsize) < 0) {
3570 return -1;
3571 }
3572 if (SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &bsize) < 0) {
3573 return -1;
3574 }
3575 if (SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &asize) < 0) {
3576 return -1;
3577 }
3578
3579 *value = rsize + gsize + bsize + asize;
3580 return 0;
3581 }
3582 case SDL_GL_ACCELERATED_VISUAL:
3583 {
3584 /* FIXME: How do we get this information? */
3585 *value = (_this->gl_config.accelerated != 0);
3586 return 0;
3587 }
3588 case SDL_GL_RETAINED_BACKING:
3589 {
3590 *value = _this->gl_config.retained_backing;
3591 return 0;
3592 }
3593 case SDL_GL_CONTEXT_MAJOR_VERSION:
3594 {
3595 *value = _this->gl_config.major_version;
3596 return 0;
3597 }
3598 case SDL_GL_CONTEXT_MINOR_VERSION:
3599 {
3600 *value = _this->gl_config.minor_version;
3601 return 0;
3602 }
3603 case SDL_GL_CONTEXT_EGL:
3604 /* FIXME: SDL_GL_CONTEXT_EGL to be deprecated in SDL 2.1 */
3605 {
3606 if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
3607 *value = 1;
3608 }
3609 else {
3610 *value = 0;
3611 }
3612 return 0;
3613 }
3614 case SDL_GL_CONTEXT_FLAGS:
3615 {
3616 *value = _this->gl_config.flags;
3617 return 0;
3618 }
3619 case SDL_GL_CONTEXT_PROFILE_MASK:
3620 {
3621 *value = _this->gl_config.profile_mask;
3622 return 0;
3623 }
3624 case SDL_GL_SHARE_WITH_CURRENT_CONTEXT:
3625 {
3626 *value = _this->gl_config.share_with_current_context;
3627 return 0;
3628 }
3629 case SDL_GL_FRAMEBUFFER_SRGB_CAPABLE:
3630 {
3631 *value = _this->gl_config.framebuffer_srgb_capable;
3632 return 0;
3633 }
3634 case SDL_GL_CONTEXT_NO_ERROR:
3635 {
3636 *value = _this->gl_config.no_error;
3637 return 0;
3638 }
3639 default:
3640 return SDL_SetError("Unknown OpenGL attribute");
3641 }
3642
3643#if SDL_VIDEO_OPENGL
3644 glGetStringFunc = SDL_GL_GetProcAddress("glGetString");
3645 if (!glGetStringFunc) {
3646 return -1;
3647 }
3648
3649 if (attachmentattrib && isAtLeastGL3((const char *) glGetStringFunc(GL_VERSION))) {
3650 glGetFramebufferAttachmentParameterivFunc = SDL_GL_GetProcAddress("glGetFramebufferAttachmentParameteriv");
3651
3652 if (glGetFramebufferAttachmentParameterivFunc) {
3653 glGetFramebufferAttachmentParameterivFunc(GL_FRAMEBUFFER, attachment, attachmentattrib, (GLint *) value);
3654 } else {
3655 return -1;
3656 }
3657 } else
3658#endif
3659 {
3660 void (APIENTRY *glGetIntegervFunc) (GLenum pname, GLint * params);
3661 glGetIntegervFunc = SDL_GL_GetProcAddress("glGetIntegerv");
3662 if (glGetIntegervFunc) {
3663 glGetIntegervFunc(attrib, (GLint *) value);
3664 } else {
3665 return -1;
3666 }
3667 }
3668
3669 glGetErrorFunc = SDL_GL_GetProcAddress("glGetError");
3670 if (!glGetErrorFunc) {
3671 return -1;
3672 }
3673
3674 error = glGetErrorFunc();
3675 if (error != GL_NO_ERROR) {
3676 if (error == GL_INVALID_ENUM) {
3677 return SDL_SetError("OpenGL error: GL_INVALID_ENUM");
3678 } else if (error == GL_INVALID_VALUE) {
3679 return SDL_SetError("OpenGL error: GL_INVALID_VALUE");
3680 }
3681 return SDL_SetError("OpenGL error: %08X", error);
3682 }
3683 return 0;
3684#else
3685 return SDL_Unsupported();
3686#endif /* SDL_VIDEO_OPENGL */
3687}
3688
3689SDL_GLContext
3690SDL_GL_CreateContext(SDL_Window * window)
3691{
3692 SDL_GLContext ctx = NULL;
3693 CHECK_WINDOW_MAGIC(window, NULL);
3694
3695 if (!(window->flags & SDL_WINDOW_OPENGL)) {
3696 SDL_SetError("The specified window isn't an OpenGL window");
3697 return NULL;
3698 }
3699
3700 ctx = _this->GL_CreateContext(_this, window);
3701
3702 /* Creating a context is assumed to make it current in the SDL driver. */
3703 if (ctx) {
3704 _this->current_glwin = window;
3705 _this->current_glctx = ctx;
3706 SDL_TLSSet(_this->current_glwin_tls, window, NULL);
3707 SDL_TLSSet(_this->current_glctx_tls, ctx, NULL);
3708 }
3709 return ctx;
3710}
3711
3712int
3713SDL_GL_MakeCurrent(SDL_Window * window, SDL_GLContext ctx)
3714{
3715 int retval;
3716
3717 if (window == SDL_GL_GetCurrentWindow() &&
3718 ctx == SDL_GL_GetCurrentContext()) {
3719 /* We're already current. */
3720 return 0;
3721 }
3722
3723 if (!ctx) {
3724 window = NULL;
3725 } else if (window) {
3726 CHECK_WINDOW_MAGIC(window, -1);
3727
3728 if (!(window->flags & SDL_WINDOW_OPENGL)) {
3729 return SDL_SetError("The specified window isn't an OpenGL window");
3730 }
3731 } else if (!_this->gl_allow_no_surface) {
3732 return SDL_SetError("Use of OpenGL without a window is not supported on this platform");
3733 }
3734
3735 retval = _this->GL_MakeCurrent(_this, window, ctx);
3736 if (retval == 0) {
3737 _this->current_glwin = window;
3738 _this->current_glctx = ctx;
3739 SDL_TLSSet(_this->current_glwin_tls, window, NULL);
3740 SDL_TLSSet(_this->current_glctx_tls, ctx, NULL);
3741 }
3742 return retval;
3743}
3744
3745SDL_Window *
3746SDL_GL_GetCurrentWindow(void)
3747{
3748 if (!_this) {
3749 SDL_UninitializedVideo();
3750 return NULL;
3751 }
3752 return (SDL_Window *)SDL_TLSGet(_this->current_glwin_tls);
3753}
3754
3755SDL_GLContext
3756SDL_GL_GetCurrentContext(void)
3757{
3758 if (!_this) {
3759 SDL_UninitializedVideo();
3760 return NULL;
3761 }
3762 return (SDL_GLContext)SDL_TLSGet(_this->current_glctx_tls);
3763}
3764
3765void SDL_GL_GetDrawableSize(SDL_Window * window, int *w, int *h)
3766{
3767 CHECK_WINDOW_MAGIC(window,);
3768
3769 if (_this->GL_GetDrawableSize) {
3770 _this->GL_GetDrawableSize(_this, window, w, h);
3771 } else {
3772 SDL_GetWindowSize(window, w, h);
3773 }
3774}
3775
3776int
3777SDL_GL_SetSwapInterval(int interval)
3778{
3779 if (!_this) {
3780 return SDL_UninitializedVideo();
3781 } else if (SDL_GL_GetCurrentContext() == NULL) {
3782 return SDL_SetError("No OpenGL context has been made current");
3783 } else if (_this->GL_SetSwapInterval) {
3784 return _this->GL_SetSwapInterval(_this, interval);
3785 } else {
3786 return SDL_SetError("Setting the swap interval is not supported");
3787 }
3788}
3789
3790int
3791SDL_GL_GetSwapInterval(void)
3792{
3793 if (!_this) {
3794 return 0;
3795 } else if (SDL_GL_GetCurrentContext() == NULL) {
3796 return 0;
3797 } else if (_this->GL_GetSwapInterval) {
3798 return _this->GL_GetSwapInterval(_this);
3799 } else {
3800 return 0;
3801 }
3802}
3803
3804void
3805SDL_GL_SwapWindow(SDL_Window * window)
3806{
3807 CHECK_WINDOW_MAGIC(window,);
3808
3809 if (!(window->flags & SDL_WINDOW_OPENGL)) {
3810 SDL_SetError("The specified window isn't an OpenGL window");
3811 return;
3812 }
3813
3814 if (SDL_GL_GetCurrentWindow() != window) {
3815 SDL_SetError("The specified window has not been made current");
3816 return;
3817 }
3818
3819 _this->GL_SwapWindow(_this, window);
3820}
3821
3822void
3823SDL_GL_DeleteContext(SDL_GLContext context)
3824{
3825 if (!_this || !context) {
3826 return;
3827 }
3828
3829 if (SDL_GL_GetCurrentContext() == context) {
3830 SDL_GL_MakeCurrent(NULL, NULL);
3831 }
3832
3833 _this->GL_DeleteContext(_this, context);
3834}
3835
3836#if 0 /* FIXME */
3837/*
3838 * Utility function used by SDL_WM_SetIcon(); flags & 1 for color key, flags
3839 * & 2 for alpha channel.
3840 */
3841static void
3842CreateMaskFromColorKeyOrAlpha(SDL_Surface * icon, Uint8 * mask, int flags)
3843{
3844 int x, y;
3845 Uint32 colorkey;
3846#define SET_MASKBIT(icon, x, y, mask) \
3847 mask[(y*((icon->w+7)/8))+(x/8)] &= ~(0x01<<(7-(x%8)))
3848
3849 colorkey = icon->format->colorkey;
3850 switch (icon->format->BytesPerPixel) {
3851 case 1:
3852 {
3853 Uint8 *pixels;
3854 for (y = 0; y < icon->h; ++y) {
3855 pixels = (Uint8 *) icon->pixels + y * icon->pitch;
3856 for (x = 0; x < icon->w; ++x) {
3857 if (*pixels++ == colorkey) {
3858 SET_MASKBIT(icon, x, y, mask);
3859 }
3860 }
3861 }
3862 }
3863 break;
3864
3865 case 2:
3866 {
3867 Uint16 *pixels;
3868 for (y = 0; y < icon->h; ++y) {
3869 pixels = (Uint16 *) icon->pixels + y * icon->pitch / 2;
3870 for (x = 0; x < icon->w; ++x) {
3871 if ((flags & 1) && *pixels == colorkey) {
3872 SET_MASKBIT(icon, x, y, mask);
3873 } else if ((flags & 2)
3874 && (*pixels & icon->format->Amask) == 0) {
3875 SET_MASKBIT(icon, x, y, mask);
3876 }
3877 pixels++;
3878 }
3879 }
3880 }
3881 break;
3882
3883 case 4:
3884 {
3885 Uint32 *pixels;
3886 for (y = 0; y < icon->h; ++y) {
3887 pixels = (Uint32 *) icon->pixels + y * icon->pitch / 4;
3888 for (x = 0; x < icon->w; ++x) {
3889 if ((flags & 1) && *pixels == colorkey) {
3890 SET_MASKBIT(icon, x, y, mask);
3891 } else if ((flags & 2)
3892 && (*pixels & icon->format->Amask) == 0) {
3893 SET_MASKBIT(icon, x, y, mask);
3894 }
3895 pixels++;
3896 }
3897 }
3898 }
3899 break;
3900 }
3901}
3902
3903/*
3904 * Sets the window manager icon for the display window.
3905 */
3906void
3907SDL_WM_SetIcon(SDL_Surface * icon, Uint8 * mask)
3908{
3909 if (icon && _this->SetIcon) {
3910 /* Generate a mask if necessary, and create the icon! */
3911 if (mask == NULL) {
3912 int mask_len = icon->h * (icon->w + 7) / 8;
3913 int flags = 0;
3914 mask = (Uint8 *) SDL_malloc(mask_len);
3915 if (mask == NULL) {
3916 return;
3917 }
3918 SDL_memset(mask, ~0, mask_len);
3919 if (icon->flags & SDL_SRCCOLORKEY)
3920 flags |= 1;
3921 if (icon->flags & SDL_SRCALPHA)
3922 flags |= 2;
3923 if (flags) {
3924 CreateMaskFromColorKeyOrAlpha(icon, mask, flags);
3925 }
3926 _this->SetIcon(_this, icon, mask);
3927 SDL_free(mask);
3928 } else {
3929 _this->SetIcon(_this, icon, mask);
3930 }
3931 }
3932}
3933#endif
3934
3935SDL_bool
3936SDL_GetWindowWMInfo(SDL_Window * window, struct SDL_SysWMinfo *info)
3937{
3938 CHECK_WINDOW_MAGIC(window, SDL_FALSE);
3939
3940 if (!info) {
3941 SDL_InvalidParamError("info");
3942 return SDL_FALSE;
3943 }
3944 info->subsystem = SDL_SYSWM_UNKNOWN;
3945
3946 if (!_this->GetWindowWMInfo) {
3947 SDL_Unsupported();
3948 return SDL_FALSE;
3949 }
3950 return (_this->GetWindowWMInfo(_this, window, info));
3951}
3952
3953void
3954SDL_StartTextInput(void)
3955{
3956 SDL_Window *window;
3957
3958 /* First, enable text events */
3959 SDL_EventState(SDL_TEXTINPUT, SDL_ENABLE);
3960 SDL_EventState(SDL_TEXTEDITING, SDL_ENABLE);
3961
3962 /* Then show the on-screen keyboard, if any */
3963 window = SDL_GetFocusWindow();
3964 if (window && _this && _this->ShowScreenKeyboard) {
3965 _this->ShowScreenKeyboard(_this, window);
3966 }
3967
3968 /* Finally start the text input system */
3969 if (_this && _this->StartTextInput) {
3970 _this->StartTextInput(_this);
3971 }
3972}
3973
3974SDL_bool
3975SDL_IsTextInputActive(void)
3976{
3977 return (SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE);
3978}
3979
3980void
3981SDL_StopTextInput(void)
3982{
3983 SDL_Window *window;
3984
3985 /* Stop the text input system */
3986 if (_this && _this->StopTextInput) {
3987 _this->StopTextInput(_this);
3988 }
3989
3990 /* Hide the on-screen keyboard, if any */
3991 window = SDL_GetFocusWindow();
3992 if (window && _this && _this->HideScreenKeyboard) {
3993 _this->HideScreenKeyboard(_this, window);
3994 }
3995
3996 /* Finally disable text events */
3997 SDL_EventState(SDL_TEXTINPUT, SDL_DISABLE);
3998 SDL_EventState(SDL_TEXTEDITING, SDL_DISABLE);
3999}
4000
4001void
4002SDL_SetTextInputRect(SDL_Rect *rect)
4003{
4004 if (_this && _this->SetTextInputRect) {
4005 _this->SetTextInputRect(_this, rect);
4006 }
4007}
4008
4009SDL_bool
4010SDL_HasScreenKeyboardSupport(void)
4011{
4012 if (_this && _this->HasScreenKeyboardSupport) {
4013 return _this->HasScreenKeyboardSupport(_this);
4014 }
4015 return SDL_FALSE;
4016}
4017
4018SDL_bool
4019SDL_IsScreenKeyboardShown(SDL_Window *window)
4020{
4021 if (window && _this && _this->IsScreenKeyboardShown) {
4022 return _this->IsScreenKeyboardShown(_this, window);
4023 }
4024 return SDL_FALSE;
4025}
4026
4027#if SDL_VIDEO_DRIVER_ANDROID
4028#include "android/SDL_androidmessagebox.h"
4029#endif
4030#if SDL_VIDEO_DRIVER_WINDOWS
4031#include "windows/SDL_windowsmessagebox.h"
4032#endif
4033#if SDL_VIDEO_DRIVER_WINRT
4034#include "winrt/SDL_winrtmessagebox.h"
4035#endif
4036#if SDL_VIDEO_DRIVER_COCOA
4037#include "cocoa/SDL_cocoamessagebox.h"
4038#endif
4039#if SDL_VIDEO_DRIVER_UIKIT
4040#include "uikit/SDL_uikitmessagebox.h"
4041#endif
4042#if SDL_VIDEO_DRIVER_X11
4043#include "x11/SDL_x11messagebox.h"
4044#endif
4045#if SDL_VIDEO_DRIVER_WAYLAND
4046#include "wayland/SDL_waylandmessagebox.h"
4047#endif
4048#if SDL_VIDEO_DRIVER_HAIKU
4049#include "haiku/SDL_bmessagebox.h"
4050#endif
4051#if SDL_VIDEO_DRIVER_OS2
4052#include "os2/SDL_os2messagebox.h"
4053#endif
4054#if SDL_VIDEO_DRIVER_VITA
4055#include "vita/SDL_vitamessagebox.h"
4056#endif
4057
4058#if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT || SDL_VIDEO_DRIVER_COCOA || SDL_VIDEO_DRIVER_UIKIT || SDL_VIDEO_DRIVER_X11 || SDL_VIDEO_DRIVER_WAYLAND || SDL_VIDEO_DRIVER_HAIKU || SDL_VIDEO_DRIVER_OS2
4059static SDL_bool SDL_MessageboxValidForDriver(const SDL_MessageBoxData *messageboxdata, SDL_SYSWM_TYPE drivertype)
4060{
4061 SDL_SysWMinfo info;
4062 SDL_Window *window = messageboxdata->window;
4063
4064 if (!window) {
4065 return SDL_TRUE;
4066 }
4067
4068 SDL_VERSION(&info.version);
4069 if (!SDL_GetWindowWMInfo(window, &info)) {
4070 return SDL_TRUE;
4071 } else {
4072 return (info.subsystem == drivertype);
4073 }
4074}
4075#endif
4076
4077int
4078SDL_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
4079{
4080 int dummybutton;
4081 int retval = -1;
4082 SDL_bool relative_mode;
4083 int show_cursor_prev;
4084 SDL_bool mouse_captured;
4085 SDL_Window *current_window;
4086 SDL_MessageBoxData mbdata;
4087
4088 if (!messageboxdata) {
4089 return SDL_InvalidParamError("messageboxdata");
4090 } else if (messageboxdata->numbuttons < 0) {
4091 return SDL_SetError("Invalid number of buttons");
4092 }
4093
4094 current_window = SDL_GetKeyboardFocus();
4095 mouse_captured = current_window && ((SDL_GetWindowFlags(current_window) & SDL_WINDOW_MOUSE_CAPTURE) != 0);
4096 relative_mode = SDL_GetRelativeMouseMode();
4097 SDL_CaptureMouse(SDL_FALSE);
4098 SDL_SetRelativeMouseMode(SDL_FALSE);
4099 show_cursor_prev = SDL_ShowCursor(1);
4100 SDL_ResetKeyboard();
4101
4102 if (!buttonid) {
4103 buttonid = &dummybutton;
4104 }
4105
4106 SDL_memcpy(&mbdata, messageboxdata, sizeof(*messageboxdata));
4107 if (!mbdata.title) mbdata.title = "";
4108 if (!mbdata.message) mbdata.message = "";
4109 messageboxdata = &mbdata;
4110
4111 if (_this && _this->ShowMessageBox) {
4112 retval = _this->ShowMessageBox(_this, messageboxdata, buttonid);
4113 }
4114
4115 /* It's completely fine to call this function before video is initialized */
4116#if SDL_VIDEO_DRIVER_ANDROID
4117 if (retval == -1 &&
4118 Android_ShowMessageBox(messageboxdata, buttonid) == 0) {
4119 retval = 0;
4120 }
4121#endif
4122#if SDL_VIDEO_DRIVER_WINDOWS
4123 if (retval == -1 &&
4124 SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_WINDOWS) &&
4125 WIN_ShowMessageBox(messageboxdata, buttonid) == 0) {
4126 retval = 0;
4127 }
4128#endif
4129#if SDL_VIDEO_DRIVER_WINRT
4130 if (retval == -1 &&
4131 SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_WINRT) &&
4132 WINRT_ShowMessageBox(messageboxdata, buttonid) == 0) {
4133 retval = 0;
4134 }
4135#endif
4136#if SDL_VIDEO_DRIVER_COCOA
4137 if (retval == -1 &&
4138 SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_COCOA) &&
4139 Cocoa_ShowMessageBox(messageboxdata, buttonid) == 0) {
4140 retval = 0;
4141 }
4142#endif
4143#if SDL_VIDEO_DRIVER_UIKIT
4144 if (retval == -1 &&
4145 SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_UIKIT) &&
4146 UIKit_ShowMessageBox(messageboxdata, buttonid) == 0) {
4147 retval = 0;
4148 }
4149#endif
4150#if SDL_VIDEO_DRIVER_X11
4151 if (retval == -1 &&
4152 SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_X11) &&
4153 X11_ShowMessageBox(messageboxdata, buttonid) == 0) {
4154 retval = 0;
4155 }
4156#endif
4157#if SDL_VIDEO_DRIVER_WAYLAND
4158 if (retval == -1 &&
4159 SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_WAYLAND) &&
4160 Wayland_ShowMessageBox(messageboxdata, buttonid) == 0) {
4161 retval = 0;
4162 }
4163#endif
4164#if SDL_VIDEO_DRIVER_HAIKU
4165 if (retval == -1 &&
4166 SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_HAIKU) &&
4167 HAIKU_ShowMessageBox(messageboxdata, buttonid) == 0) {
4168 retval = 0;
4169 }
4170#endif
4171#if SDL_VIDEO_DRIVER_OS2
4172 if (retval == -1 &&
4173 SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_OS2) &&
4174 OS2_ShowMessageBox(messageboxdata, buttonid) == 0) {
4175 retval = 0;
4176 }
4177#endif
4178#if SDL_VIDEO_DRIVER_VITA
4179 if (retval == -1 &&
4180 VITA_ShowMessageBox(messageboxdata, buttonid) == 0) {
4181 retval = 0;
4182 }
4183#endif
4184 if (retval == -1) {
4185 SDL_SetError("No message system available");
4186 }
4187
4188 if (current_window) {
4189 SDL_RaiseWindow(current_window);
4190 if (mouse_captured) {
4191 SDL_CaptureMouse(SDL_TRUE);
4192 }
4193 }
4194
4195 SDL_ShowCursor(show_cursor_prev);
4196 SDL_SetRelativeMouseMode(relative_mode);
4197
4198 return retval;
4199}
4200
4201int
4202SDL_ShowSimpleMessageBox(Uint32 flags, const char *title, const char *message, SDL_Window *window)
4203{
4204#ifdef __EMSCRIPTEN__
4205 /* !!! FIXME: propose a browser API for this, get this #ifdef out of here? */
4206 /* Web browsers don't (currently) have an API for a custom message box
4207 that can block, but for the most common case (SDL_ShowSimpleMessageBox),
4208 we can use the standard Javascript alert() function. */
4209 if (!title) title = "";
4210 if (!message) message = "";
4211 EM_ASM_({
4212 alert(UTF8ToString($0) + "\n\n" + UTF8ToString($1));
4213 }, title, message);
4214 return 0;
4215#else
4216 SDL_MessageBoxData data;
4217 SDL_MessageBoxButtonData button;
4218
4219 SDL_zero(data);
4220 data.flags = flags;
4221 data.title = title;
4222 data.message = message;
4223 data.numbuttons = 1;
4224 data.buttons = &button;
4225 data.window = window;
4226
4227 SDL_zero(button);
4228 button.flags |= SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
4229 button.flags |= SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
4230 button.text = "OK";
4231
4232 return SDL_ShowMessageBox(&data, NULL);
4233#endif
4234}
4235
4236SDL_bool
4237SDL_ShouldAllowTopmost(void)
4238{
4239 return SDL_GetHintBoolean(SDL_HINT_ALLOW_TOPMOST, SDL_TRUE);
4240}
4241
4242int
4243SDL_SetWindowHitTest(SDL_Window * window, SDL_HitTest callback, void *userdata)
4244{
4245 CHECK_WINDOW_MAGIC(window, -1);
4246
4247 if (!_this->SetWindowHitTest) {
4248 return SDL_Unsupported();
4249 } else if (_this->SetWindowHitTest(window, callback != NULL) == -1) {
4250 return -1;
4251 }
4252
4253 window->hit_test = callback;
4254 window->hit_test_data = userdata;
4255
4256 return 0;
4257}
4258
4259float
4260SDL_ComputeDiagonalDPI(int hpix, int vpix, float hinches, float vinches)
4261{
4262 float den2 = hinches * hinches + vinches * vinches;
4263 if (den2 <= 0.0f) {
4264 return 0.0f;
4265 }
4266
4267 return (float)(SDL_sqrt((double)hpix * (double)hpix + (double)vpix * (double)vpix) /
4268 SDL_sqrt((double)den2));
4269}
4270
4271/*
4272 * Functions used by iOS application delegates
4273 */
4274void SDL_OnApplicationWillTerminate(void)
4275{
4276 SDL_SendAppEvent(SDL_APP_TERMINATING);
4277}
4278
4279void SDL_OnApplicationDidReceiveMemoryWarning(void)
4280{
4281 SDL_SendAppEvent(SDL_APP_LOWMEMORY);
4282}
4283
4284void SDL_OnApplicationWillResignActive(void)
4285{
4286 if (_this) {
4287 SDL_Window *window;
4288 for (window = _this->windows; window != NULL; window = window->next) {
4289 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
4290 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
4291 }
4292 }
4293 SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND);
4294}
4295
4296void SDL_OnApplicationDidEnterBackground(void)
4297{
4298 SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND);
4299}
4300
4301void SDL_OnApplicationWillEnterForeground(void)
4302{
4303 SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
4304}
4305
4306void SDL_OnApplicationDidBecomeActive(void)
4307{
4308 SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
4309
4310 if (_this) {
4311 SDL_Window *window;
4312 for (window = _this->windows; window != NULL; window = window->next) {
4313 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
4314 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0);
4315 }
4316 }
4317}
4318
4319#define NOT_A_VULKAN_WINDOW "The specified window isn't a Vulkan window"
4320
4321int SDL_Vulkan_LoadLibrary(const char *path)
4322{
4323 int retval;
4324 if (!_this) {
4325 SDL_UninitializedVideo();
4326 return -1;
4327 }
4328 if (_this->vulkan_config.loader_loaded) {
4329 if (path && SDL_strcmp(path, _this->vulkan_config.loader_path) != 0) {
4330 return SDL_SetError("Vulkan loader library already loaded");
4331 }
4332 retval = 0;
4333 } else {
4334 if (!_this->Vulkan_LoadLibrary) {
4335 return SDL_SetError("Vulkan support is either not configured in SDL "
4336 "or not available in current SDL video driver "
4337 "(%s) or platform", _this->name);
4338 }
4339 retval = _this->Vulkan_LoadLibrary(_this, path);
4340 }
4341 if (retval == 0) {
4342 _this->vulkan_config.loader_loaded++;
4343 }
4344 return retval;
4345}
4346
4347void *SDL_Vulkan_GetVkGetInstanceProcAddr(void)
4348{
4349 if (!_this) {
4350 SDL_UninitializedVideo();
4351 return NULL;
4352 }
4353 if (!_this->vulkan_config.loader_loaded) {
4354 SDL_SetError("No Vulkan loader has been loaded");
4355 return NULL;
4356 }
4357 return _this->vulkan_config.vkGetInstanceProcAddr;
4358}
4359
4360void SDL_Vulkan_UnloadLibrary(void)
4361{
4362 if (!_this) {
4363 SDL_UninitializedVideo();
4364 return;
4365 }
4366 if (_this->vulkan_config.loader_loaded > 0) {
4367 if (--_this->vulkan_config.loader_loaded > 0) {
4368 return;
4369 }
4370 if (_this->Vulkan_UnloadLibrary) {
4371 _this->Vulkan_UnloadLibrary(_this);
4372 }
4373 }
4374}
4375
4376SDL_bool SDL_Vulkan_GetInstanceExtensions(SDL_Window *window, unsigned *count, const char **names)
4377{
4378 if (window) {
4379 CHECK_WINDOW_MAGIC(window, SDL_FALSE);
4380
4381 if (!(window->flags & SDL_WINDOW_VULKAN))
4382 {
4383 SDL_SetError(NOT_A_VULKAN_WINDOW);
4384 return SDL_FALSE;
4385 }
4386 }
4387
4388 if (!count) {
4389 SDL_InvalidParamError("count");
4390 return SDL_FALSE;
4391 }
4392
4393 return _this->Vulkan_GetInstanceExtensions(_this, window, count, names);
4394}
4395
4396SDL_bool SDL_Vulkan_CreateSurface(SDL_Window *window,
4397 VkInstance instance,
4398 VkSurfaceKHR *surface)
4399{
4400 CHECK_WINDOW_MAGIC(window, SDL_FALSE);
4401
4402 if (!(window->flags & SDL_WINDOW_VULKAN)) {
4403 SDL_SetError(NOT_A_VULKAN_WINDOW);
4404 return SDL_FALSE;
4405 }
4406
4407 if (!instance) {
4408 SDL_InvalidParamError("instance");
4409 return SDL_FALSE;
4410 }
4411
4412 if (!surface) {
4413 SDL_InvalidParamError("surface");
4414 return SDL_FALSE;
4415 }
4416
4417 return _this->Vulkan_CreateSurface(_this, window, instance, surface);
4418}
4419
4420void SDL_Vulkan_GetDrawableSize(SDL_Window * window, int *w, int *h)
4421{
4422 CHECK_WINDOW_MAGIC(window,);
4423
4424 if (_this->Vulkan_GetDrawableSize) {
4425 _this->Vulkan_GetDrawableSize(_this, window, w, h);
4426 } else {
4427 SDL_GetWindowSize(window, w, h);
4428 }
4429}
4430
4431SDL_MetalView
4432SDL_Metal_CreateView(SDL_Window * window)
4433{
4434 CHECK_WINDOW_MAGIC(window, NULL);
4435
4436 if (!(window->flags & SDL_WINDOW_METAL)) {
4437 SDL_SetError("The specified window isn't a Metal window");
4438 return NULL;
4439 }
4440
4441 if (_this->Metal_CreateView) {
4442 return _this->Metal_CreateView(_this, window);
4443 } else {
4444 SDL_SetError("Metal is not supported.");
4445 return NULL;
4446 }
4447}
4448
4449void
4450SDL_Metal_DestroyView(SDL_MetalView view)
4451{
4452 if (_this && view && _this->Metal_DestroyView) {
4453 _this->Metal_DestroyView(_this, view);
4454 }
4455}
4456
4457void *
4458SDL_Metal_GetLayer(SDL_MetalView view)
4459{
4460 if (_this && _this->Metal_GetLayer) {
4461 if (view) {
4462 return _this->Metal_GetLayer(_this, view);
4463 } else {
4464 SDL_InvalidParamError("view");
4465 return NULL;
4466 }
4467 } else {
4468 SDL_SetError("Metal is not supported.");
4469 return NULL;
4470 }
4471}
4472
4473void SDL_Metal_GetDrawableSize(SDL_Window * window, int *w, int *h)
4474{
4475 CHECK_WINDOW_MAGIC(window,);
4476
4477 if (_this->Metal_GetDrawableSize) {
4478 _this->Metal_GetDrawableSize(_this, window, w, h);
4479 } else {
4480 SDL_GetWindowSize(window, w, h);
4481 }
4482}
4483
4484/* vi: set ts=4 sw=4 expandtab: */
4485