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