sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_video.c (120013B)


      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: */