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