src/video/SDL_video.c
author Ryan C. Gordon <icculus@icculus.org>
Tue, 05 Jan 2016 02:46:10 -0500
changeset 10025 bf4f8cde1c54
parent 10024 9a1189c7b891
child 10026 5f73f513b9f5
permissions -rw-r--r--
Added SDL_SetWindowOpacity() and SDL_GetWindowOpacity().

This is currently implemented for X11, Cocoa, Windows, and DirectFB.

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