diff options
Diffstat (limited to 'source/3rd-party/SDL2/src/video/SDL_video.c')
-rw-r--r-- | source/3rd-party/SDL2/src/video/SDL_video.c | 4161 |
1 files changed, 4161 insertions, 0 deletions
diff --git a/source/3rd-party/SDL2/src/video/SDL_video.c b/source/3rd-party/SDL2/src/video/SDL_video.c new file mode 100644 index 0000000..336fdaa --- /dev/null +++ b/source/3rd-party/SDL2/src/video/SDL_video.c @@ -0,0 +1,4161 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../SDL_internal.h" + +/* The high-level video driver subsystem */ + +#include "SDL.h" +#include "SDL_video.h" +#include "SDL_sysvideo.h" +#include "SDL_blit.h" +#include "SDL_pixels_c.h" +#include "SDL_rect_c.h" +#include "../events/SDL_events_c.h" +#include "../timer/SDL_timer_c.h" + +#include "SDL_syswm.h" + +#if SDL_VIDEO_OPENGL +#include "SDL_opengl.h" +#endif /* SDL_VIDEO_OPENGL */ + +#if SDL_VIDEO_OPENGL_ES +#include "SDL_opengles.h" +#endif /* SDL_VIDEO_OPENGL_ES */ + +/* GL and GLES2 headers conflict on Linux 32 bits */ +#if SDL_VIDEO_OPENGL_ES2 && !SDL_VIDEO_OPENGL +#include "SDL_opengles2.h" +#endif /* SDL_VIDEO_OPENGL_ES2 && !SDL_VIDEO_OPENGL */ + +#if !SDL_VIDEO_OPENGL +#ifndef GL_CONTEXT_RELEASE_BEHAVIOR_KHR +#define GL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x82FB +#endif +#endif + +#ifdef __EMSCRIPTEN__ +#include <emscripten.h> +#endif + +/* Available video drivers */ +static VideoBootStrap *bootstrap[] = { +#if SDL_VIDEO_DRIVER_COCOA + &COCOA_bootstrap, +#endif +#if SDL_VIDEO_DRIVER_X11 + &X11_bootstrap, +#endif +#if SDL_VIDEO_DRIVER_MIR + &MIR_bootstrap, +#endif +#if SDL_VIDEO_DRIVER_WAYLAND + &Wayland_bootstrap, +#endif +#if SDL_VIDEO_DRIVER_VIVANTE + &VIVANTE_bootstrap, +#endif +#if SDL_VIDEO_DRIVER_DIRECTFB + &DirectFB_bootstrap, +#endif +#if SDL_VIDEO_DRIVER_WINDOWS + &WINDOWS_bootstrap, +#endif +#if SDL_VIDEO_DRIVER_WINRT + &WINRT_bootstrap, +#endif +#if SDL_VIDEO_DRIVER_HAIKU + &HAIKU_bootstrap, +#endif +#if SDL_VIDEO_DRIVER_PANDORA + &PND_bootstrap, +#endif +#if SDL_VIDEO_DRIVER_UIKIT + &UIKIT_bootstrap, +#endif +#if SDL_VIDEO_DRIVER_ANDROID + &Android_bootstrap, +#endif +#if SDL_VIDEO_DRIVER_PSP + &PSP_bootstrap, +#endif +#if SDL_VIDEO_DRIVER_KMSDRM + &KMSDRM_bootstrap, +#endif +#if SDL_VIDEO_DRIVER_RPI + &RPI_bootstrap, +#endif +#if SDL_VIDEO_DRIVER_NACL + &NACL_bootstrap, +#endif +#if SDL_VIDEO_DRIVER_EMSCRIPTEN + &Emscripten_bootstrap, +#endif +#if SDL_VIDEO_DRIVER_QNX + &QNX_bootstrap, +#endif +#if SDL_VIDEO_DRIVER_DUMMY + &DUMMY_bootstrap, +#endif + NULL +}; + +static SDL_VideoDevice *_this = NULL; + +#define CHECK_WINDOW_MAGIC(window, retval) \ + if (!_this) { \ + SDL_UninitializedVideo(); \ + return retval; \ + } \ + SDL_assert(window && window->magic == &_this->window_magic); \ + if (!window || window->magic != &_this->window_magic) { \ + SDL_SetError("Invalid window"); \ + return retval; \ + } + +#define CHECK_DISPLAY_INDEX(displayIndex, retval) \ + if (!_this) { \ + SDL_UninitializedVideo(); \ + return retval; \ + } \ + SDL_assert(_this->displays != NULL); \ + SDL_assert(displayIndex >= 0 && displayIndex < _this->num_displays); \ + if (displayIndex < 0 || displayIndex >= _this->num_displays) { \ + SDL_SetError("displayIndex must be in the range 0 - %d", \ + _this->num_displays - 1); \ + return retval; \ + } + +#define FULLSCREEN_MASK (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN) + +#ifdef __MACOSX__ +/* Support for Mac OS X fullscreen spaces */ +extern SDL_bool Cocoa_IsWindowInFullscreenSpace(SDL_Window * window); +extern SDL_bool Cocoa_SetWindowFullscreenSpace(SDL_Window * window, SDL_bool state); +#endif + + +/* Support for framebuffer emulation using an accelerated renderer */ + +#define SDL_WINDOWTEXTUREDATA "_SDL_WindowTextureData" + +typedef struct { + SDL_Renderer *renderer; + SDL_Texture *texture; + void *pixels; + int pitch; + int bytes_per_pixel; +} SDL_WindowTextureData; + +static SDL_bool +ShouldUseTextureFramebuffer() +{ + const char *hint; + + /* If there's no native framebuffer support then there's no option */ + if (!_this->CreateWindowFramebuffer) { + return SDL_TRUE; + } + + /* If this is the dummy driver there is no texture support */ + if (_this->is_dummy) { + return SDL_FALSE; + } + + /* If the user has specified a software renderer we can't use a + texture framebuffer, or renderer creation will go recursive. + */ + hint = SDL_GetHint(SDL_HINT_RENDER_DRIVER); + if (hint && SDL_strcasecmp(hint, "software") == 0) { + return SDL_FALSE; + } + + /* See if the user or application wants a specific behavior */ + hint = SDL_GetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION); + if (hint) { + if (*hint == '0' || SDL_strcasecmp(hint, "false") == 0) { + return SDL_FALSE; + } else { + return SDL_TRUE; + } + } + + /* Each platform has different performance characteristics */ +#if defined(__WIN32__) + /* GDI BitBlt() is way faster than Direct3D dynamic textures right now. + */ + return SDL_FALSE; + +#elif defined(__MACOSX__) + /* Mac OS X uses OpenGL as the native fast path (for cocoa and X11) */ + return SDL_TRUE; + +#elif defined(__LINUX__) + /* Properly configured OpenGL drivers are faster than MIT-SHM */ +#if SDL_VIDEO_OPENGL + /* Ugh, find a way to cache this value! */ + { + SDL_Window *window; + SDL_GLContext context; + SDL_bool hasAcceleratedOpenGL = SDL_FALSE; + + window = SDL_CreateWindow("OpenGL test", -32, -32, 32, 32, SDL_WINDOW_OPENGL|SDL_WINDOW_HIDDEN); + if (window) { + context = SDL_GL_CreateContext(window); + if (context) { + const GLubyte *(APIENTRY * glGetStringFunc) (GLenum); + const char *vendor = NULL; + + glGetStringFunc = SDL_GL_GetProcAddress("glGetString"); + if (glGetStringFunc) { + vendor = (const char *) glGetStringFunc(GL_VENDOR); + } + /* Add more vendors here at will... */ + if (vendor && + (SDL_strstr(vendor, "ATI Technologies") || + SDL_strstr(vendor, "NVIDIA"))) { + hasAcceleratedOpenGL = SDL_TRUE; + } + SDL_GL_DeleteContext(context); + } + SDL_DestroyWindow(window); + } + return hasAcceleratedOpenGL; + } +#elif SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2 + /* Let's be optimistic about this! */ + return SDL_TRUE; +#else + return SDL_FALSE; +#endif + +#else + /* Play it safe, assume that if there is a framebuffer driver that it's + optimized for the current platform. + */ + return SDL_FALSE; +#endif +} + +static int +SDL_CreateWindowTexture(SDL_VideoDevice *unused, SDL_Window * window, Uint32 * format, void ** pixels, int *pitch) +{ + SDL_WindowTextureData *data; + + data = SDL_GetWindowData(window, SDL_WINDOWTEXTUREDATA); + if (!data) { + SDL_Renderer *renderer = NULL; + int i; + const char *hint = SDL_GetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION); + + /* Check to see if there's a specific driver requested */ + if (hint && *hint != '0' && *hint != '1' && + SDL_strcasecmp(hint, "true") != 0 && + SDL_strcasecmp(hint, "false") != 0 && + SDL_strcasecmp(hint, "software") != 0) { + for (i = 0; i < SDL_GetNumRenderDrivers(); ++i) { + SDL_RendererInfo info; + SDL_GetRenderDriverInfo(i, &info); + if (SDL_strcasecmp(info.name, hint) == 0) { + renderer = SDL_CreateRenderer(window, i, 0); + break; + } + } + } + + if (!renderer) { + for (i = 0; i < SDL_GetNumRenderDrivers(); ++i) { + SDL_RendererInfo info; + SDL_GetRenderDriverInfo(i, &info); + if (SDL_strcmp(info.name, "software") != 0) { + renderer = SDL_CreateRenderer(window, i, 0); + if (renderer) { + break; + } + } + } + } + if (!renderer) { + return SDL_SetError("No hardware accelerated renderers available"); + } + + /* Create the data after we successfully create the renderer (bug #1116) */ + data = (SDL_WindowTextureData *)SDL_calloc(1, sizeof(*data)); + if (!data) { + SDL_DestroyRenderer(renderer); + return SDL_OutOfMemory(); + } + SDL_SetWindowData(window, SDL_WINDOWTEXTUREDATA, data); + + data->renderer = renderer; + } + + /* Free any old texture and pixel data */ + if (data->texture) { + SDL_DestroyTexture(data->texture); + data->texture = NULL; + } + SDL_free(data->pixels); + data->pixels = NULL; + + { + SDL_RendererInfo info; + Uint32 i; + + if (SDL_GetRendererInfo(data->renderer, &info) < 0) { + return -1; + } + + /* Find the first format without an alpha channel */ + *format = info.texture_formats[0]; + + for (i = 0; i < info.num_texture_formats; ++i) { + if (!SDL_ISPIXELFORMAT_FOURCC(info.texture_formats[i]) && + !SDL_ISPIXELFORMAT_ALPHA(info.texture_formats[i])) { + *format = info.texture_formats[i]; + break; + } + } + } + + data->texture = SDL_CreateTexture(data->renderer, *format, + SDL_TEXTUREACCESS_STREAMING, + window->w, window->h); + if (!data->texture) { + return -1; + } + + /* Create framebuffer data */ + data->bytes_per_pixel = SDL_BYTESPERPIXEL(*format); + data->pitch = (((window->w * data->bytes_per_pixel) + 3) & ~3); + + { + /* Make static analysis happy about potential malloc(0) calls. */ + const size_t allocsize = window->h * data->pitch; + data->pixels = SDL_malloc((allocsize > 0) ? allocsize : 1); + if (!data->pixels) { + return SDL_OutOfMemory(); + } + } + + *pixels = data->pixels; + *pitch = data->pitch; + + /* Make sure we're not double-scaling the viewport */ + SDL_RenderSetViewport(data->renderer, NULL); + + return 0; +} + +static int +SDL_UpdateWindowTexture(SDL_VideoDevice *unused, SDL_Window * window, const SDL_Rect * rects, int numrects) +{ + SDL_WindowTextureData *data; + SDL_Rect rect; + void *src; + + data = SDL_GetWindowData(window, SDL_WINDOWTEXTUREDATA); + if (!data || !data->texture) { + return SDL_SetError("No window texture data"); + } + + /* Update a single rect that contains subrects for best DMA performance */ + if (SDL_GetSpanEnclosingRect(window->w, window->h, numrects, rects, &rect)) { + src = (void *)((Uint8 *)data->pixels + + rect.y * data->pitch + + rect.x * data->bytes_per_pixel); + if (SDL_UpdateTexture(data->texture, &rect, src, data->pitch) < 0) { + return -1; + } + + if (SDL_RenderCopy(data->renderer, data->texture, NULL, NULL) < 0) { + return -1; + } + + SDL_RenderPresent(data->renderer); + } + return 0; +} + +static void +SDL_DestroyWindowTexture(SDL_VideoDevice *unused, SDL_Window * window) +{ + SDL_WindowTextureData *data; + + data = SDL_SetWindowData(window, SDL_WINDOWTEXTUREDATA, NULL); + if (!data) { + return; + } + if (data->texture) { + SDL_DestroyTexture(data->texture); + } + if (data->renderer) { + SDL_DestroyRenderer(data->renderer); + } + SDL_free(data->pixels); + SDL_free(data); +} + + +static int +cmpmodes(const void *A, const void *B) +{ + const SDL_DisplayMode *a = (const SDL_DisplayMode *) A; + const SDL_DisplayMode *b = (const SDL_DisplayMode *) B; + if (a == b) { + return 0; + } else if (a->w != b->w) { + return b->w - a->w; + } else if (a->h != b->h) { + return b->h - a->h; + } else if (SDL_BITSPERPIXEL(a->format) != SDL_BITSPERPIXEL(b->format)) { + return SDL_BITSPERPIXEL(b->format) - SDL_BITSPERPIXEL(a->format); + } else if (SDL_PIXELLAYOUT(a->format) != SDL_PIXELLAYOUT(b->format)) { + return SDL_PIXELLAYOUT(b->format) - SDL_PIXELLAYOUT(a->format); + } else if (a->refresh_rate != b->refresh_rate) { + return b->refresh_rate - a->refresh_rate; + } + return 0; +} + +static int +SDL_UninitializedVideo() +{ + return SDL_SetError("Video subsystem has not been initialized"); +} + +int +SDL_GetNumVideoDrivers(void) +{ + return SDL_arraysize(bootstrap) - 1; +} + +const char * +SDL_GetVideoDriver(int index) +{ + if (index >= 0 && index < SDL_GetNumVideoDrivers()) { + return bootstrap[index]->name; + } + return NULL; +} + +/* + * Initialize the video and event subsystems -- determine native pixel format + */ +int +SDL_VideoInit(const char *driver_name) +{ + SDL_VideoDevice *video; + int index; + int i; + + /* Check to make sure we don't overwrite '_this' */ + if (_this != NULL) { + SDL_VideoQuit(); + } + +#if !SDL_TIMERS_DISABLED + SDL_TicksInit(); +#endif + + /* Start the event loop */ + if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0 || + SDL_KeyboardInit() < 0 || + SDL_MouseInit() < 0 || + SDL_TouchInit() < 0) { + return -1; + } + + /* Select the proper video driver */ + index = 0; + video = NULL; + if (driver_name == NULL) { + driver_name = SDL_getenv("SDL_VIDEODRIVER"); + } + if (driver_name != NULL) { + for (i = 0; bootstrap[i]; ++i) { + if (SDL_strncasecmp(bootstrap[i]->name, driver_name, SDL_strlen(driver_name)) == 0) { + if (bootstrap[i]->available()) { + video = bootstrap[i]->create(index); + break; + } + } + } + } else { + for (i = 0; bootstrap[i]; ++i) { + if (bootstrap[i]->available()) { + video = bootstrap[i]->create(index); + if (video != NULL) { + break; + } + } + } + } + if (video == NULL) { + if (driver_name) { + return SDL_SetError("%s not available", driver_name); + } + return SDL_SetError("No available video device"); + } + _this = video; + _this->name = bootstrap[i]->name; + _this->next_object_id = 1; + + + /* Set some very sane GL defaults */ + _this->gl_config.driver_loaded = 0; + _this->gl_config.dll_handle = NULL; + SDL_GL_ResetAttributes(); + + _this->current_glwin_tls = SDL_TLSCreate(); + _this->current_glctx_tls = SDL_TLSCreate(); + + /* Initialize the video subsystem */ + if (_this->VideoInit(_this) < 0) { + SDL_VideoQuit(); + return -1; + } + + /* Make sure some displays were added */ + if (_this->num_displays == 0) { + SDL_VideoQuit(); + return SDL_SetError("The video driver did not add any displays"); + } + + /* Add the renderer framebuffer emulation if desired */ + if (ShouldUseTextureFramebuffer()) { + _this->CreateWindowFramebuffer = SDL_CreateWindowTexture; + _this->UpdateWindowFramebuffer = SDL_UpdateWindowTexture; + _this->DestroyWindowFramebuffer = SDL_DestroyWindowTexture; + } + + /* Disable the screen saver by default. This is a change from <= 2.0.1, + but most things using SDL are games or media players; you wouldn't + want a screensaver to trigger if you're playing exclusively with a + joystick, or passively watching a movie. Things that use SDL but + function more like a normal desktop app should explicitly reenable the + screensaver. */ + if (!SDL_GetHintBoolean(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, SDL_FALSE)) { + SDL_DisableScreenSaver(); + } + + /* If we don't use a screen keyboard, turn on text input by default, + otherwise programs that expect to get text events without enabling + UNICODE input won't get any events. + + Actually, come to think of it, you needed to call SDL_EnableUNICODE(1) + in SDL 1.2 before you got text input events. Hmm... + */ + if (!SDL_HasScreenKeyboardSupport()) { + SDL_StartTextInput(); + } + + /* We're ready to go! */ + return 0; +} + +const char * +SDL_GetCurrentVideoDriver() +{ + if (!_this) { + SDL_UninitializedVideo(); + return NULL; + } + return _this->name; +} + +SDL_VideoDevice * +SDL_GetVideoDevice(void) +{ + return _this; +} + +int +SDL_AddBasicVideoDisplay(const SDL_DisplayMode * desktop_mode) +{ + SDL_VideoDisplay display; + + SDL_zero(display); + if (desktop_mode) { + display.desktop_mode = *desktop_mode; + } + display.current_mode = display.desktop_mode; + + return SDL_AddVideoDisplay(&display); +} + +int +SDL_AddVideoDisplay(const SDL_VideoDisplay * display) +{ + SDL_VideoDisplay *displays; + int index = -1; + + displays = + SDL_realloc(_this->displays, + (_this->num_displays + 1) * sizeof(*displays)); + if (displays) { + index = _this->num_displays++; + displays[index] = *display; + displays[index].device = _this; + _this->displays = displays; + + if (display->name) { + displays[index].name = SDL_strdup(display->name); + } else { + char name[32]; + + SDL_itoa(index, name, 10); + displays[index].name = SDL_strdup(name); + } + } else { + SDL_OutOfMemory(); + } + return index; +} + +int +SDL_GetNumVideoDisplays(void) +{ + if (!_this) { + SDL_UninitializedVideo(); + return 0; + } + return _this->num_displays; +} + +int +SDL_GetIndexOfDisplay(SDL_VideoDisplay *display) +{ + int displayIndex; + + for (displayIndex = 0; displayIndex < _this->num_displays; ++displayIndex) { + if (display == &_this->displays[displayIndex]) { + return displayIndex; + } + } + + /* Couldn't find the display, just use index 0 */ + return 0; +} + +void * +SDL_GetDisplayDriverData(int displayIndex) +{ + CHECK_DISPLAY_INDEX(displayIndex, NULL); + + return _this->displays[displayIndex].driverdata; +} + +const char * +SDL_GetDisplayName(int displayIndex) +{ + CHECK_DISPLAY_INDEX(displayIndex, NULL); + + return _this->displays[displayIndex].name; +} + +int +SDL_GetDisplayBounds(int displayIndex, SDL_Rect * rect) +{ + CHECK_DISPLAY_INDEX(displayIndex, -1); + + if (rect) { + SDL_VideoDisplay *display = &_this->displays[displayIndex]; + + if (_this->GetDisplayBounds) { + if (_this->GetDisplayBounds(_this, display, rect) == 0) { + return 0; + } + } + + /* Assume that the displays are left to right */ + if (displayIndex == 0) { + rect->x = 0; + rect->y = 0; + } else { + SDL_GetDisplayBounds(displayIndex-1, rect); + rect->x += rect->w; + } + rect->w = display->current_mode.w; + rect->h = display->current_mode.h; + } + return 0; /* !!! FIXME: should this be an error if (rect==NULL) ? */ +} + +int SDL_GetDisplayUsableBounds(int displayIndex, SDL_Rect * rect) +{ + CHECK_DISPLAY_INDEX(displayIndex, -1); + + if (rect) { + SDL_VideoDisplay *display = &_this->displays[displayIndex]; + + if (_this->GetDisplayUsableBounds) { + if (_this->GetDisplayUsableBounds(_this, display, rect) == 0) { + return 0; + } + } + + /* Oh well, just give the entire display bounds. */ + return SDL_GetDisplayBounds(displayIndex, rect); + } + return 0; /* !!! FIXME: should this be an error if (rect==NULL) ? */ +} + +int +SDL_GetDisplayDPI(int displayIndex, float * ddpi, float * hdpi, float * vdpi) +{ + SDL_VideoDisplay *display; + + CHECK_DISPLAY_INDEX(displayIndex, -1); + + display = &_this->displays[displayIndex]; + + if (_this->GetDisplayDPI) { + if (_this->GetDisplayDPI(_this, display, ddpi, hdpi, vdpi) == 0) { + return 0; + } + } else { + return SDL_Unsupported(); + } + + return -1; +} + +SDL_DisplayOrientation +SDL_GetDisplayOrientation(int displayIndex) +{ + SDL_VideoDisplay *display; + + CHECK_DISPLAY_INDEX(displayIndex, SDL_ORIENTATION_UNKNOWN); + + display = &_this->displays[displayIndex]; + return display->orientation; +} + +SDL_bool +SDL_AddDisplayMode(SDL_VideoDisplay * display, const SDL_DisplayMode * mode) +{ + SDL_DisplayMode *modes; + int i, nmodes; + + /* Make sure we don't already have the mode in the list */ + modes = display->display_modes; + nmodes = display->num_display_modes; + for (i = 0; i < nmodes; ++i) { + if (cmpmodes(mode, &modes[i]) == 0) { + return SDL_FALSE; + } + } + + /* Go ahead and add the new mode */ + if (nmodes == display->max_display_modes) { + modes = + SDL_realloc(modes, + (display->max_display_modes + 32) * sizeof(*modes)); + if (!modes) { + return SDL_FALSE; + } + display->display_modes = modes; + display->max_display_modes += 32; + } + modes[nmodes] = *mode; + display->num_display_modes++; + + /* Re-sort video modes */ + SDL_qsort(display->display_modes, display->num_display_modes, + sizeof(SDL_DisplayMode), cmpmodes); + + return SDL_TRUE; +} + +static int +SDL_GetNumDisplayModesForDisplay(SDL_VideoDisplay * display) +{ + if (!display->num_display_modes && _this->GetDisplayModes) { + _this->GetDisplayModes(_this, display); + SDL_qsort(display->display_modes, display->num_display_modes, + sizeof(SDL_DisplayMode), cmpmodes); + } + return display->num_display_modes; +} + +int +SDL_GetNumDisplayModes(int displayIndex) +{ + CHECK_DISPLAY_INDEX(displayIndex, -1); + + return SDL_GetNumDisplayModesForDisplay(&_this->displays[displayIndex]); +} + +int +SDL_GetDisplayMode(int displayIndex, int index, SDL_DisplayMode * mode) +{ + SDL_VideoDisplay *display; + + CHECK_DISPLAY_INDEX(displayIndex, -1); + + display = &_this->displays[displayIndex]; + if (index < 0 || index >= SDL_GetNumDisplayModesForDisplay(display)) { + return SDL_SetError("index must be in the range of 0 - %d", + SDL_GetNumDisplayModesForDisplay(display) - 1); + } + if (mode) { + *mode = display->display_modes[index]; + } + return 0; +} + +int +SDL_GetDesktopDisplayMode(int displayIndex, SDL_DisplayMode * mode) +{ + SDL_VideoDisplay *display; + + CHECK_DISPLAY_INDEX(displayIndex, -1); + + display = &_this->displays[displayIndex]; + if (mode) { + *mode = display->desktop_mode; + } + return 0; +} + +int +SDL_GetCurrentDisplayMode(int displayIndex, SDL_DisplayMode * mode) +{ + SDL_VideoDisplay *display; + + CHECK_DISPLAY_INDEX(displayIndex, -1); + + display = &_this->displays[displayIndex]; + if (mode) { + *mode = display->current_mode; + } + return 0; +} + +static SDL_DisplayMode * +SDL_GetClosestDisplayModeForDisplay(SDL_VideoDisplay * display, + const SDL_DisplayMode * mode, + SDL_DisplayMode * closest) +{ + Uint32 target_format; + int target_refresh_rate; + int i; + SDL_DisplayMode *current, *match; + + if (!mode || !closest) { + SDL_SetError("Missing desired mode or closest mode parameter"); + return NULL; + } + + /* Default to the desktop format */ + if (mode->format) { + target_format = mode->format; + } else { + target_format = display->desktop_mode.format; + } + + /* Default to the desktop refresh rate */ + if (mode->refresh_rate) { + target_refresh_rate = mode->refresh_rate; + } else { + target_refresh_rate = display->desktop_mode.refresh_rate; + } + + match = NULL; + for (i = 0; i < SDL_GetNumDisplayModesForDisplay(display); ++i) { + current = &display->display_modes[i]; + + if (current->w && (current->w < mode->w)) { + /* Out of sorted modes large enough here */ + break; + } + if (current->h && (current->h < mode->h)) { + if (current->w && (current->w == mode->w)) { + /* Out of sorted modes large enough here */ + break; + } + /* Wider, but not tall enough, due to a different + aspect ratio. This mode must be skipped, but closer + modes may still follow. */ + continue; + } + if (!match || current->w < match->w || current->h < match->h) { + match = current; + continue; + } + if (current->format != match->format) { + /* Sorted highest depth to lowest */ + if (current->format == target_format || + (SDL_BITSPERPIXEL(current->format) >= + SDL_BITSPERPIXEL(target_format) + && SDL_PIXELTYPE(current->format) == + SDL_PIXELTYPE(target_format))) { + match = current; + } + continue; + } + if (current->refresh_rate != match->refresh_rate) { + /* Sorted highest refresh to lowest */ + if (current->refresh_rate >= target_refresh_rate) { + match = current; + } + } + } + if (match) { + if (match->format) { + closest->format = match->format; + } else { + closest->format = mode->format; + } + if (match->w && match->h) { + closest->w = match->w; + closest->h = match->h; + } else { + closest->w = mode->w; + closest->h = mode->h; + } + if (match->refresh_rate) { + closest->refresh_rate = match->refresh_rate; + } else { + closest->refresh_rate = mode->refresh_rate; + } + closest->driverdata = match->driverdata; + + /* + * Pick some reasonable defaults if the app and driver don't + * care + */ + if (!closest->format) { + closest->format = SDL_PIXELFORMAT_RGB888; + } + if (!closest->w) { + closest->w = 640; + } + if (!closest->h) { + closest->h = 480; + } + return closest; + } + return NULL; +} + +SDL_DisplayMode * +SDL_GetClosestDisplayMode(int displayIndex, + const SDL_DisplayMode * mode, + SDL_DisplayMode * closest) +{ + SDL_VideoDisplay *display; + + CHECK_DISPLAY_INDEX(displayIndex, NULL); + + display = &_this->displays[displayIndex]; + return SDL_GetClosestDisplayModeForDisplay(display, mode, closest); +} + +static int +SDL_SetDisplayModeForDisplay(SDL_VideoDisplay * display, const SDL_DisplayMode * mode) +{ + SDL_DisplayMode display_mode; + SDL_DisplayMode current_mode; + + if (mode) { + display_mode = *mode; + + /* Default to the current mode */ + if (!display_mode.format) { + display_mode.format = display->current_mode.format; + } + if (!display_mode.w) { + display_mode.w = display->current_mode.w; + } + if (!display_mode.h) { + display_mode.h = display->current_mode.h; + } + if (!display_mode.refresh_rate) { + display_mode.refresh_rate = display->current_mode.refresh_rate; + } + + /* Get a good video mode, the closest one possible */ + if (!SDL_GetClosestDisplayModeForDisplay(display, &display_mode, &display_mode)) { + return SDL_SetError("No video mode large enough for %dx%d", + display_mode.w, display_mode.h); + } + } else { + display_mode = display->desktop_mode; + } + + /* See if there's anything left to do */ + current_mode = display->current_mode; + if (SDL_memcmp(&display_mode, ¤t_mode, sizeof(display_mode)) == 0) { + return 0; + } + + /* Actually change the display mode */ + if (!_this->SetDisplayMode) { + return SDL_SetError("SDL video driver doesn't support changing display mode"); + } + if (_this->SetDisplayMode(_this, display, &display_mode) < 0) { + return -1; + } + display->current_mode = display_mode; + return 0; +} + +SDL_VideoDisplay * +SDL_GetDisplay(int displayIndex) +{ + CHECK_DISPLAY_INDEX(displayIndex, NULL); + + return &_this->displays[displayIndex]; +} + +int +SDL_GetWindowDisplayIndex(SDL_Window * window) +{ + int displayIndex; + int i, dist; + int closest = -1; + int closest_dist = 0x7FFFFFFF; + SDL_Point center; + SDL_Point delta; + SDL_Rect rect; + + CHECK_WINDOW_MAGIC(window, -1); + + if (SDL_WINDOWPOS_ISUNDEFINED(window->x) || + SDL_WINDOWPOS_ISCENTERED(window->x)) { + displayIndex = (window->x & 0xFFFF); + if (displayIndex >= _this->num_displays) { + displayIndex = 0; + } + return displayIndex; + } + if (SDL_WINDOWPOS_ISUNDEFINED(window->y) || + SDL_WINDOWPOS_ISCENTERED(window->y)) { + displayIndex = (window->y & 0xFFFF); + if (displayIndex >= _this->num_displays) { + displayIndex = 0; + } + return displayIndex; + } + + /* Find the display containing the window */ + for (i = 0; i < _this->num_displays; ++i) { + SDL_VideoDisplay *display = &_this->displays[i]; + + if (display->fullscreen_window == window) { + return i; + } + } + center.x = window->x + window->w / 2; + center.y = window->y + window->h / 2; + for (i = 0; i < _this->num_displays; ++i) { + SDL_GetDisplayBounds(i, &rect); + if (SDL_EnclosePoints(¢er, 1, &rect, NULL)) { + return i; + } + + delta.x = center.x - (rect.x + rect.w / 2); + delta.y = center.y - (rect.y + rect.h / 2); + dist = (delta.x*delta.x + delta.y*delta.y); + if (dist < closest_dist) { + closest = i; + closest_dist = dist; + } + } + if (closest < 0) { + SDL_SetError("Couldn't find any displays"); + } + return closest; +} + +SDL_VideoDisplay * +SDL_GetDisplayForWindow(SDL_Window *window) +{ + int displayIndex = SDL_GetWindowDisplayIndex(window); + if (displayIndex >= 0) { + return &_this->displays[displayIndex]; + } else { + return NULL; + } +} + +int +SDL_SetWindowDisplayMode(SDL_Window * window, const SDL_DisplayMode * mode) +{ + CHECK_WINDOW_MAGIC(window, -1); + + if (mode) { + window->fullscreen_mode = *mode; + } else { + SDL_zero(window->fullscreen_mode); + } + + if (FULLSCREEN_VISIBLE(window) && (window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP) { + SDL_DisplayMode fullscreen_mode; + if (SDL_GetWindowDisplayMode(window, &fullscreen_mode) == 0) { + SDL_SetDisplayModeForDisplay(SDL_GetDisplayForWindow(window), &fullscreen_mode); + } + } + return 0; +} + +int +SDL_GetWindowDisplayMode(SDL_Window * window, SDL_DisplayMode * mode) +{ + SDL_DisplayMode fullscreen_mode; + SDL_VideoDisplay *display; + + CHECK_WINDOW_MAGIC(window, -1); + + if (!mode) { + return SDL_InvalidParamError("mode"); + } + + fullscreen_mode = window->fullscreen_mode; + if (!fullscreen_mode.w) { + fullscreen_mode.w = window->windowed.w; + } + if (!fullscreen_mode.h) { + fullscreen_mode.h = window->windowed.h; + } + + display = SDL_GetDisplayForWindow(window); + + /* if in desktop size mode, just return the size of the desktop */ + if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) { + fullscreen_mode = display->desktop_mode; + } else if (!SDL_GetClosestDisplayModeForDisplay(SDL_GetDisplayForWindow(window), + &fullscreen_mode, + &fullscreen_mode)) { + return SDL_SetError("Couldn't find display mode match"); + } + + if (mode) { + *mode = fullscreen_mode; + } + return 0; +} + +Uint32 +SDL_GetWindowPixelFormat(SDL_Window * window) +{ + SDL_VideoDisplay *display; + + CHECK_WINDOW_MAGIC(window, SDL_PIXELFORMAT_UNKNOWN); + + display = SDL_GetDisplayForWindow(window); + return display->current_mode.format; +} + +static void +SDL_RestoreMousePosition(SDL_Window *window) +{ + int x, y; + + if (window == SDL_GetMouseFocus()) { + SDL_GetMouseState(&x, &y); + SDL_WarpMouseInWindow(window, x, y); + } +} + +#if __WINRT__ +extern Uint32 WINRT_DetectWindowFlags(SDL_Window * window); +#endif + +static int +SDL_UpdateFullscreenMode(SDL_Window * window, SDL_bool fullscreen) +{ + SDL_VideoDisplay *display; + SDL_Window *other; + + CHECK_WINDOW_MAGIC(window,-1); + + /* if we are in the process of hiding don't go back to fullscreen */ + if (window->is_hiding && fullscreen) { + return 0; + } + +#ifdef __MACOSX__ + /* if the window is going away and no resolution change is necessary, + do nothing, or else we may trigger an ugly double-transition + */ + if (SDL_strcmp(_this->name, "cocoa") == 0) { /* don't do this for X11, etc */ + if (window->is_destroying && (window->last_fullscreen_flags & FULLSCREEN_MASK) == SDL_WINDOW_FULLSCREEN_DESKTOP) + return 0; + + /* If we're switching between a fullscreen Space and "normal" fullscreen, we need to get back to normal first. */ + if (fullscreen && ((window->last_fullscreen_flags & FULLSCREEN_MASK) == SDL_WINDOW_FULLSCREEN_DESKTOP) && ((window->flags & FULLSCREEN_MASK) == SDL_WINDOW_FULLSCREEN)) { + if (!Cocoa_SetWindowFullscreenSpace(window, SDL_FALSE)) { + return -1; + } + } else if (fullscreen && ((window->last_fullscreen_flags & FULLSCREEN_MASK) == SDL_WINDOW_FULLSCREEN) && ((window->flags & FULLSCREEN_MASK) == SDL_WINDOW_FULLSCREEN_DESKTOP)) { + display = SDL_GetDisplayForWindow(window); + SDL_SetDisplayModeForDisplay(display, NULL); + if (_this->SetWindowFullscreen) { + _this->SetWindowFullscreen(_this, window, display, SDL_FALSE); + } + } + + if (Cocoa_SetWindowFullscreenSpace(window, fullscreen)) { + if (Cocoa_IsWindowInFullscreenSpace(window) != fullscreen) { + return -1; + } + window->last_fullscreen_flags = window->flags; + return 0; + } + } +#elif __WINRT__ && (NTDDI_VERSION < NTDDI_WIN10) + /* HACK: WinRT 8.x apps can't choose whether or not they are fullscreen + or not. The user can choose this, via OS-provided UI, but this can't + be set programmatically. + + Just look at what SDL's WinRT video backend code detected with regards + to fullscreen (being active, or not), and figure out a return/error code + from that. + */ + if (fullscreen == !(WINRT_DetectWindowFlags(window) & FULLSCREEN_MASK)) { + /* Uh oh, either: + 1. fullscreen was requested, and we're already windowed + 2. windowed-mode was requested, and we're already fullscreen + + WinRT 8.x can't resolve either programmatically, so we're + giving up. + */ + return -1; + } else { + /* Whatever was requested, fullscreen or windowed mode, is already + in-place. + */ + return 0; + } +#endif + + display = SDL_GetDisplayForWindow(window); + + if (fullscreen) { + /* Hide any other fullscreen windows */ + if (display->fullscreen_window && + display->fullscreen_window != window) { + SDL_MinimizeWindow(display->fullscreen_window); + } + } + + /* See if anything needs to be done now */ + if ((display->fullscreen_window == window) == fullscreen) { + if ((window->last_fullscreen_flags & FULLSCREEN_MASK) == (window->flags & FULLSCREEN_MASK)) { + return 0; + } + } + + /* See if there are any fullscreen windows */ + for (other = _this->windows; other; other = other->next) { + SDL_bool setDisplayMode = SDL_FALSE; + + if (other == window) { + setDisplayMode = fullscreen; + } else if (FULLSCREEN_VISIBLE(other) && + SDL_GetDisplayForWindow(other) == display) { + setDisplayMode = SDL_TRUE; + } + + if (setDisplayMode) { + SDL_DisplayMode fullscreen_mode; + + SDL_zero(fullscreen_mode); + + if (SDL_GetWindowDisplayMode(other, &fullscreen_mode) == 0) { + SDL_bool resized = SDL_TRUE; + + if (other->w == fullscreen_mode.w && other->h == fullscreen_mode.h) { + resized = SDL_FALSE; + } + + /* only do the mode change if we want exclusive fullscreen */ + if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP) { + if (SDL_SetDisplayModeForDisplay(display, &fullscreen_mode) < 0) { + return -1; + } + } else { + if (SDL_SetDisplayModeForDisplay(display, NULL) < 0) { + return -1; + } + } + + if (_this->SetWindowFullscreen) { + _this->SetWindowFullscreen(_this, other, display, SDL_TRUE); + } + display->fullscreen_window = other; + + /* Generate a mode change event here */ + if (resized) { +#ifndef ANDROID + // Android may not resize the window to exactly what our fullscreen mode is, especially on + // windowed Android environments like the Chromebook or Samsung DeX. Given this, we shouldn't + // use fullscreen_mode.w and fullscreen_mode.h, but rather get our current native size. As such, + // Android's SetWindowFullscreen will generate the window event for us with the proper final size. + + SDL_SendWindowEvent(other, SDL_WINDOWEVENT_RESIZED, + fullscreen_mode.w, fullscreen_mode.h); +#endif + } else { + SDL_OnWindowResized(other); + } + + SDL_RestoreMousePosition(other); + + window->last_fullscreen_flags = window->flags; + return 0; + } + } + } + + /* Nope, restore the desktop mode */ + SDL_SetDisplayModeForDisplay(display, NULL); + + if (_this->SetWindowFullscreen) { + _this->SetWindowFullscreen(_this, window, display, SDL_FALSE); + } + display->fullscreen_window = NULL; + + /* Generate a mode change event here */ + SDL_OnWindowResized(window); + + /* Restore the cursor position */ + SDL_RestoreMousePosition(window); + + window->last_fullscreen_flags = window->flags; + return 0; +} + +#define CREATE_FLAGS \ + (SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_SKIP_TASKBAR | SDL_WINDOW_POPUP_MENU | SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_VULKAN | SDL_WINDOW_MINIMIZED) + +static SDL_INLINE SDL_bool +IsAcceptingDragAndDrop(void) +{ + if ((SDL_GetEventState(SDL_DROPFILE) == SDL_ENABLE) || + (SDL_GetEventState(SDL_DROPTEXT) == SDL_ENABLE)) { + return SDL_TRUE; + } + return SDL_FALSE; +} + +/* prepare a newly-created window */ +static SDL_INLINE void +PrepareDragAndDropSupport(SDL_Window *window) +{ + if (_this->AcceptDragAndDrop) { + _this->AcceptDragAndDrop(window, IsAcceptingDragAndDrop()); + } +} + +/* toggle d'n'd for all existing windows. */ +void +SDL_ToggleDragAndDropSupport(void) +{ + if (_this && _this->AcceptDragAndDrop) { + const SDL_bool enable = IsAcceptingDragAndDrop(); + SDL_Window *window; + for (window = _this->windows; window; window = window->next) { + _this->AcceptDragAndDrop(window, enable); + } + } +} + +static void +SDL_FinishWindowCreation(SDL_Window *window, Uint32 flags) +{ + PrepareDragAndDropSupport(window); + + if (flags & SDL_WINDOW_MAXIMIZED) { + SDL_MaximizeWindow(window); + } + if (flags & SDL_WINDOW_MINIMIZED) { + SDL_MinimizeWindow(window); + } + if (flags & SDL_WINDOW_FULLSCREEN) { + SDL_SetWindowFullscreen(window, flags); + } + if (flags & SDL_WINDOW_INPUT_GRABBED) { + SDL_SetWindowGrab(window, SDL_TRUE); + } + if (!(flags & SDL_WINDOW_HIDDEN)) { + SDL_ShowWindow(window); + } +} + +SDL_Window * +SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags) +{ + SDL_Window *window; + + if (!_this) { + /* Initialize the video system if needed */ + if (SDL_VideoInit(NULL) < 0) { + return NULL; + } + } + + if ((((flags & SDL_WINDOW_UTILITY) != 0) + ((flags & SDL_WINDOW_TOOLTIP) != 0) + ((flags & SDL_WINDOW_POPUP_MENU) != 0)) > 1) { + SDL_SetError("Conflicting window flags specified"); + return NULL; + } + + /* Some platforms can't create zero-sized windows */ + if (w < 1) { + w = 1; + } + if (h < 1) { + h = 1; + } + + /* Some platforms blow up if the windows are too large. Raise it later? */ + if ((w > 16384) || (h > 16384)) { + SDL_SetError("Window is too large."); + return NULL; + } + + /* Some platforms have OpenGL enabled by default */ +#if (SDL_VIDEO_OPENGL && __MACOSX__) || __IPHONEOS__ || __ANDROID__ || __NACL__ + if (!_this->is_dummy && !(flags & SDL_WINDOW_VULKAN)) { + flags |= SDL_WINDOW_OPENGL; + } +#endif + if (flags & SDL_WINDOW_OPENGL) { + if (!_this->GL_CreateContext) { + SDL_SetError("OpenGL support is either not configured in SDL " + "or not available in current SDL video driver " + "(%s) or platform", _this->name); + return NULL; + } + if (SDL_GL_LoadLibrary(NULL) < 0) { + return NULL; + } + } + + if (flags & SDL_WINDOW_VULKAN) { + if (!_this->Vulkan_CreateSurface) { + SDL_SetError("Vulkan support is either not configured in SDL " + "or not available in current SDL video driver " + "(%s) or platform", _this->name); + return NULL; + } + if (flags & SDL_WINDOW_OPENGL) { + SDL_SetError("Vulkan and OpenGL not supported on same window"); + return NULL; + } + if (SDL_Vulkan_LoadLibrary(NULL) < 0) { + return NULL; + } + } + + /* Unless the user has specified the high-DPI disabling hint, respect the + * SDL_WINDOW_ALLOW_HIGHDPI flag. + */ + if (flags & SDL_WINDOW_ALLOW_HIGHDPI) { + if (SDL_GetHintBoolean(SDL_HINT_VIDEO_HIGHDPI_DISABLED, SDL_FALSE)) { + flags &= ~SDL_WINDOW_ALLOW_HIGHDPI; + } + } + + window = (SDL_Window *)SDL_calloc(1, sizeof(*window)); + if (!window) { + SDL_OutOfMemory(); + return NULL; + } + window->magic = &_this->window_magic; + window->id = _this->next_object_id++; + window->x = x; + window->y = y; + window->w = w; + window->h = h; + if (SDL_WINDOWPOS_ISUNDEFINED(x) || SDL_WINDOWPOS_ISUNDEFINED(y) || + SDL_WINDOWPOS_ISCENTERED(x) || SDL_WINDOWPOS_ISCENTERED(y)) { + SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); + int displayIndex; + SDL_Rect bounds; + + displayIndex = SDL_GetIndexOfDisplay(display); + SDL_GetDisplayBounds(displayIndex, &bounds); + if (SDL_WINDOWPOS_ISUNDEFINED(x) || SDL_WINDOWPOS_ISCENTERED(x)) { + window->x = bounds.x + (bounds.w - w) / 2; + } + if (SDL_WINDOWPOS_ISUNDEFINED(y) || SDL_WINDOWPOS_ISCENTERED(y)) { + window->y = bounds.y + (bounds.h - h) / 2; + } + } + window->windowed.x = window->x; + window->windowed.y = window->y; + window->windowed.w = window->w; + window->windowed.h = window->h; + + if (flags & SDL_WINDOW_FULLSCREEN) { + SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); + int displayIndex; + SDL_Rect bounds; + + displayIndex = SDL_GetIndexOfDisplay(display); + SDL_GetDisplayBounds(displayIndex, &bounds); + + window->x = bounds.x; + window->y = bounds.y; + window->w = bounds.w; + window->h = bounds.h; + } + + window->flags = ((flags & CREATE_FLAGS) | SDL_WINDOW_HIDDEN); + window->last_fullscreen_flags = window->flags; + window->opacity = 1.0f; + window->brightness = 1.0f; + window->next = _this->windows; + window->is_destroying = SDL_FALSE; + + if (_this->windows) { + _this->windows->prev = window; + } + _this->windows = window; + + if (_this->CreateSDLWindow && _this->CreateSDLWindow(_this, window) < 0) { + SDL_DestroyWindow(window); + return NULL; + } + + /* Clear minimized if not on windows, only windows handles it at create rather than FinishWindowCreation, + * but it's important or window focus will get broken on windows! + */ +#if !defined(__WIN32__) + if (window->flags & SDL_WINDOW_MINIMIZED) { + window->flags &= ~SDL_WINDOW_MINIMIZED; + } +#endif + +#if __WINRT__ && (NTDDI_VERSION < NTDDI_WIN10) + /* HACK: WinRT 8.x apps can't choose whether or not they are fullscreen + or not. The user can choose this, via OS-provided UI, but this can't + be set programmatically. + + Just look at what SDL's WinRT video backend code detected with regards + to fullscreen (being active, or not), and figure out a return/error code + from that. + */ + flags = window->flags; +#endif + + if (title) { + SDL_SetWindowTitle(window, title); + } + SDL_FinishWindowCreation(window, flags); + + /* If the window was created fullscreen, make sure the mode code matches */ + SDL_UpdateFullscreenMode(window, FULLSCREEN_VISIBLE(window)); + + return window; +} + +SDL_Window * +SDL_CreateWindowFrom(const void *data) +{ + SDL_Window *window; + + if (!_this) { + SDL_UninitializedVideo(); + return NULL; + } + if (!_this->CreateSDLWindowFrom) { + SDL_Unsupported(); + return NULL; + } + window = (SDL_Window *)SDL_calloc(1, sizeof(*window)); + if (!window) { + SDL_OutOfMemory(); + return NULL; + } + window->magic = &_this->window_magic; + window->id = _this->next_object_id++; + window->flags = SDL_WINDOW_FOREIGN; + window->last_fullscreen_flags = window->flags; + window->is_destroying = SDL_FALSE; + window->opacity = 1.0f; + window->brightness = 1.0f; + window->next = _this->windows; + if (_this->windows) { + _this->windows->prev = window; + } + _this->windows = window; + + if (_this->CreateSDLWindowFrom(_this, window, data) < 0) { + SDL_DestroyWindow(window); + return NULL; + } + + PrepareDragAndDropSupport(window); + + return window; +} + +int +SDL_RecreateWindow(SDL_Window * window, Uint32 flags) +{ + SDL_bool loaded_opengl = SDL_FALSE; + + if ((flags & SDL_WINDOW_OPENGL) && !_this->GL_CreateContext) { + return SDL_SetError("OpenGL support is either not configured in SDL " + "or not available in current SDL video driver " + "(%s) or platform", _this->name); + } + + if (window->flags & SDL_WINDOW_FOREIGN) { + /* Can't destroy and re-create foreign windows, hrm */ + flags |= SDL_WINDOW_FOREIGN; + } else { + flags &= ~SDL_WINDOW_FOREIGN; + } + + /* Restore video mode, etc. */ + SDL_HideWindow(window); + + /* Tear down the old native window */ + if (window->surface) { + window->surface->flags &= ~SDL_DONTFREE; + SDL_FreeSurface(window->surface); + window->surface = NULL; + } + if (_this->DestroyWindowFramebuffer) { + _this->DestroyWindowFramebuffer(_this, window); + } + if (_this->DestroyWindow && !(flags & SDL_WINDOW_FOREIGN)) { + _this->DestroyWindow(_this, window); + } + + if ((window->flags & SDL_WINDOW_OPENGL) != (flags & SDL_WINDOW_OPENGL)) { + if (flags & SDL_WINDOW_OPENGL) { + if (SDL_GL_LoadLibrary(NULL) < 0) { + return -1; + } + loaded_opengl = SDL_TRUE; + } else { + SDL_GL_UnloadLibrary(); + } + } + + if ((window->flags & SDL_WINDOW_VULKAN) != (flags & SDL_WINDOW_VULKAN)) { + SDL_SetError("Can't change SDL_WINDOW_VULKAN window flag"); + return -1; + } + + if ((window->flags & SDL_WINDOW_VULKAN) && (flags & SDL_WINDOW_OPENGL)) { + SDL_SetError("Vulkan and OpenGL not supported on same window"); + return -1; + } + + window->flags = ((flags & CREATE_FLAGS) | SDL_WINDOW_HIDDEN); + window->last_fullscreen_flags = window->flags; + window->is_destroying = SDL_FALSE; + + if (_this->CreateSDLWindow && !(flags & SDL_WINDOW_FOREIGN)) { + if (_this->CreateSDLWindow(_this, window) < 0) { + if (loaded_opengl) { + SDL_GL_UnloadLibrary(); + window->flags &= ~SDL_WINDOW_OPENGL; + } + return -1; + } + } + + if (flags & SDL_WINDOW_FOREIGN) { + window->flags |= SDL_WINDOW_FOREIGN; + } + + if (_this->SetWindowTitle && window->title) { + _this->SetWindowTitle(_this, window); + } + + if (_this->SetWindowIcon && window->icon) { + _this->SetWindowIcon(_this, window, window->icon); + } + + if (window->hit_test) { + _this->SetWindowHitTest(window, SDL_TRUE); + } + + SDL_FinishWindowCreation(window, flags); + + return 0; +} + +SDL_bool +SDL_HasWindows(void) +{ + return (_this && _this->windows != NULL); +} + +Uint32 +SDL_GetWindowID(SDL_Window * window) +{ + CHECK_WINDOW_MAGIC(window, 0); + + return window->id; +} + +SDL_Window * +SDL_GetWindowFromID(Uint32 id) +{ + SDL_Window *window; + + if (!_this) { + return NULL; + } + for (window = _this->windows; window; window = window->next) { + if (window->id == id) { + return window; + } + } + return NULL; +} + +Uint32 +SDL_GetWindowFlags(SDL_Window * window) +{ + CHECK_WINDOW_MAGIC(window, 0); + + return window->flags; +} + +void +SDL_SetWindowTitle(SDL_Window * window, const char *title) +{ + CHECK_WINDOW_MAGIC(window,); + + if (title == window->title) { + return; + } + SDL_free(window->title); + + window->title = SDL_strdup(title ? title : ""); + + if (_this->SetWindowTitle) { + _this->SetWindowTitle(_this, window); + } +} + +const char * +SDL_GetWindowTitle(SDL_Window * window) +{ + CHECK_WINDOW_MAGIC(window, ""); + + return window->title ? window->title : ""; +} + +void +SDL_SetWindowIcon(SDL_Window * window, SDL_Surface * icon) +{ + CHECK_WINDOW_MAGIC(window,); + + if (!icon) { + return; + } + + SDL_FreeSurface(window->icon); + + /* Convert the icon into ARGB8888 */ + window->icon = SDL_ConvertSurfaceFormat(icon, SDL_PIXELFORMAT_ARGB8888, 0); + if (!window->icon) { + return; + } + + if (_this->SetWindowIcon) { + _this->SetWindowIcon(_this, window, window->icon); + } +} + +void* +SDL_SetWindowData(SDL_Window * window, const char *name, void *userdata) +{ + SDL_WindowUserData *prev, *data; + + CHECK_WINDOW_MAGIC(window, NULL); + + /* Input validation */ + if (name == NULL || name[0] == '\0') { + SDL_InvalidParamError("name"); + return NULL; + } + + /* See if the named data already exists */ + prev = NULL; + for (data = window->data; data; prev = data, data = data->next) { + if (data->name && SDL_strcmp(data->name, name) == 0) { + void *last_value = data->data; + + if (userdata) { + /* Set the new value */ + data->data = userdata; + } else { + /* Delete this value */ + if (prev) { + prev->next = data->next; + } else { + window->data = data->next; + } + SDL_free(data->name); + SDL_free(data); + } + return last_value; + } + } + + /* Add new data to the window */ + if (userdata) { + data = (SDL_WindowUserData *)SDL_malloc(sizeof(*data)); + data->name = SDL_strdup(name); + data->data = userdata; + data->next = window->data; + window->data = data; + } + return NULL; +} + +void * +SDL_GetWindowData(SDL_Window * window, const char *name) +{ + SDL_WindowUserData *data; + + CHECK_WINDOW_MAGIC(window, NULL); + + /* Input validation */ + if (name == NULL || name[0] == '\0') { + SDL_InvalidParamError("name"); + return NULL; + } + + for (data = window->data; data; data = data->next) { + if (data->name && SDL_strcmp(data->name, name) == 0) { + return data->data; + } + } + return NULL; +} + +void +SDL_SetWindowPosition(SDL_Window * window, int x, int y) +{ + CHECK_WINDOW_MAGIC(window,); + + if (SDL_WINDOWPOS_ISCENTERED(x) || SDL_WINDOWPOS_ISCENTERED(y)) { + int displayIndex = (x & 0xFFFF); + SDL_Rect bounds; + if (displayIndex >= _this->num_displays) { + displayIndex = 0; + } + + SDL_zero(bounds); + + SDL_GetDisplayBounds(displayIndex, &bounds); + if (SDL_WINDOWPOS_ISCENTERED(x)) { + x = bounds.x + (bounds.w - window->w) / 2; + } + if (SDL_WINDOWPOS_ISCENTERED(y)) { + y = bounds.y + (bounds.h - window->h) / 2; + } + } + + if ((window->flags & SDL_WINDOW_FULLSCREEN)) { + if (!SDL_WINDOWPOS_ISUNDEFINED(x)) { + window->windowed.x = x; + } + if (!SDL_WINDOWPOS_ISUNDEFINED(y)) { + window->windowed.y = y; + } + } else { + if (!SDL_WINDOWPOS_ISUNDEFINED(x)) { + window->x = x; + } + if (!SDL_WINDOWPOS_ISUNDEFINED(y)) { + window->y = y; + } + + if (_this->SetWindowPosition) { + _this->SetWindowPosition(_this, window); + } + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y); + } +} + +void +SDL_GetWindowPosition(SDL_Window * window, int *x, int *y) +{ + CHECK_WINDOW_MAGIC(window,); + + /* Fullscreen windows are always at their display's origin */ + if (window->flags & SDL_WINDOW_FULLSCREEN) { + int displayIndex; + + if (x) { + *x = 0; + } + if (y) { + *y = 0; + } + + /* Find the window's monitor and update to the + monitor offset. */ + displayIndex = SDL_GetWindowDisplayIndex(window); + if (displayIndex >= 0) { + SDL_Rect bounds; + + SDL_zero(bounds); + + SDL_GetDisplayBounds(displayIndex, &bounds); + if (x) { + *x = bounds.x; + } + if (y) { + *y = bounds.y; + } + } + } else { + if (x) { + *x = window->x; + } + if (y) { + *y = window->y; + } + } +} + +void +SDL_SetWindowBordered(SDL_Window * window, SDL_bool bordered) +{ + CHECK_WINDOW_MAGIC(window,); + if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { + const int want = (bordered != SDL_FALSE); /* normalize the flag. */ + const int have = ((window->flags & SDL_WINDOW_BORDERLESS) == 0); + if ((want != have) && (_this->SetWindowBordered)) { + if (want) { + window->flags &= ~SDL_WINDOW_BORDERLESS; + } else { + window->flags |= SDL_WINDOW_BORDERLESS; + } + _this->SetWindowBordered(_this, window, (SDL_bool) want); + } + } +} + +void +SDL_SetWindowResizable(SDL_Window * window, SDL_bool resizable) +{ + CHECK_WINDOW_MAGIC(window,); + if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { + const int want = (resizable != SDL_FALSE); /* normalize the flag. */ + const int have = ((window->flags & SDL_WINDOW_RESIZABLE) != 0); + if ((want != have) && (_this->SetWindowResizable)) { + if (want) { + window->flags |= SDL_WINDOW_RESIZABLE; + } else { + window->flags &= ~SDL_WINDOW_RESIZABLE; + } + _this->SetWindowResizable(_this, window, (SDL_bool) want); + } + } +} + +void +SDL_SetWindowSize(SDL_Window * window, int w, int h) +{ + CHECK_WINDOW_MAGIC(window,); + if (w <= 0) { + SDL_InvalidParamError("w"); + return; + } + if (h <= 0) { + SDL_InvalidParamError("h"); + return; + } + + /* Make sure we don't exceed any window size limits */ + if (window->min_w && w < window->min_w) { + w = window->min_w; + } + if (window->max_w && w > window->max_w) { + w = window->max_w; + } + if (window->min_h && h < window->min_h) { + h = window->min_h; + } + if (window->max_h && h > window->max_h) { + h = window->max_h; + } + + window->windowed.w = w; + window->windowed.h = h; + + if (window->flags & SDL_WINDOW_FULLSCREEN) { + if (FULLSCREEN_VISIBLE(window) && (window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP) { + window->last_fullscreen_flags = 0; + SDL_UpdateFullscreenMode(window, SDL_TRUE); + } + } else { + window->w = w; + window->h = h; + if (_this->SetWindowSize) { + _this->SetWindowSize(_this, window); + } + if (window->w == w && window->h == h) { + /* We didn't get a SDL_WINDOWEVENT_RESIZED event (by design) */ + SDL_OnWindowResized(window); + } + } +} + +void +SDL_GetWindowSize(SDL_Window * window, int *w, int *h) +{ + CHECK_WINDOW_MAGIC(window,); + if (w) { + *w = window->w; + } + if (h) { + *h = window->h; + } +} + +int +SDL_GetWindowBordersSize(SDL_Window * window, int *top, int *left, int *bottom, int *right) +{ + int dummy = 0; + + if (!top) { top = &dummy; } + if (!left) { left = &dummy; } + if (!right) { right = &dummy; } + if (!bottom) { bottom = &dummy; } + + /* Always initialize, so applications don't have to care */ + *top = *left = *bottom = *right = 0; + + CHECK_WINDOW_MAGIC(window, -1); + + if (!_this->GetWindowBordersSize) { + return SDL_Unsupported(); + } + + return _this->GetWindowBordersSize(_this, window, top, left, bottom, right); +} + +void +SDL_SetWindowMinimumSize(SDL_Window * window, int min_w, int min_h) +{ + CHECK_WINDOW_MAGIC(window,); + if (min_w <= 0) { + SDL_InvalidParamError("min_w"); + return; + } + if (min_h <= 0) { + SDL_InvalidParamError("min_h"); + return; + } + + if ((window->max_w && min_w >= window->max_w) || + (window->max_h && min_h >= window->max_h)) { + SDL_SetError("SDL_SetWindowMinimumSize(): Tried to set minimum size larger than maximum size"); + return; + } + + window->min_w = min_w; + window->min_h = min_h; + + if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { + if (_this->SetWindowMinimumSize) { + _this->SetWindowMinimumSize(_this, window); + } + /* Ensure that window is not smaller than minimal size */ + SDL_SetWindowSize(window, SDL_max(window->w, window->min_w), SDL_max(window->h, window->min_h)); + } +} + +void +SDL_GetWindowMinimumSize(SDL_Window * window, int *min_w, int *min_h) +{ + CHECK_WINDOW_MAGIC(window,); + if (min_w) { + *min_w = window->min_w; + } + if (min_h) { + *min_h = window->min_h; + } +} + +void +SDL_SetWindowMaximumSize(SDL_Window * window, int max_w, int max_h) +{ + CHECK_WINDOW_MAGIC(window,); + if (max_w <= 0) { + SDL_InvalidParamError("max_w"); + return; + } + if (max_h <= 0) { + SDL_InvalidParamError("max_h"); + return; + } + + if (max_w <= window->min_w || max_h <= window->min_h) { + SDL_SetError("SDL_SetWindowMaximumSize(): Tried to set maximum size smaller than minimum size"); + return; + } + + window->max_w = max_w; + window->max_h = max_h; + + if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { + if (_this->SetWindowMaximumSize) { + _this->SetWindowMaximumSize(_this, window); + } + /* Ensure that window is not larger than maximal size */ + SDL_SetWindowSize(window, SDL_min(window->w, window->max_w), SDL_min(window->h, window->max_h)); + } +} + +void +SDL_GetWindowMaximumSize(SDL_Window * window, int *max_w, int *max_h) +{ + CHECK_WINDOW_MAGIC(window,); + if (max_w) { + *max_w = window->max_w; + } + if (max_h) { + *max_h = window->max_h; + } +} + +void +SDL_ShowWindow(SDL_Window * window) +{ + CHECK_WINDOW_MAGIC(window,); + + if (window->flags & SDL_WINDOW_SHOWN) { + return; + } + + if (_this->ShowWindow) { + _this->ShowWindow(_this, window); + } + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_SHOWN, 0, 0); +} + +void +SDL_HideWindow(SDL_Window * window) +{ + CHECK_WINDOW_MAGIC(window,); + + if (!(window->flags & SDL_WINDOW_SHOWN)) { + return; + } + + window->is_hiding = SDL_TRUE; + SDL_UpdateFullscreenMode(window, SDL_FALSE); + + if (_this->HideWindow) { + _this->HideWindow(_this, window); + } + window->is_hiding = SDL_FALSE; + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_HIDDEN, 0, 0); +} + +void +SDL_RaiseWindow(SDL_Window * window) +{ + CHECK_WINDOW_MAGIC(window,); + + if (!(window->flags & SDL_WINDOW_SHOWN)) { + return; + } + if (_this->RaiseWindow) { + _this->RaiseWindow(_this, window); + } +} + +void +SDL_MaximizeWindow(SDL_Window * window) +{ + CHECK_WINDOW_MAGIC(window,); + + if (window->flags & SDL_WINDOW_MAXIMIZED) { + return; + } + + /* !!! FIXME: should this check if the window is resizable? */ + + if (_this->MaximizeWindow) { + _this->MaximizeWindow(_this, window); + } +} + +void +SDL_MinimizeWindow(SDL_Window * window) +{ + CHECK_WINDOW_MAGIC(window,); + + if (window->flags & SDL_WINDOW_MINIMIZED) { + return; + } + + SDL_UpdateFullscreenMode(window, SDL_FALSE); + + if (_this->MinimizeWindow) { + _this->MinimizeWindow(_this, window); + } +} + +void +SDL_RestoreWindow(SDL_Window * window) +{ + CHECK_WINDOW_MAGIC(window,); + + if (!(window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) { + return; + } + + if (_this->RestoreWindow) { + _this->RestoreWindow(_this, window); + } +} + +int +SDL_SetWindowFullscreen(SDL_Window * window, Uint32 flags) +{ + Uint32 oldflags; + CHECK_WINDOW_MAGIC(window, -1); + + flags &= FULLSCREEN_MASK; + + if (flags == (window->flags & FULLSCREEN_MASK)) { + return 0; + } + + /* clear the previous flags and OR in the new ones */ + oldflags = window->flags & FULLSCREEN_MASK; + window->flags &= ~FULLSCREEN_MASK; + window->flags |= flags; + + if (SDL_UpdateFullscreenMode(window, FULLSCREEN_VISIBLE(window)) == 0) { + return 0; + } + + window->flags &= ~FULLSCREEN_MASK; + window->flags |= oldflags; + return -1; +} + +static SDL_Surface * +SDL_CreateWindowFramebuffer(SDL_Window * window) +{ + Uint32 format; + void *pixels; + int pitch; + int bpp; + Uint32 Rmask, Gmask, Bmask, Amask; + + if (!_this->CreateWindowFramebuffer || !_this->UpdateWindowFramebuffer) { + return NULL; + } + + if (_this->CreateWindowFramebuffer(_this, window, &format, &pixels, &pitch) < 0) { + return NULL; + } + + if (!SDL_PixelFormatEnumToMasks(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) { + return NULL; + } + + return SDL_CreateRGBSurfaceFrom(pixels, window->w, window->h, bpp, pitch, Rmask, Gmask, Bmask, Amask); +} + +SDL_Surface * +SDL_GetWindowSurface(SDL_Window * window) +{ + CHECK_WINDOW_MAGIC(window, NULL); + + if (!window->surface_valid) { + if (window->surface) { + window->surface->flags &= ~SDL_DONTFREE; + SDL_FreeSurface(window->surface); + } + window->surface = SDL_CreateWindowFramebuffer(window); + if (window->surface) { + window->surface_valid = SDL_TRUE; + window->surface->flags |= SDL_DONTFREE; + } + } + return window->surface; +} + +int +SDL_UpdateWindowSurface(SDL_Window * window) +{ + SDL_Rect full_rect; + + CHECK_WINDOW_MAGIC(window, -1); + + full_rect.x = 0; + full_rect.y = 0; + full_rect.w = window->w; + full_rect.h = window->h; + return SDL_UpdateWindowSurfaceRects(window, &full_rect, 1); +} + +int +SDL_UpdateWindowSurfaceRects(SDL_Window * window, const SDL_Rect * rects, + int numrects) +{ + CHECK_WINDOW_MAGIC(window, -1); + + if (!window->surface_valid) { + return SDL_SetError("Window surface is invalid, please call SDL_GetWindowSurface() to get a new surface"); + } + + return _this->UpdateWindowFramebuffer(_this, window, rects, numrects); +} + +int +SDL_SetWindowBrightness(SDL_Window * window, float brightness) +{ + Uint16 ramp[256]; + int status; + + CHECK_WINDOW_MAGIC(window, -1); + + SDL_CalculateGammaRamp(brightness, ramp); + status = SDL_SetWindowGammaRamp(window, ramp, ramp, ramp); + if (status == 0) { + window->brightness = brightness; + } + return status; +} + +float +SDL_GetWindowBrightness(SDL_Window * window) +{ + CHECK_WINDOW_MAGIC(window, 1.0f); + + return window->brightness; +} + +int +SDL_SetWindowOpacity(SDL_Window * window, float opacity) +{ + int retval; + CHECK_WINDOW_MAGIC(window, -1); + + if (!_this->SetWindowOpacity) { + return SDL_Unsupported(); + } + + if (opacity < 0.0f) { + opacity = 0.0f; + } else if (opacity > 1.0f) { + opacity = 1.0f; + } + + retval = _this->SetWindowOpacity(_this, window, opacity); + if (retval == 0) { + window->opacity = opacity; + } + + return retval; +} + +int +SDL_GetWindowOpacity(SDL_Window * window, float * out_opacity) +{ + CHECK_WINDOW_MAGIC(window, -1); + + if (out_opacity) { + *out_opacity = window->opacity; + } + + return 0; +} + +int +SDL_SetWindowModalFor(SDL_Window * modal_window, SDL_Window * parent_window) +{ + CHECK_WINDOW_MAGIC(modal_window, -1); + CHECK_WINDOW_MAGIC(parent_window, -1); + + if (!_this->SetWindowModalFor) { + return SDL_Unsupported(); + } + + return _this->SetWindowModalFor(_this, modal_window, parent_window); +} + +int +SDL_SetWindowInputFocus(SDL_Window * window) +{ + CHECK_WINDOW_MAGIC(window, -1); + + if (!_this->SetWindowInputFocus) { + return SDL_Unsupported(); + } + + return _this->SetWindowInputFocus(_this, window); +} + + +int +SDL_SetWindowGammaRamp(SDL_Window * window, const Uint16 * red, + const Uint16 * green, + const Uint16 * blue) +{ + CHECK_WINDOW_MAGIC(window, -1); + + if (!_this->SetWindowGammaRamp) { + return SDL_Unsupported(); + } + + if (!window->gamma) { + if (SDL_GetWindowGammaRamp(window, NULL, NULL, NULL) < 0) { + return -1; + } + SDL_assert(window->gamma != NULL); + } + + if (red) { + SDL_memcpy(&window->gamma[0*256], red, 256*sizeof(Uint16)); + } + if (green) { + SDL_memcpy(&window->gamma[1*256], green, 256*sizeof(Uint16)); + } + if (blue) { + SDL_memcpy(&window->gamma[2*256], blue, 256*sizeof(Uint16)); + } + if (window->flags & SDL_WINDOW_INPUT_FOCUS) { + return _this->SetWindowGammaRamp(_this, window, window->gamma); + } else { + return 0; + } +} + +int +SDL_GetWindowGammaRamp(SDL_Window * window, Uint16 * red, + Uint16 * green, + Uint16 * blue) +{ + CHECK_WINDOW_MAGIC(window, -1); + + if (!window->gamma) { + int i; + + window->gamma = (Uint16 *)SDL_malloc(256*6*sizeof(Uint16)); + if (!window->gamma) { + return SDL_OutOfMemory(); + } + window->saved_gamma = window->gamma + 3*256; + + if (_this->GetWindowGammaRamp) { + if (_this->GetWindowGammaRamp(_this, window, window->gamma) < 0) { + return -1; + } + } else { + /* Create an identity gamma ramp */ + for (i = 0; i < 256; ++i) { + Uint16 value = (Uint16)((i << 8) | i); + + window->gamma[0*256+i] = value; + window->gamma[1*256+i] = value; + window->gamma[2*256+i] = value; + } + } + SDL_memcpy(window->saved_gamma, window->gamma, 3*256*sizeof(Uint16)); + } + + if (red) { + SDL_memcpy(red, &window->gamma[0*256], 256*sizeof(Uint16)); + } + if (green) { + SDL_memcpy(green, &window->gamma[1*256], 256*sizeof(Uint16)); + } + if (blue) { + SDL_memcpy(blue, &window->gamma[2*256], 256*sizeof(Uint16)); + } + return 0; +} + +void +SDL_UpdateWindowGrab(SDL_Window * window) +{ + SDL_Window *grabbed_window; + SDL_bool grabbed; + if ((SDL_GetMouse()->relative_mode || (window->flags & SDL_WINDOW_INPUT_GRABBED)) && + (window->flags & SDL_WINDOW_INPUT_FOCUS)) { + grabbed = SDL_TRUE; + } else { + grabbed = SDL_FALSE; + } + + grabbed_window = _this->grabbed_window; + if (grabbed) { + if (grabbed_window && (grabbed_window != window)) { + /* stealing a grab from another window! */ + grabbed_window->flags &= ~SDL_WINDOW_INPUT_GRABBED; + if (_this->SetWindowGrab) { + _this->SetWindowGrab(_this, grabbed_window, SDL_FALSE); + } + } + _this->grabbed_window = window; + } else if (grabbed_window == window) { + _this->grabbed_window = NULL; /* ungrabbing. */ + } + + if (_this->SetWindowGrab) { + _this->SetWindowGrab(_this, window, grabbed); + } +} + +void +SDL_SetWindowGrab(SDL_Window * window, SDL_bool grabbed) +{ + CHECK_WINDOW_MAGIC(window,); + + if (!!grabbed == !!(window->flags & SDL_WINDOW_INPUT_GRABBED)) { + return; + } + if (grabbed) { + window->flags |= SDL_WINDOW_INPUT_GRABBED; + } else { + window->flags &= ~SDL_WINDOW_INPUT_GRABBED; + } + SDL_UpdateWindowGrab(window); +} + +SDL_bool +SDL_GetWindowGrab(SDL_Window * window) +{ + CHECK_WINDOW_MAGIC(window, SDL_FALSE); + SDL_assert(!_this->grabbed_window || ((_this->grabbed_window->flags & SDL_WINDOW_INPUT_GRABBED) != 0)); + return window == _this->grabbed_window; +} + +SDL_Window * +SDL_GetGrabbedWindow(void) +{ + SDL_assert(!_this->grabbed_window || ((_this->grabbed_window->flags & SDL_WINDOW_INPUT_GRABBED) != 0)); + return _this->grabbed_window; +} + +void +SDL_OnWindowShown(SDL_Window * window) +{ + SDL_OnWindowRestored(window); +} + +void +SDL_OnWindowHidden(SDL_Window * window) +{ + SDL_UpdateFullscreenMode(window, SDL_FALSE); +} + +void +SDL_OnWindowResized(SDL_Window * window) +{ + window->surface_valid = SDL_FALSE; + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_SIZE_CHANGED, window->w, window->h); +} + +void +SDL_OnWindowMinimized(SDL_Window * window) +{ + SDL_UpdateFullscreenMode(window, SDL_FALSE); +} + +void +SDL_OnWindowRestored(SDL_Window * window) +{ + /* + * FIXME: Is this fine to just remove this, or should it be preserved just + * for the fullscreen case? In principle it seems like just hiding/showing + * windows shouldn't affect the stacking order; maybe the right fix is to + * re-decouple OnWindowShown and OnWindowRestored. + */ + /*SDL_RaiseWindow(window);*/ + + if (FULLSCREEN_VISIBLE(window)) { + SDL_UpdateFullscreenMode(window, SDL_TRUE); + } +} + +void +SDL_OnWindowEnter(SDL_Window * window) +{ + if (_this->OnWindowEnter) { + _this->OnWindowEnter(_this, window); + } +} + +void +SDL_OnWindowLeave(SDL_Window * window) +{ +} + +void +SDL_OnWindowFocusGained(SDL_Window * window) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + if (window->gamma && _this->SetWindowGammaRamp) { + _this->SetWindowGammaRamp(_this, window, window->gamma); + } + + if (mouse && mouse->relative_mode) { + SDL_SetMouseFocus(window); + SDL_WarpMouseInWindow(window, window->w/2, window->h/2); + } + + SDL_UpdateWindowGrab(window); +} + +static SDL_bool +ShouldMinimizeOnFocusLoss(SDL_Window * window) +{ + if (!(window->flags & SDL_WINDOW_FULLSCREEN) || window->is_destroying) { + return SDL_FALSE; + } + +#ifdef __MACOSX__ + if (SDL_strcmp(_this->name, "cocoa") == 0) { /* don't do this for X11, etc */ + if (Cocoa_IsWindowInFullscreenSpace(window)) { + return SDL_FALSE; + } + } +#endif + + return SDL_GetHintBoolean(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, SDL_TRUE); +} + +void +SDL_OnWindowFocusLost(SDL_Window * window) +{ + if (window->gamma && _this->SetWindowGammaRamp) { + _this->SetWindowGammaRamp(_this, window, window->saved_gamma); + } + + SDL_UpdateWindowGrab(window); + + if (ShouldMinimizeOnFocusLoss(window)) { + SDL_MinimizeWindow(window); + } +} + +/* !!! FIXME: is this different than SDL_GetKeyboardFocus()? + !!! FIXME: Also, SDL_GetKeyboardFocus() is O(1), this isn't. */ +SDL_Window * +SDL_GetFocusWindow(void) +{ + SDL_Window *window; + + if (!_this) { + return NULL; + } + for (window = _this->windows; window; window = window->next) { + if (window->flags & SDL_WINDOW_INPUT_FOCUS) { + return window; + } + } + return NULL; +} + +void +SDL_DestroyWindow(SDL_Window * window) +{ + SDL_VideoDisplay *display; + + CHECK_WINDOW_MAGIC(window,); + + window->is_destroying = SDL_TRUE; + + /* Restore video mode, etc. */ + SDL_HideWindow(window); + + /* Make sure this window no longer has focus */ + if (SDL_GetKeyboardFocus() == window) { + SDL_SetKeyboardFocus(NULL); + } + if (SDL_GetMouseFocus() == window) { + SDL_SetMouseFocus(NULL); + } + + /* make no context current if this is the current context window. */ + if (window->flags & SDL_WINDOW_OPENGL) { + if (_this->current_glwin == window) { + SDL_GL_MakeCurrent(window, NULL); + } + } + + if (window->surface) { + window->surface->flags &= ~SDL_DONTFREE; + SDL_FreeSurface(window->surface); + } + if (_this->DestroyWindowFramebuffer) { + _this->DestroyWindowFramebuffer(_this, window); + } + if (_this->DestroyWindow) { + _this->DestroyWindow(_this, window); + } + if (window->flags & SDL_WINDOW_OPENGL) { + SDL_GL_UnloadLibrary(); + } + if (window->flags & SDL_WINDOW_VULKAN) { + SDL_Vulkan_UnloadLibrary(); + } + + display = SDL_GetDisplayForWindow(window); + if (display->fullscreen_window == window) { + display->fullscreen_window = NULL; + } + + /* Now invalidate magic */ + window->magic = NULL; + + /* Free memory associated with the window */ + SDL_free(window->title); + SDL_FreeSurface(window->icon); + SDL_free(window->gamma); + while (window->data) { + SDL_WindowUserData *data = window->data; + + window->data = data->next; + SDL_free(data->name); + SDL_free(data); + } + + /* Unlink the window from the list */ + if (window->next) { + window->next->prev = window->prev; + } + if (window->prev) { + window->prev->next = window->next; + } else { + _this->windows = window->next; + } + + SDL_free(window); +} + +SDL_bool +SDL_IsScreenSaverEnabled() +{ + if (!_this) { + return SDL_TRUE; + } + return _this->suspend_screensaver ? SDL_FALSE : SDL_TRUE; +} + +void +SDL_EnableScreenSaver() +{ + if (!_this) { + return; + } + if (!_this->suspend_screensaver) { + return; + } + _this->suspend_screensaver = SDL_FALSE; + if (_this->SuspendScreenSaver) { + _this->SuspendScreenSaver(_this); + } +} + +void +SDL_DisableScreenSaver() +{ + if (!_this) { + return; + } + if (_this->suspend_screensaver) { + return; + } + _this->suspend_screensaver = SDL_TRUE; + if (_this->SuspendScreenSaver) { + _this->SuspendScreenSaver(_this); + } +} + +void +SDL_VideoQuit(void) +{ + int i, j; + + if (!_this) { + return; + } + + /* Halt event processing before doing anything else */ + SDL_TouchQuit(); + SDL_MouseQuit(); + SDL_KeyboardQuit(); + SDL_QuitSubSystem(SDL_INIT_EVENTS); + + SDL_EnableScreenSaver(); + + /* Clean up the system video */ + while (_this->windows) { + SDL_DestroyWindow(_this->windows); + } + _this->VideoQuit(_this); + + for (i = 0; i < _this->num_displays; ++i) { + SDL_VideoDisplay *display = &_this->displays[i]; + for (j = display->num_display_modes; j--;) { + SDL_free(display->display_modes[j].driverdata); + display->display_modes[j].driverdata = NULL; + } + SDL_free(display->display_modes); + display->display_modes = NULL; + SDL_free(display->desktop_mode.driverdata); + display->desktop_mode.driverdata = NULL; + SDL_free(display->driverdata); + display->driverdata = NULL; + } + if (_this->displays) { + for (i = 0; i < _this->num_displays; ++i) { + SDL_free(_this->displays[i].name); + } + SDL_free(_this->displays); + _this->displays = NULL; + _this->num_displays = 0; + } + SDL_free(_this->clipboard_text); + _this->clipboard_text = NULL; + _this->free(_this); + _this = NULL; +} + +int +SDL_GL_LoadLibrary(const char *path) +{ + int retval; + + if (!_this) { + return SDL_UninitializedVideo(); + } + if (_this->gl_config.driver_loaded) { + if (path && SDL_strcmp(path, _this->gl_config.driver_path) != 0) { + return SDL_SetError("OpenGL library already loaded"); + } + retval = 0; + } else { + if (!_this->GL_LoadLibrary) { + return SDL_SetError("No dynamic GL support in current SDL video driver (%s)", _this->name); + } + retval = _this->GL_LoadLibrary(_this, path); + } + if (retval == 0) { + ++_this->gl_config.driver_loaded; + } else { + if (_this->GL_UnloadLibrary) { + _this->GL_UnloadLibrary(_this); + } + } + return (retval); +} + +void * +SDL_GL_GetProcAddress(const char *proc) +{ + void *func; + + if (!_this) { + SDL_UninitializedVideo(); + return NULL; + } + func = NULL; + if (_this->GL_GetProcAddress) { + if (_this->gl_config.driver_loaded) { + func = _this->GL_GetProcAddress(_this, proc); + } else { + SDL_SetError("No GL driver has been loaded"); + } + } else { + SDL_SetError("No dynamic GL support in current SDL video driver (%s)", _this->name); + } + return func; +} + +void +SDL_GL_UnloadLibrary(void) +{ + if (!_this) { + SDL_UninitializedVideo(); + return; + } + if (_this->gl_config.driver_loaded > 0) { + if (--_this->gl_config.driver_loaded > 0) { + return; + } + if (_this->GL_UnloadLibrary) { + _this->GL_UnloadLibrary(_this); + } + } +} + +#if SDL_VIDEO_OPENGL || SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2 +static SDL_INLINE SDL_bool +isAtLeastGL3(const char *verstr) +{ + return (verstr && (SDL_atoi(verstr) >= 3)); +} +#endif + +SDL_bool +SDL_GL_ExtensionSupported(const char *extension) +{ +#if SDL_VIDEO_OPENGL || SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2 + const GLubyte *(APIENTRY * glGetStringFunc) (GLenum); + const char *extensions; + const char *start; + const char *where, *terminator; + + /* Extension names should not have spaces. */ + where = SDL_strchr(extension, ' '); + if (where || *extension == '\0') { + return SDL_FALSE; + } + /* See if there's an environment variable override */ + start = SDL_getenv(extension); + if (start && *start == '0') { + return SDL_FALSE; + } + + /* Lookup the available extensions */ + + glGetStringFunc = SDL_GL_GetProcAddress("glGetString"); + if (!glGetStringFunc) { + return SDL_FALSE; + } + + if (isAtLeastGL3((const char *) glGetStringFunc(GL_VERSION))) { + const GLubyte *(APIENTRY * glGetStringiFunc) (GLenum, GLuint); + void (APIENTRY * glGetIntegervFunc) (GLenum pname, GLint * params); + GLint num_exts = 0; + GLint i; + + glGetStringiFunc = SDL_GL_GetProcAddress("glGetStringi"); + glGetIntegervFunc = SDL_GL_GetProcAddress("glGetIntegerv"); + if ((!glGetStringiFunc) || (!glGetIntegervFunc)) { + return SDL_FALSE; + } + + #ifndef GL_NUM_EXTENSIONS + #define GL_NUM_EXTENSIONS 0x821D + #endif + glGetIntegervFunc(GL_NUM_EXTENSIONS, &num_exts); + for (i = 0; i < num_exts; i++) { + const char *thisext = (const char *) glGetStringiFunc(GL_EXTENSIONS, i); + if (SDL_strcmp(thisext, extension) == 0) { + return SDL_TRUE; + } + } + + return SDL_FALSE; + } + + /* Try the old way with glGetString(GL_EXTENSIONS) ... */ + + extensions = (const char *) glGetStringFunc(GL_EXTENSIONS); + if (!extensions) { + return SDL_FALSE; + } + /* + * It takes a bit of care to be fool-proof about parsing the OpenGL + * extensions string. Don't be fooled by sub-strings, etc. + */ + + start = extensions; + + for (;;) { + where = SDL_strstr(start, extension); + if (!where) + break; + + terminator = where + SDL_strlen(extension); + if (where == extensions || *(where - 1) == ' ') + if (*terminator == ' ' || *terminator == '\0') + return SDL_TRUE; + + start = terminator; + } + return SDL_FALSE; +#else + return SDL_FALSE; +#endif +} + +/* Deduce supported ES profile versions from the supported + ARB_ES*_compatibility extensions. There is no direct query. + + This is normally only called when the OpenGL driver supports + {GLX,WGL}_EXT_create_context_es2_profile. + */ +void +SDL_GL_DeduceMaxSupportedESProfile(int* major, int* minor) +{ +/* THIS REQUIRES AN EXISTING GL CONTEXT THAT HAS BEEN MADE CURRENT. */ +/* Please refer to https://bugzilla.libsdl.org/show_bug.cgi?id=3725 for discussion. */ +#if SDL_VIDEO_OPENGL || SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2 + /* XXX This is fragile; it will break in the event of release of + * new versions of OpenGL ES. + */ + if (SDL_GL_ExtensionSupported("GL_ARB_ES3_2_compatibility")) { + *major = 3; + *minor = 2; + } else if (SDL_GL_ExtensionSupported("GL_ARB_ES3_1_compatibility")) { + *major = 3; + *minor = 1; + } else if (SDL_GL_ExtensionSupported("GL_ARB_ES3_compatibility")) { + *major = 3; + *minor = 0; + } else { + *major = 2; + *minor = 0; + } +#endif +} + +void +SDL_GL_ResetAttributes() +{ + if (!_this) { + return; + } + + _this->gl_config.red_size = 3; + _this->gl_config.green_size = 3; + _this->gl_config.blue_size = 2; + _this->gl_config.alpha_size = 0; + _this->gl_config.buffer_size = 0; + _this->gl_config.depth_size = 16; + _this->gl_config.stencil_size = 0; + _this->gl_config.double_buffer = 1; + _this->gl_config.accum_red_size = 0; + _this->gl_config.accum_green_size = 0; + _this->gl_config.accum_blue_size = 0; + _this->gl_config.accum_alpha_size = 0; + _this->gl_config.stereo = 0; + _this->gl_config.multisamplebuffers = 0; + _this->gl_config.multisamplesamples = 0; + _this->gl_config.retained_backing = 1; + _this->gl_config.accelerated = -1; /* accelerated or not, both are fine */ + + if (_this->GL_DefaultProfileConfig) { + _this->GL_DefaultProfileConfig(_this, &_this->gl_config.profile_mask, + &_this->gl_config.major_version, + &_this->gl_config.minor_version); + } else { +#if SDL_VIDEO_OPENGL + _this->gl_config.major_version = 2; + _this->gl_config.minor_version = 1; + _this->gl_config.profile_mask = 0; +#elif SDL_VIDEO_OPENGL_ES2 + _this->gl_config.major_version = 2; + _this->gl_config.minor_version = 0; + _this->gl_config.profile_mask = SDL_GL_CONTEXT_PROFILE_ES; +#elif SDL_VIDEO_OPENGL_ES + _this->gl_config.major_version = 1; + _this->gl_config.minor_version = 1; + _this->gl_config.profile_mask = SDL_GL_CONTEXT_PROFILE_ES; +#endif + } + + _this->gl_config.flags = 0; + _this->gl_config.framebuffer_srgb_capable = 0; + _this->gl_config.no_error = 0; + _this->gl_config.release_behavior = SDL_GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH; + _this->gl_config.reset_notification = SDL_GL_CONTEXT_RESET_NO_NOTIFICATION; + + _this->gl_config.share_with_current_context = 0; +} + +int +SDL_GL_SetAttribute(SDL_GLattr attr, int value) +{ +#if SDL_VIDEO_OPENGL || SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2 + int retval; + + if (!_this) { + return SDL_UninitializedVideo(); + } + retval = 0; + switch (attr) { + case SDL_GL_RED_SIZE: + _this->gl_config.red_size = value; + break; + case SDL_GL_GREEN_SIZE: + _this->gl_config.green_size = value; + break; + case SDL_GL_BLUE_SIZE: + _this->gl_config.blue_size = value; + break; + case SDL_GL_ALPHA_SIZE: + _this->gl_config.alpha_size = value; + break; + case SDL_GL_DOUBLEBUFFER: + _this->gl_config.double_buffer = value; + break; + case SDL_GL_BUFFER_SIZE: + _this->gl_config.buffer_size = value; + break; + case SDL_GL_DEPTH_SIZE: + _this->gl_config.depth_size = value; + break; + case SDL_GL_STENCIL_SIZE: + _this->gl_config.stencil_size = value; + break; + case SDL_GL_ACCUM_RED_SIZE: + _this->gl_config.accum_red_size = value; + break; + case SDL_GL_ACCUM_GREEN_SIZE: + _this->gl_config.accum_green_size = value; + break; + case SDL_GL_ACCUM_BLUE_SIZE: + _this->gl_config.accum_blue_size = value; + break; + case SDL_GL_ACCUM_ALPHA_SIZE: + _this->gl_config.accum_alpha_size = value; + break; + case SDL_GL_STEREO: + _this->gl_config.stereo = value; + break; + case SDL_GL_MULTISAMPLEBUFFERS: + _this->gl_config.multisamplebuffers = value; + break; + case SDL_GL_MULTISAMPLESAMPLES: + _this->gl_config.multisamplesamples = value; + break; + case SDL_GL_ACCELERATED_VISUAL: + _this->gl_config.accelerated = value; + break; + case SDL_GL_RETAINED_BACKING: + _this->gl_config.retained_backing = value; + break; + case SDL_GL_CONTEXT_MAJOR_VERSION: + _this->gl_config.major_version = value; + break; + case SDL_GL_CONTEXT_MINOR_VERSION: + _this->gl_config.minor_version = value; + break; + case SDL_GL_CONTEXT_EGL: + /* FIXME: SDL_GL_CONTEXT_EGL to be deprecated in SDL 2.1 */ + if (value != 0) { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + } else { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0); + }; + break; + case SDL_GL_CONTEXT_FLAGS: + if (value & ~(SDL_GL_CONTEXT_DEBUG_FLAG | + SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG | + SDL_GL_CONTEXT_ROBUST_ACCESS_FLAG | + SDL_GL_CONTEXT_RESET_ISOLATION_FLAG)) { + retval = SDL_SetError("Unknown OpenGL context flag %d", value); + break; + } + _this->gl_config.flags = value; + break; + case SDL_GL_CONTEXT_PROFILE_MASK: + if (value != 0 && + value != SDL_GL_CONTEXT_PROFILE_CORE && + value != SDL_GL_CONTEXT_PROFILE_COMPATIBILITY && + value != SDL_GL_CONTEXT_PROFILE_ES) { + retval = SDL_SetError("Unknown OpenGL context profile %d", value); + break; + } + _this->gl_config.profile_mask = value; + break; + case SDL_GL_SHARE_WITH_CURRENT_CONTEXT: + _this->gl_config.share_with_current_context = value; + break; + case SDL_GL_FRAMEBUFFER_SRGB_CAPABLE: + _this->gl_config.framebuffer_srgb_capable = value; + break; + case SDL_GL_CONTEXT_RELEASE_BEHAVIOR: + _this->gl_config.release_behavior = value; + break; + case SDL_GL_CONTEXT_RESET_NOTIFICATION: + _this->gl_config.reset_notification = value; + break; + case SDL_GL_CONTEXT_NO_ERROR: + _this->gl_config.no_error = value; + break; + default: + retval = SDL_SetError("Unknown OpenGL attribute"); + break; + } + return retval; +#else + return SDL_Unsupported(); +#endif /* SDL_VIDEO_OPENGL */ +} + +int +SDL_GL_GetAttribute(SDL_GLattr attr, int *value) +{ +#if SDL_VIDEO_OPENGL || SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2 + GLenum (APIENTRY *glGetErrorFunc) (void); + GLenum attrib = 0; + GLenum error = 0; + + /* + * Some queries in Core Profile desktop OpenGL 3+ contexts require + * glGetFramebufferAttachmentParameteriv instead of glGetIntegerv. Note that + * the enums we use for the former function don't exist in OpenGL ES 2, and + * the function itself doesn't exist prior to OpenGL 3 and OpenGL ES 2. + */ +#if SDL_VIDEO_OPENGL + const GLubyte *(APIENTRY *glGetStringFunc) (GLenum name); + void (APIENTRY *glGetFramebufferAttachmentParameterivFunc) (GLenum target, GLenum attachment, GLenum pname, GLint* params); + GLenum attachment = GL_BACK_LEFT; + GLenum attachmentattrib = 0; +#endif + + if (!value) { + return SDL_InvalidParamError("value"); + } + + /* Clear value in any case */ + *value = 0; + + if (!_this) { + return SDL_UninitializedVideo(); + } + + switch (attr) { + case SDL_GL_RED_SIZE: +#if SDL_VIDEO_OPENGL + attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE; +#endif + attrib = GL_RED_BITS; + break; + case SDL_GL_BLUE_SIZE: +#if SDL_VIDEO_OPENGL + attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE; +#endif + attrib = GL_BLUE_BITS; + break; + case SDL_GL_GREEN_SIZE: +#if SDL_VIDEO_OPENGL + attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE; +#endif + attrib = GL_GREEN_BITS; + break; + case SDL_GL_ALPHA_SIZE: +#if SDL_VIDEO_OPENGL + attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE; +#endif + attrib = GL_ALPHA_BITS; + break; + case SDL_GL_DOUBLEBUFFER: +#if SDL_VIDEO_OPENGL + attrib = GL_DOUBLEBUFFER; + break; +#else + /* OpenGL ES 1.0 and above specifications have EGL_SINGLE_BUFFER */ + /* parameter which switches double buffer to single buffer. OpenGL ES */ + /* SDL driver must set proper value after initialization */ + *value = _this->gl_config.double_buffer; + return 0; +#endif + case SDL_GL_DEPTH_SIZE: +#if SDL_VIDEO_OPENGL + attachment = GL_DEPTH; + attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE; +#endif + attrib = GL_DEPTH_BITS; + break; + case SDL_GL_STENCIL_SIZE: +#if SDL_VIDEO_OPENGL + attachment = GL_STENCIL; + attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE; +#endif + attrib = GL_STENCIL_BITS; + break; +#if SDL_VIDEO_OPENGL + case SDL_GL_ACCUM_RED_SIZE: + attrib = GL_ACCUM_RED_BITS; + break; + case SDL_GL_ACCUM_GREEN_SIZE: + attrib = GL_ACCUM_GREEN_BITS; + break; + case SDL_GL_ACCUM_BLUE_SIZE: + attrib = GL_ACCUM_BLUE_BITS; + break; + case SDL_GL_ACCUM_ALPHA_SIZE: + attrib = GL_ACCUM_ALPHA_BITS; + break; + case SDL_GL_STEREO: + attrib = GL_STEREO; + break; +#else + case SDL_GL_ACCUM_RED_SIZE: + case SDL_GL_ACCUM_GREEN_SIZE: + case SDL_GL_ACCUM_BLUE_SIZE: + case SDL_GL_ACCUM_ALPHA_SIZE: + case SDL_GL_STEREO: + /* none of these are supported in OpenGL ES */ + *value = 0; + return 0; +#endif + case SDL_GL_MULTISAMPLEBUFFERS: + attrib = GL_SAMPLE_BUFFERS; + break; + case SDL_GL_MULTISAMPLESAMPLES: + attrib = GL_SAMPLES; + break; + case SDL_GL_CONTEXT_RELEASE_BEHAVIOR: +#if SDL_VIDEO_OPENGL + attrib = GL_CONTEXT_RELEASE_BEHAVIOR; +#else + attrib = GL_CONTEXT_RELEASE_BEHAVIOR_KHR; +#endif + break; + case SDL_GL_BUFFER_SIZE: + { + int rsize = 0, gsize = 0, bsize = 0, asize = 0; + + /* There doesn't seem to be a single flag in OpenGL for this! */ + if (SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &rsize) < 0) { + return -1; + } + if (SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &gsize) < 0) { + return -1; + } + if (SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &bsize) < 0) { + return -1; + } + if (SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &asize) < 0) { + return -1; + } + + *value = rsize + gsize + bsize + asize; + return 0; + } + case SDL_GL_ACCELERATED_VISUAL: + { + /* FIXME: How do we get this information? */ + *value = (_this->gl_config.accelerated != 0); + return 0; + } + case SDL_GL_RETAINED_BACKING: + { + *value = _this->gl_config.retained_backing; + return 0; + } + case SDL_GL_CONTEXT_MAJOR_VERSION: + { + *value = _this->gl_config.major_version; + return 0; + } + case SDL_GL_CONTEXT_MINOR_VERSION: + { + *value = _this->gl_config.minor_version; + return 0; + } + case SDL_GL_CONTEXT_EGL: + /* FIXME: SDL_GL_CONTEXT_EGL to be deprecated in SDL 2.1 */ + { + if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) { + *value = 1; + } + else { + *value = 0; + } + return 0; + } + case SDL_GL_CONTEXT_FLAGS: + { + *value = _this->gl_config.flags; + return 0; + } + case SDL_GL_CONTEXT_PROFILE_MASK: + { + *value = _this->gl_config.profile_mask; + return 0; + } + case SDL_GL_SHARE_WITH_CURRENT_CONTEXT: + { + *value = _this->gl_config.share_with_current_context; + return 0; + } + case SDL_GL_FRAMEBUFFER_SRGB_CAPABLE: + { + *value = _this->gl_config.framebuffer_srgb_capable; + return 0; + } + case SDL_GL_CONTEXT_NO_ERROR: + { + *value = _this->gl_config.no_error; + return 0; + } + default: + return SDL_SetError("Unknown OpenGL attribute"); + } + +#if SDL_VIDEO_OPENGL + glGetStringFunc = SDL_GL_GetProcAddress("glGetString"); + if (!glGetStringFunc) { + return -1; + } + + if (attachmentattrib && isAtLeastGL3((const char *) glGetStringFunc(GL_VERSION))) { + glGetFramebufferAttachmentParameterivFunc = SDL_GL_GetProcAddress("glGetFramebufferAttachmentParameteriv"); + + if (glGetFramebufferAttachmentParameterivFunc) { + glGetFramebufferAttachmentParameterivFunc(GL_FRAMEBUFFER, attachment, attachmentattrib, (GLint *) value); + } else { + return -1; + } + } else +#endif + { + void (APIENTRY *glGetIntegervFunc) (GLenum pname, GLint * params); + glGetIntegervFunc = SDL_GL_GetProcAddress("glGetIntegerv"); + if (glGetIntegervFunc) { + glGetIntegervFunc(attrib, (GLint *) value); + } else { + return -1; + } + } + + glGetErrorFunc = SDL_GL_GetProcAddress("glGetError"); + if (!glGetErrorFunc) { + return -1; + } + + error = glGetErrorFunc(); + if (error != GL_NO_ERROR) { + if (error == GL_INVALID_ENUM) { + return SDL_SetError("OpenGL error: GL_INVALID_ENUM"); + } else if (error == GL_INVALID_VALUE) { + return SDL_SetError("OpenGL error: GL_INVALID_VALUE"); + } + return SDL_SetError("OpenGL error: %08X", error); + } + return 0; +#else + return SDL_Unsupported(); +#endif /* SDL_VIDEO_OPENGL */ +} + +SDL_GLContext +SDL_GL_CreateContext(SDL_Window * window) +{ + SDL_GLContext ctx = NULL; + CHECK_WINDOW_MAGIC(window, NULL); + + if (!(window->flags & SDL_WINDOW_OPENGL)) { + SDL_SetError("The specified window isn't an OpenGL window"); + return NULL; + } + + ctx = _this->GL_CreateContext(_this, window); + + /* Creating a context is assumed to make it current in the SDL driver. */ + if (ctx) { + _this->current_glwin = window; + _this->current_glctx = ctx; + SDL_TLSSet(_this->current_glwin_tls, window, NULL); + SDL_TLSSet(_this->current_glctx_tls, ctx, NULL); + } + return ctx; +} + +int +SDL_GL_MakeCurrent(SDL_Window * window, SDL_GLContext ctx) +{ + int retval; + + if (window == SDL_GL_GetCurrentWindow() && + ctx == SDL_GL_GetCurrentContext()) { + /* We're already current. */ + return 0; + } + + if (!ctx) { + window = NULL; + } else { + CHECK_WINDOW_MAGIC(window, -1); + + if (!(window->flags & SDL_WINDOW_OPENGL)) { + return SDL_SetError("The specified window isn't an OpenGL window"); + } + } + + retval = _this->GL_MakeCurrent(_this, window, ctx); + if (retval == 0) { + _this->current_glwin = window; + _this->current_glctx = ctx; + SDL_TLSSet(_this->current_glwin_tls, window, NULL); + SDL_TLSSet(_this->current_glctx_tls, ctx, NULL); + } + return retval; +} + +SDL_Window * +SDL_GL_GetCurrentWindow(void) +{ + if (!_this) { + SDL_UninitializedVideo(); + return NULL; + } + return (SDL_Window *)SDL_TLSGet(_this->current_glwin_tls); +} + +SDL_GLContext +SDL_GL_GetCurrentContext(void) +{ + if (!_this) { + SDL_UninitializedVideo(); + return NULL; + } + return (SDL_GLContext)SDL_TLSGet(_this->current_glctx_tls); +} + +void SDL_GL_GetDrawableSize(SDL_Window * window, int *w, int *h) +{ + CHECK_WINDOW_MAGIC(window,); + + if (_this->GL_GetDrawableSize) { + _this->GL_GetDrawableSize(_this, window, w, h); + } else { + SDL_GetWindowSize(window, w, h); + } +} + +int +SDL_GL_SetSwapInterval(int interval) +{ + if (!_this) { + return SDL_UninitializedVideo(); + } else if (SDL_GL_GetCurrentContext() == NULL) { + return SDL_SetError("No OpenGL context has been made current"); + } else if (_this->GL_SetSwapInterval) { + return _this->GL_SetSwapInterval(_this, interval); + } else { + return SDL_SetError("Setting the swap interval is not supported"); + } +} + +int +SDL_GL_GetSwapInterval(void) +{ + if (!_this) { + return 0; + } else if (SDL_GL_GetCurrentContext() == NULL) { + return 0; + } else if (_this->GL_GetSwapInterval) { + return _this->GL_GetSwapInterval(_this); + } else { + return 0; + } +} + +void +SDL_GL_SwapWindow(SDL_Window * window) +{ + CHECK_WINDOW_MAGIC(window,); + + if (!(window->flags & SDL_WINDOW_OPENGL)) { + SDL_SetError("The specified window isn't an OpenGL window"); + return; + } + + if (SDL_GL_GetCurrentWindow() != window) { + SDL_SetError("The specified window has not been made current"); + return; + } + + _this->GL_SwapWindow(_this, window); +} + +void +SDL_GL_DeleteContext(SDL_GLContext context) +{ + if (!_this || !context) { + return; + } + + if (SDL_GL_GetCurrentContext() == context) { + SDL_GL_MakeCurrent(NULL, NULL); + } + + _this->GL_DeleteContext(_this, context); +} + +#if 0 /* FIXME */ +/* + * Utility function used by SDL_WM_SetIcon(); flags & 1 for color key, flags + * & 2 for alpha channel. + */ +static void +CreateMaskFromColorKeyOrAlpha(SDL_Surface * icon, Uint8 * mask, int flags) +{ + int x, y; + Uint32 colorkey; +#define SET_MASKBIT(icon, x, y, mask) \ + mask[(y*((icon->w+7)/8))+(x/8)] &= ~(0x01<<(7-(x%8))) + + colorkey = icon->format->colorkey; + switch (icon->format->BytesPerPixel) { + case 1: + { + Uint8 *pixels; + for (y = 0; y < icon->h; ++y) { + pixels = (Uint8 *) icon->pixels + y * icon->pitch; + for (x = 0; x < icon->w; ++x) { + if (*pixels++ == colorkey) { + SET_MASKBIT(icon, x, y, mask); + } + } + } + } + break; + + case 2: + { + Uint16 *pixels; + for (y = 0; y < icon->h; ++y) { + pixels = (Uint16 *) icon->pixels + y * icon->pitch / 2; + for (x = 0; x < icon->w; ++x) { + if ((flags & 1) && *pixels == colorkey) { + SET_MASKBIT(icon, x, y, mask); + } else if ((flags & 2) + && (*pixels & icon->format->Amask) == 0) { + SET_MASKBIT(icon, x, y, mask); + } + pixels++; + } + } + } + break; + + case 4: + { + Uint32 *pixels; + for (y = 0; y < icon->h; ++y) { + pixels = (Uint32 *) icon->pixels + y * icon->pitch / 4; + for (x = 0; x < icon->w; ++x) { + if ((flags & 1) && *pixels == colorkey) { + SET_MASKBIT(icon, x, y, mask); + } else if ((flags & 2) + && (*pixels & icon->format->Amask) == 0) { + SET_MASKBIT(icon, x, y, mask); + } + pixels++; + } + } + } + break; + } +} + +/* + * Sets the window manager icon for the display window. + */ +void +SDL_WM_SetIcon(SDL_Surface * icon, Uint8 * mask) +{ + if (icon && _this->SetIcon) { + /* Generate a mask if necessary, and create the icon! */ + if (mask == NULL) { + int mask_len = icon->h * (icon->w + 7) / 8; + int flags = 0; + mask = (Uint8 *) SDL_malloc(mask_len); + if (mask == NULL) { + return; + } + SDL_memset(mask, ~0, mask_len); + if (icon->flags & SDL_SRCCOLORKEY) + flags |= 1; + if (icon->flags & SDL_SRCALPHA) + flags |= 2; + if (flags) { + CreateMaskFromColorKeyOrAlpha(icon, mask, flags); + } + _this->SetIcon(_this, icon, mask); + SDL_free(mask); + } else { + _this->SetIcon(_this, icon, mask); + } + } +} +#endif + +SDL_bool +SDL_GetWindowWMInfo(SDL_Window * window, struct SDL_SysWMinfo *info) +{ + CHECK_WINDOW_MAGIC(window, SDL_FALSE); + + if (!info) { + SDL_InvalidParamError("info"); + return SDL_FALSE; + } + info->subsystem = SDL_SYSWM_UNKNOWN; + + if (!_this->GetWindowWMInfo) { + SDL_Unsupported(); + return SDL_FALSE; + } + return (_this->GetWindowWMInfo(_this, window, info)); +} + +void +SDL_StartTextInput(void) +{ + SDL_Window *window; + + /* First, enable text events */ + SDL_EventState(SDL_TEXTINPUT, SDL_ENABLE); + SDL_EventState(SDL_TEXTEDITING, SDL_ENABLE); + + /* Then show the on-screen keyboard, if any */ + window = SDL_GetFocusWindow(); + if (window && _this && _this->ShowScreenKeyboard) { + _this->ShowScreenKeyboard(_this, window); + } + + /* Finally start the text input system */ + if (_this && _this->StartTextInput) { + _this->StartTextInput(_this); + } +} + +SDL_bool +SDL_IsTextInputActive(void) +{ + return (SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE); +} + +void +SDL_StopTextInput(void) +{ + SDL_Window *window; + + /* Stop the text input system */ + if (_this && _this->StopTextInput) { + _this->StopTextInput(_this); + } + + /* Hide the on-screen keyboard, if any */ + window = SDL_GetFocusWindow(); + if (window && _this && _this->HideScreenKeyboard) { + _this->HideScreenKeyboard(_this, window); + } + + /* Finally disable text events */ + SDL_EventState(SDL_TEXTINPUT, SDL_DISABLE); + SDL_EventState(SDL_TEXTEDITING, SDL_DISABLE); +} + +void +SDL_SetTextInputRect(SDL_Rect *rect) +{ + if (_this && _this->SetTextInputRect) { + _this->SetTextInputRect(_this, rect); + } +} + +SDL_bool +SDL_HasScreenKeyboardSupport(void) +{ + if (_this && _this->HasScreenKeyboardSupport) { + return _this->HasScreenKeyboardSupport(_this); + } + return SDL_FALSE; +} + +SDL_bool +SDL_IsScreenKeyboardShown(SDL_Window *window) +{ + if (window && _this && _this->IsScreenKeyboardShown) { + return _this->IsScreenKeyboardShown(_this, window); + } + return SDL_FALSE; +} + +#if SDL_VIDEO_DRIVER_ANDROID +#include "android/SDL_androidmessagebox.h" +#endif +#if SDL_VIDEO_DRIVER_WINDOWS +#include "windows/SDL_windowsmessagebox.h" +#endif +#if SDL_VIDEO_DRIVER_WINRT +#include "winrt/SDL_winrtmessagebox.h" +#endif +#if SDL_VIDEO_DRIVER_COCOA +#include "cocoa/SDL_cocoamessagebox.h" +#endif +#if SDL_VIDEO_DRIVER_UIKIT +#include "uikit/SDL_uikitmessagebox.h" +#endif +#if SDL_VIDEO_DRIVER_X11 +#include "x11/SDL_x11messagebox.h" +#endif + + +#if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT || SDL_VIDEO_DRIVER_COCOA || SDL_VIDEO_DRIVER_UIKIT || SDL_VIDEO_DRIVER_X11 +static SDL_bool SDL_MessageboxValidForDriver(const SDL_MessageBoxData *messageboxdata, SDL_SYSWM_TYPE drivertype) +{ + SDL_SysWMinfo info; + SDL_Window *window = messageboxdata->window; + + if (!window) { + return SDL_TRUE; + } + + SDL_VERSION(&info.version); + if (!SDL_GetWindowWMInfo(window, &info)) { + return SDL_TRUE; + } else { + return (info.subsystem == drivertype); + } +} +#endif + +int +SDL_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid) +{ + int dummybutton; + int retval = -1; + SDL_bool relative_mode; + int show_cursor_prev; + SDL_bool mouse_captured; + SDL_Window *current_window; + + if (!messageboxdata) { + return SDL_InvalidParamError("messageboxdata"); + } else if (messageboxdata->numbuttons < 0) { + return SDL_SetError("Invalid number of buttons"); + } + + current_window = SDL_GetKeyboardFocus(); + mouse_captured = current_window && ((SDL_GetWindowFlags(current_window) & SDL_WINDOW_MOUSE_CAPTURE) != 0); + relative_mode = SDL_GetRelativeMouseMode(); + SDL_CaptureMouse(SDL_FALSE); + SDL_SetRelativeMouseMode(SDL_FALSE); + show_cursor_prev = SDL_ShowCursor(1); + SDL_ResetKeyboard(); + + if (!buttonid) { + buttonid = &dummybutton; + } + + if (_this && _this->ShowMessageBox) { + retval = _this->ShowMessageBox(_this, messageboxdata, buttonid); + } + + /* It's completely fine to call this function before video is initialized */ +#if SDL_VIDEO_DRIVER_ANDROID + if (retval == -1 && + Android_ShowMessageBox(messageboxdata, buttonid) == 0) { + retval = 0; + } +#endif +#if SDL_VIDEO_DRIVER_WINDOWS + if (retval == -1 && + SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_WINDOWS) && + WIN_ShowMessageBox(messageboxdata, buttonid) == 0) { + retval = 0; + } +#endif +#if SDL_VIDEO_DRIVER_WINRT + if (retval == -1 && + SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_WINRT) && + WINRT_ShowMessageBox(messageboxdata, buttonid) == 0) { + retval = 0; + } +#endif +#if SDL_VIDEO_DRIVER_COCOA + if (retval == -1 && + SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_COCOA) && + Cocoa_ShowMessageBox(messageboxdata, buttonid) == 0) { + retval = 0; + } +#endif +#if SDL_VIDEO_DRIVER_UIKIT + if (retval == -1 && + SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_UIKIT) && + UIKit_ShowMessageBox(messageboxdata, buttonid) == 0) { + retval = 0; + } +#endif +#if SDL_VIDEO_DRIVER_X11 + if (retval == -1 && + SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_X11) && + X11_ShowMessageBox(messageboxdata, buttonid) == 0) { + retval = 0; + } +#endif + if (retval == -1) { + SDL_SetError("No message system available"); + } + + if (current_window) { + SDL_RaiseWindow(current_window); + if (mouse_captured) { + SDL_CaptureMouse(SDL_TRUE); + } + } + + SDL_ShowCursor(show_cursor_prev); + SDL_SetRelativeMouseMode(relative_mode); + + return retval; +} + +int +SDL_ShowSimpleMessageBox(Uint32 flags, const char *title, const char *message, SDL_Window *window) +{ +#ifdef __EMSCRIPTEN__ + /* !!! FIXME: propose a browser API for this, get this #ifdef out of here? */ + /* Web browsers don't (currently) have an API for a custom message box + that can block, but for the most common case (SDL_ShowSimpleMessageBox), + we can use the standard Javascript alert() function. */ + EM_ASM_({ + alert(UTF8ToString($0) + "\n\n" + UTF8ToString($1)); + }, title, message); + return 0; +#else + SDL_MessageBoxData data; + SDL_MessageBoxButtonData button; + + SDL_zero(data); + data.flags = flags; + data.title = title; + data.message = message; + data.numbuttons = 1; + data.buttons = &button; + data.window = window; + + SDL_zero(button); + button.flags |= SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT; + button.flags |= SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT; + button.text = "OK"; + + return SDL_ShowMessageBox(&data, NULL); +#endif +} + +SDL_bool +SDL_ShouldAllowTopmost(void) +{ + return SDL_GetHintBoolean(SDL_HINT_ALLOW_TOPMOST, SDL_TRUE); +} + +int +SDL_SetWindowHitTest(SDL_Window * window, SDL_HitTest callback, void *userdata) +{ + CHECK_WINDOW_MAGIC(window, -1); + + if (!_this->SetWindowHitTest) { + return SDL_Unsupported(); + } else if (_this->SetWindowHitTest(window, callback != NULL) == -1) { + return -1; + } + + window->hit_test = callback; + window->hit_test_data = userdata; + + return 0; +} + +float +SDL_ComputeDiagonalDPI(int hpix, int vpix, float hinches, float vinches) +{ + float den2 = hinches * hinches + vinches * vinches; + if (den2 <= 0.0f) { + return 0.0f; + } + + return (float)(SDL_sqrt((double)hpix * (double)hpix + (double)vpix * (double)vpix) / + SDL_sqrt((double)den2)); +} + +/* + * Functions used by iOS application delegates + */ +void SDL_OnApplicationWillTerminate(void) +{ + SDL_SendAppEvent(SDL_APP_TERMINATING); +} + +void SDL_OnApplicationDidReceiveMemoryWarning(void) +{ + SDL_SendAppEvent(SDL_APP_LOWMEMORY); +} + +void SDL_OnApplicationWillResignActive(void) +{ + if (_this) { + SDL_Window *window; + for (window = _this->windows; window != NULL; window = window->next) { + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0); + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MINIMIZED, 0, 0); + } + } + SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND); +} + +void SDL_OnApplicationDidEnterBackground(void) +{ + SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND); +} + +void SDL_OnApplicationWillEnterForeground(void) +{ + SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND); +} + +void SDL_OnApplicationDidBecomeActive(void) +{ + SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND); + + if (_this) { + SDL_Window *window; + for (window = _this->windows; window != NULL; window = window->next) { + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0); + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0); + } + } +} + +#define NOT_A_VULKAN_WINDOW "The specified window isn't a Vulkan window" + +int SDL_Vulkan_LoadLibrary(const char *path) +{ + int retval; + if (!_this) { + SDL_UninitializedVideo(); + return -1; + } + if (_this->vulkan_config.loader_loaded) { + if (path && SDL_strcmp(path, _this->vulkan_config.loader_path) != 0) { + return SDL_SetError("Vulkan loader library already loaded"); + } + retval = 0; + } else { + if (!_this->Vulkan_LoadLibrary) { + return SDL_SetError("Vulkan support is either not configured in SDL " + "or not available in current SDL video driver " + "(%s) or platform", _this->name); + } + retval = _this->Vulkan_LoadLibrary(_this, path); + } + if (retval == 0) { + _this->vulkan_config.loader_loaded++; + } + return retval; +} + +void *SDL_Vulkan_GetVkGetInstanceProcAddr(void) +{ + if (!_this) { + SDL_UninitializedVideo(); + return NULL; + } + if (!_this->vulkan_config.loader_loaded) { + SDL_SetError("No Vulkan loader has been loaded"); + return NULL; + } + return _this->vulkan_config.vkGetInstanceProcAddr; +} + +void SDL_Vulkan_UnloadLibrary(void) +{ + if (!_this) { + SDL_UninitializedVideo(); + return; + } + if (_this->vulkan_config.loader_loaded > 0) { + if (--_this->vulkan_config.loader_loaded > 0) { + return; + } + if (_this->Vulkan_UnloadLibrary) { + _this->Vulkan_UnloadLibrary(_this); + } + } +} + +SDL_bool SDL_Vulkan_GetInstanceExtensions(SDL_Window *window, unsigned *count, const char **names) +{ + if (window) { + CHECK_WINDOW_MAGIC(window, SDL_FALSE); + + if (!(window->flags & SDL_WINDOW_VULKAN)) + { + SDL_SetError(NOT_A_VULKAN_WINDOW); + return SDL_FALSE; + } + } + + if (!count) { + SDL_InvalidParamError("count"); + return SDL_FALSE; + } + + return _this->Vulkan_GetInstanceExtensions(_this, window, count, names); +} + +SDL_bool SDL_Vulkan_CreateSurface(SDL_Window *window, + VkInstance instance, + VkSurfaceKHR *surface) +{ + CHECK_WINDOW_MAGIC(window, SDL_FALSE); + + if (!(window->flags & SDL_WINDOW_VULKAN)) { + SDL_SetError(NOT_A_VULKAN_WINDOW); + return SDL_FALSE; + } + + if (!instance) { + SDL_InvalidParamError("instance"); + return SDL_FALSE; + } + + if (!surface) { + SDL_InvalidParamError("surface"); + return SDL_FALSE; + } + + return _this->Vulkan_CreateSurface(_this, window, instance, surface); +} + +void SDL_Vulkan_GetDrawableSize(SDL_Window * window, int *w, int *h) +{ + CHECK_WINDOW_MAGIC(window,); + + if (_this->Vulkan_GetDrawableSize) { + _this->Vulkan_GetDrawableSize(_this, window, w, h); + } else { + SDL_GetWindowSize(window, w, h); + } +} + +/* vi: set ts=4 sw=4 expandtab: */ |