| /* |
| Simple DirectMedia Layer |
| Copyright (C) 1997-2023 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 && !SDL_VIDEO_OPENGL |
| #include "SDL_opengles.h" |
| #endif /* SDL_VIDEO_OPENGL_ES && !SDL_VIDEO_OPENGL */ |
| |
| /* 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 |
| |
| #ifdef __LINUX__ |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.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_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_PS2 |
| &PS2_bootstrap, |
| #endif |
| #if SDL_VIDEO_DRIVER_PSP |
| &PSP_bootstrap, |
| #endif |
| #if SDL_VIDEO_DRIVER_VITA |
| &VITA_bootstrap, |
| #endif |
| #if SDL_VIDEO_DRIVER_N3DS |
| &N3DS_bootstrap, |
| #endif |
| #if SDL_VIDEO_DRIVER_KMSDRM |
| &KMSDRM_bootstrap, |
| #endif |
| #if SDL_VIDEO_DRIVER_RISCOS |
| &RISCOS_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_OS2 |
| &OS2DIVE_bootstrap, |
| &OS2VMAN_bootstrap, |
| #endif |
| #if SDL_VIDEO_DRIVER_NGAGE |
| &NGAGE_bootstrap, |
| #endif |
| #if SDL_VIDEO_DRIVER_OFFSCREEN |
| &OFFSCREEN_bootstrap, |
| #endif |
| #if SDL_VIDEO_DRIVER_DUMMY |
| &DUMMY_bootstrap, |
| #if SDL_INPUT_LINUXEV |
| &DUMMY_evdev_bootstrap, |
| #endif |
| #endif |
| NULL |
| }; |
| |
| #define CHECK_WINDOW_MAGIC(window, retval) \ |
| if (!_this) { \ |
| SDL_UninitializedVideo(); \ |
| return retval; \ |
| } \ |
| 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; \ |
| } \ |
| 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) |
| |
| #if defined(__MACOSX__) && defined(SDL_VIDEO_DRIVER_COCOA) |
| /* 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 |
| |
| /* Convenience functions for reading driver flags */ |
| static SDL_bool DisableDisplayModeSwitching(_THIS) |
| { |
| return !!(_this->quirk_flags & VIDEO_DEVICE_QUIRK_DISABLE_DISPLAY_MODE_SWITCHING); |
| } |
| |
| static SDL_bool DisableUnsetFullscreenOnMinimize(_THIS) |
| { |
| return !!(_this->quirk_flags & VIDEO_DEVICE_QUIRK_DISABLE_UNSET_FULLSCREEN_ON_MINIMIZE); |
| } |
| |
| /* 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 Uint32 SDL_DefaultGraphicsBackends(SDL_VideoDevice *_this) |
| { |
| #if (SDL_VIDEO_OPENGL && __MACOSX__) || (__IPHONEOS__ && !TARGET_OS_MACCATALYST) || __ANDROID__ || __NACL__ |
| if (_this->GL_CreateContext != NULL) { |
| return SDL_WINDOW_OPENGL; |
| } |
| #endif |
| #if SDL_VIDEO_METAL && (TARGET_OS_MACCATALYST || __MACOSX__ || __IPHONEOS__) |
| if (_this->Metal_CreateView != NULL) { |
| return SDL_WINDOW_METAL; |
| } |
| #endif |
| return 0; |
| } |
| |
| static int SDL_CreateWindowTexture(SDL_VideoDevice *_this, SDL_Window *window, Uint32 *format, void **pixels, int *pitch) |
| { |
| SDL_RendererInfo info; |
| SDL_WindowTextureData *data = SDL_GetWindowData(window, SDL_WINDOWTEXTUREDATA); |
| int i; |
| int w, h; |
| |
| SDL_GetWindowSizeInPixels(window, &w, &h); |
| |
| if (data == NULL) { |
| SDL_Renderer *renderer = NULL; |
| const char *hint = SDL_GetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION); |
| const SDL_bool specific_accelerated_renderer = (hint && *hint != '0' && *hint != '1' && |
| SDL_strcasecmp(hint, "true") != 0 && |
| SDL_strcasecmp(hint, "false") != 0 && |
| SDL_strcasecmp(hint, "software") != 0); |
| |
| /* Check to see if there's a specific driver requested */ |
| if (specific_accelerated_renderer) { |
| for (i = 0; i < SDL_GetNumRenderDrivers(); ++i) { |
| SDL_GetRenderDriverInfo(i, &info); |
| if (SDL_strcasecmp(info.name, hint) == 0) { |
| renderer = SDL_CreateRenderer(window, i, 0); |
| break; |
| } |
| } |
| if (renderer == NULL || (SDL_GetRendererInfo(renderer, &info) == -1)) { |
| if (renderer) { |
| SDL_DestroyRenderer(renderer); |
| } |
| return SDL_SetError("Requested renderer for " SDL_HINT_FRAMEBUFFER_ACCELERATION " is not available"); |
| } |
| /* if it was specifically requested, even if SDL_RENDERER_ACCELERATED isn't set, we'll accept this renderer. */ |
| } else { |
| for (i = 0; i < SDL_GetNumRenderDrivers(); ++i) { |
| SDL_GetRenderDriverInfo(i, &info); |
| if (SDL_strcmp(info.name, "software") != 0) { |
| renderer = SDL_CreateRenderer(window, i, 0); |
| if (renderer && (SDL_GetRendererInfo(renderer, &info) == 0) && (info.flags & SDL_RENDERER_ACCELERATED)) { |
| break; /* this will work. */ |
| } |
| if (renderer) { /* wasn't accelerated, etc, skip it. */ |
| SDL_DestroyRenderer(renderer); |
| renderer = NULL; |
| } |
| } |
| } |
| if (renderer == NULL) { |
| return SDL_SetError("No hardware accelerated renderers available"); |
| } |
| } |
| |
| SDL_assert(renderer != NULL); /* should have explicitly checked this above. */ |
| |
| /* Create the data after we successfully create the renderer (bug #1116) */ |
| data = (SDL_WindowTextureData *)SDL_calloc(1, sizeof(*data)); |
| if (data == NULL) { |
| SDL_DestroyRenderer(renderer); |
| return SDL_OutOfMemory(); |
| } |
| SDL_SetWindowData(window, SDL_WINDOWTEXTUREDATA, data); |
| |
| data->renderer = renderer; |
| } else { |
| if (SDL_GetRendererInfo(data->renderer, &info) == -1) { |
| return -1; |
| } |
| } |
| |
| /* Free any old texture and pixel data */ |
| if (data->texture) { |
| SDL_DestroyTexture(data->texture); |
| data->texture = NULL; |
| } |
| SDL_free(data->pixels); |
| data->pixels = NULL; |
| |
| /* Find the first format without an alpha channel */ |
| *format = info.texture_formats[0]; |
| |
| for (i = 0; i < (int)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, |
| w, h); |
| if (!data->texture) { |
| /* codechecker_false_positive [Malloc] Static analyzer doesn't realize allocated `data` is saved to SDL_WINDOWTEXTUREDATA and not leaked here. */ |
| return -1; /* NOLINT(clang-analyzer-unix.Malloc) */ |
| } |
| |
| /* Create framebuffer data */ |
| data->bytes_per_pixel = SDL_BYTESPERPIXEL(*format); |
| data->pitch = (((w * data->bytes_per_pixel) + 3) & ~3); |
| |
| { |
| /* Make static analysis happy about potential SDL_malloc(0) calls. */ |
| const size_t allocsize = (size_t)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 SDL_VideoDevice *_this = NULL; |
| static SDL_atomic_t SDL_messagebox_count; |
| |
| static int SDL_UpdateWindowTexture(SDL_VideoDevice *unused, SDL_Window *window, const SDL_Rect *rects, int numrects) |
| { |
| SDL_WindowTextureData *data; |
| SDL_Rect rect; |
| void *src; |
| int w, h; |
| |
| SDL_GetWindowSizeInPixels(window, &w, &h); |
| |
| data = SDL_GetWindowData(window, SDL_WINDOWTEXTUREDATA); |
| if (data == NULL || !data->texture) { |
| return SDL_SetError("No window texture data"); |
| } |
| |
| /* Update a single rect that contains subrects for best DMA performance */ |
| if (SDL_GetSpanEnclosingRect(w, 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 == NULL) { |
| return; |
| } |
| if (data->texture) { |
| SDL_DestroyTexture(data->texture); |
| } |
| if (data->renderer) { |
| SDL_DestroyRenderer(data->renderer); |
| } |
| SDL_free(data->pixels); |
| SDL_free(data); |
| } |
| |
| static int SDLCALL 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; |
| SDL_bool init_events = SDL_FALSE; |
| SDL_bool init_keyboard = SDL_FALSE; |
| SDL_bool init_mouse = SDL_FALSE; |
| SDL_bool init_touch = SDL_FALSE; |
| int i = 0; |
| |
| /* 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) { |
| goto pre_driver_error; |
| } |
| init_events = SDL_TRUE; |
| if (SDL_KeyboardInit() < 0) { |
| goto pre_driver_error; |
| } |
| init_keyboard = SDL_TRUE; |
| if (SDL_MouseInit() < 0) { |
| goto pre_driver_error; |
| } |
| init_mouse = SDL_TRUE; |
| if (SDL_TouchInit() < 0) { |
| goto pre_driver_error; |
| } |
| init_touch = SDL_TRUE; |
| |
| /* Select the proper video driver */ |
| video = NULL; |
| if (driver_name == NULL) { |
| driver_name = SDL_GetHint(SDL_HINT_VIDEODRIVER); |
| } |
| if (driver_name != NULL && *driver_name != 0) { |
| const char *driver_attempt = driver_name; |
| while (driver_attempt != NULL && *driver_attempt != 0 && video == NULL) { |
| const char *driver_attempt_end = SDL_strchr(driver_attempt, ','); |
| size_t driver_attempt_len = (driver_attempt_end != NULL) ? (driver_attempt_end - driver_attempt) |
| : SDL_strlen(driver_attempt); |
| |
| for (i = 0; bootstrap[i]; ++i) { |
| if ((driver_attempt_len == SDL_strlen(bootstrap[i]->name)) && |
| (SDL_strncasecmp(bootstrap[i]->name, driver_attempt, driver_attempt_len) == 0)) { |
| video = bootstrap[i]->create(); |
| break; |
| } |
| } |
| |
| driver_attempt = (driver_attempt_end != NULL) ? (driver_attempt_end + 1) : NULL; |
| } |
| } else { |
| for (i = 0; bootstrap[i]; ++i) { |
| video = bootstrap[i]->create(); |
| if (video != NULL) { |
| break; |
| } |
| } |
| } |
| if (video == NULL) { |
| if (driver_name) { |
| SDL_SetError("%s not available", driver_name); |
| goto pre_driver_error; |
| } |
| SDL_SetError("No available video device"); |
| goto pre_driver_error; |
| } |
| |
| /* From this point on, use SDL_VideoQuit to cleanup on error, rather than |
| pre_driver_error. */ |
| _this = video; |
| _this->name = bootstrap[i]->name; |
| _this->next_object_id = 1; |
| _this->thread = SDL_ThreadID(); |
| |
| /* 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"); |
| } |
| |
| /* 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; |
| |
| pre_driver_error: |
| SDL_assert(_this == NULL); |
| if (init_touch) { |
| SDL_TouchQuit(); |
| } |
| if (init_mouse) { |
| SDL_MouseQuit(); |
| } |
| if (init_keyboard) { |
| SDL_KeyboardQuit(); |
| } |
| if (init_events) { |
| SDL_QuitSubSystem(SDL_INIT_EVENTS); |
| } |
| return -1; |
| } |
| |
| const char *SDL_GetCurrentVideoDriver() |
| { |
| if (_this == NULL) { |
| SDL_UninitializedVideo(); |
| return NULL; |
| } |
| return _this->name; |
| } |
| |
| SDL_VideoDevice *SDL_GetVideoDevice(void) |
| { |
| return _this; |
| } |
| |
| SDL_bool SDL_OnVideoThread() |
| { |
| return (_this && SDL_ThreadID() == _this->thread) ? SDL_TRUE : SDL_FALSE; |
| } |
| |
| 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, SDL_FALSE); |
| } |
| |
| int SDL_AddVideoDisplay(const SDL_VideoDisplay *display, SDL_bool send_event) |
| { |
| 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); |
| } |
| |
| if (send_event) { |
| SDL_SendDisplayEvent(&_this->displays[index], SDL_DISPLAYEVENT_CONNECTED, 0); |
| } |
| } else { |
| SDL_OutOfMemory(); |
| } |
| return index; |
| } |
| |
| void SDL_DelVideoDisplay(int index) |
| { |
| if (index < 0 || index >= _this->num_displays) { |
| return; |
| } |
| |
| SDL_SendDisplayEvent(&_this->displays[index], SDL_DISPLAYEVENT_DISCONNECTED, 0); |
| |
| if (index < (_this->num_displays - 1)) { |
| SDL_free(_this->displays[index].driverdata); |
| SDL_memmove(&_this->displays[index], &_this->displays[index + 1], (_this->num_displays - index - 1) * sizeof(_this->displays[index])); |
| } |
| --_this->num_displays; |
| } |
| |
| int SDL_GetNumVideoDisplays(void) |
| { |
| if (_this == NULL) { |
| 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; |
| } |
| |
| SDL_bool SDL_IsVideoContextExternal(void) |
| { |
| return SDL_GetHintBoolean(SDL_HINT_VIDEO_EXTERNAL_CONTEXT, SDL_FALSE); |
| } |
| |
| const char *SDL_GetDisplayName(int displayIndex) |
| { |
| CHECK_DISPLAY_INDEX(displayIndex, NULL); |
| |
| return _this->displays[displayIndex].name; |
| } |
| |
| int SDL_GetDisplayBounds(int displayIndex, SDL_Rect *rect) |
| { |
| SDL_VideoDisplay *display; |
| |
| CHECK_DISPLAY_INDEX(displayIndex, -1); |
| |
| if (rect == NULL) { |
| return SDL_InvalidParamError("rect"); |
| } |
| |
| 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; |
| } |
| |
| static int ParseDisplayUsableBoundsHint(SDL_Rect *rect) |
| { |
| const char *hint = SDL_GetHint(SDL_HINT_DISPLAY_USABLE_BOUNDS); |
| return hint && (SDL_sscanf(hint, "%d,%d,%d,%d", &rect->x, &rect->y, &rect->w, &rect->h) == 4); |
| } |
| |
| int SDL_GetDisplayUsableBounds(int displayIndex, SDL_Rect *rect) |
| { |
| SDL_VideoDisplay *display; |
| |
| CHECK_DISPLAY_INDEX(displayIndex, -1); |
| |
| if (rect == NULL) { |
| return SDL_InvalidParamError("rect"); |
| } |
| |
| display = &_this->displays[displayIndex]; |
| |
| if ((displayIndex == 0) && ParseDisplayUsableBoundsHint(rect)) { |
| return 0; |
| } |
| |
| 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); |
| } |
| |
| 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 == NULL) { |
| 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; |
| } |
| |
| void SDL_SetCurrentDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode) |
| { |
| SDL_memcpy(&display->current_mode, mode, sizeof(*mode)); |
| } |
| |
| void SDL_SetDesktopDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode) |
| { |
| if (display->desktop_mode.driverdata) { |
| SDL_free(display->desktop_mode.driverdata); |
| } |
| SDL_memcpy(&display->desktop_mode, mode, sizeof(*mode)); |
| } |
| |
| 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]); |
| } |
| |
| void SDL_ResetDisplayModes(int displayIndex) |
| { |
| SDL_VideoDisplay *display; |
| int i; |
| |
| CHECK_DISPLAY_INDEX(displayIndex, ); |
| |
| display = &_this->displays[displayIndex]; |
| for (i = display->num_display_modes; i--;) { |
| SDL_free(display->display_modes[i].driverdata); |
| display->display_modes[i].driverdata = NULL; |
| } |
| SDL_free(display->display_modes); |
| display->display_modes = NULL; |
| display->num_display_modes = 0; |
| display->max_display_modes = 0; |
| } |
| |
| 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 == NULL || closest == NULL) { |
| SDL_InvalidParamError("mode/closest"); |
| 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 == NULL || 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; |
| int result; |
| |
| /* Mode switching disabled via driver quirk flag, nothing to do and cannot fail. */ |
| if (DisableDisplayModeSwitching(_this)) { |
| return 0; |
| } |
| |
| 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"); |
| } |
| _this->setting_display_mode = SDL_TRUE; |
| result = _this->SetDisplayMode(_this, display, &display_mode); |
| _this->setting_display_mode = SDL_FALSE; |
| if (result < 0) { |
| return -1; |
| } |
| SDL_SetCurrentDisplayMode(display, &display_mode); |
| return 0; |
| } |
| |
| SDL_VideoDisplay *SDL_GetDisplay(int displayIndex) |
| { |
| CHECK_DISPLAY_INDEX(displayIndex, NULL); |
| |
| return &_this->displays[displayIndex]; |
| } |
| |
| /** |
| * If x, y are outside of rect, snaps them to the closest point inside rect |
| * (between rect->x, rect->y, inclusive, and rect->x + w, rect->y + h, exclusive) |
| */ |
| static void SDL_GetClosestPointOnRect(const SDL_Rect *rect, SDL_Point *point) |
| { |
| const int right = rect->x + rect->w - 1; |
| const int bottom = rect->y + rect->h - 1; |
| |
| if (point->x < rect->x) { |
| point->x = rect->x; |
| } else if (point->x > right) { |
| point->x = right; |
| } |
| |
| if (point->y < rect->y) { |
| point->y = rect->y; |
| } else if (point->y > bottom) { |
| point->y = bottom; |
| } |
| } |
| |
| static int GetRectDisplayIndex(int x, int y, int w, int h) |
| { |
| int i, dist; |
| int closest = -1; |
| int closest_dist = 0x7FFFFFFF; |
| SDL_Point closest_point_on_display; |
| SDL_Point delta; |
| SDL_Point center; |
| center.x = x + w / 2; |
| center.y = y + h / 2; |
| |
| if (_this) { |
| for (i = 0; i < _this->num_displays; ++i) { |
| SDL_Rect display_rect; |
| SDL_GetDisplayBounds(i, &display_rect); |
| |
| /* Check if the window is fully enclosed */ |
| if (SDL_EnclosePoints(¢er, 1, &display_rect, NULL)) { |
| return i; |
| } |
| |
| /* Snap window center to the display rect */ |
| closest_point_on_display = center; |
| SDL_GetClosestPointOnRect(&display_rect, &closest_point_on_display); |
| |
| delta.x = center.x - closest_point_on_display.x; |
| delta.y = center.y - closest_point_on_display.y; |
| 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; |
| } |
| |
| int SDL_GetPointDisplayIndex(const SDL_Point *point) |
| { |
| return GetRectDisplayIndex(point->x, point->y, 1, 1); |
| } |
| |
| int SDL_GetRectDisplayIndex(const SDL_Rect *rect) |
| { |
| return GetRectDisplayIndex(rect->x, rect->y, rect->w, rect->h); |
| } |
| |
| int SDL_GetWindowDisplayIndex(SDL_Window *window) |
| { |
| int displayIndex = -1; |
| |
| CHECK_WINDOW_MAGIC(window, -1); |
| if (_this->GetWindowDisplayIndex) { |
| displayIndex = _this->GetWindowDisplayIndex(_this, window); |
| } |
| |
| /* A backend implementation may fail to get a display index for the window |
| * (for example if the window is off-screen), but other code may expect it |
| * to succeed in that situation, so we fall back to a generic position- |
| * based implementation in that case. */ |
| if (displayIndex >= 0) { |
| return displayIndex; |
| } else { |
| int i; |
| 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; |
| } |
| |
| displayIndex = GetRectDisplayIndex(window->x, window->y, window->w, window->h); |
| |
| /* Find the display containing the window if fullscreen */ |
| for (i = 0; i < _this->num_displays; ++i) { |
| SDL_VideoDisplay *display = &_this->displays[i]; |
| |
| if (display->fullscreen_window == window) { |
| if (displayIndex != i) { |
| if (displayIndex < 0) { |
| displayIndex = i; |
| } else { |
| SDL_VideoDisplay *new_display = &_this->displays[displayIndex]; |
| |
| /* The window was moved to a different display */ |
| if (new_display->fullscreen_window != NULL) { |
| /* Uh oh, there's already a fullscreen window here */ |
| } else { |
| new_display->fullscreen_window = window; |
| } |
| display->fullscreen_window = NULL; |
| } |
| } |
| break; |
| } |
| } |
| return displayIndex; |
| } |
| } |
| |
| 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) { |
| if (SDL_SetDisplayModeForDisplay(SDL_GetDisplayForWindow(window), &fullscreen_mode) == 0) { |
| #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(window, SDL_WINDOWEVENT_RESIZED, fullscreen_mode.w, fullscreen_mode.h); |
| #endif |
| } |
| } |
| } |
| 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 == NULL) { |
| 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)) { |
| SDL_zerop(mode); |
| return SDL_SetError("Couldn't find display mode match"); |
| } |
| |
| *mode = fullscreen_mode; |
| |
| return 0; |
| } |
| |
| void *SDL_GetWindowICCProfile(SDL_Window *window, size_t *size) |
| { |
| if (!_this->GetWindowICCProfile) { |
| SDL_Unsupported(); |
| return NULL; |
| } |
| return _this->GetWindowICCProfile(_this, window, size); |
| } |
| |
| 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; |
| } |
| |
| #if defined(__MACOSX__) && defined(SDL_VIDEO_DRIVER_COCOA) |
| /* 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 (display == NULL) { /* No display connected, nothing to do. */ |
| return 0; |
| } |
| |
| 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; |
| } |
| if (!fullscreen) { |
| if (_this->SetWindowFullscreen) { |
| _this->SetWindowFullscreen(_this, window, display, SDL_FALSE); |
| } |
| window->last_fullscreen_flags = window->flags; |
| 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 ((other->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) { |
| #if !defined(__ANDROID__) && !defined(__WIN32__) |
| /* 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. |
| */ |
| |
| /* This is also unnecessary on Win32 (WIN_SetWindowFullscreen calls SetWindowPos, |
| * WM_WINDOWPOSCHANGED will send SDL_WINDOWEVENT_RESIZED). Also, on Windows with DPI scaling enabled, |
| * we're keeping modes in pixels, but window sizes in dpi-scaled points, so this would be a unit mismatch. |
| */ |
| 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 | SDL_WINDOW_METAL) |
| |
| 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_MOUSE_GRABBED) { |
| /* We must specifically call SDL_SetWindowGrab() and not |
| SDL_SetWindowMouseGrab() here because older applications may use |
| this flag plus SDL_HINT_GRAB_KEYBOARD to indicate that they want |
| the keyboard grabbed too and SDL_SetWindowMouseGrab() won't do that. |
| */ |
| SDL_SetWindowGrab(window, SDL_TRUE); |
| } |
| if (flags & SDL_WINDOW_KEYBOARD_GRABBED) { |
| SDL_SetWindowKeyboardGrab(window, SDL_TRUE); |
| } |
| if (!(flags & SDL_WINDOW_HIDDEN)) { |
| SDL_ShowWindow(window); |
| } |
| } |
| |
| static int SDL_ContextNotSupported(const char *name) |
| { |
| return SDL_SetError("%s support is either not configured in SDL " |
| "or not available in current SDL video driver " |
| "(%s) or platform", |
| name, |
| _this->name); |
| } |
| |
| static int SDL_DllNotSupported(const char *name) |
| { |
| return SDL_SetError("No dynamic %s support in current SDL video driver (%s)", name, _this->name); |
| } |
| |
| SDL_Window *SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags) |
| { |
| SDL_Window *window; |
| Uint32 type_flags, graphics_flags; |
| |
| if (_this == NULL) { |
| /* Initialize the video system if needed */ |
| if (SDL_Init(SDL_INIT_VIDEO) < 0) { |
| return NULL; |
| } |
| |
| /* Make clang-tidy happy */ |
| if (_this == NULL) { |
| return NULL; |
| } |
| } |
| |
| /* Make sure the display list is up to date for window placement */ |
| if (_this->RefreshDisplays) { |
| _this->RefreshDisplays(_this); |
| } |
| |
| /* ensure no more than one of these flags is set */ |
| type_flags = flags & (SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_POPUP_MENU); |
| if (type_flags & (type_flags - 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; |
| } |
| |
| /* ensure no more than one of these flags is set */ |
| graphics_flags = flags & (SDL_WINDOW_OPENGL | SDL_WINDOW_METAL | SDL_WINDOW_VULKAN); |
| if (graphics_flags & (graphics_flags - 1)) { |
| SDL_SetError("Conflicting window flags specified"); |
| return NULL; |
| } |
| |
| /* Some platforms have certain graphics backends enabled by default */ |
| if (!graphics_flags && !SDL_IsVideoContextExternal()) { |
| flags |= SDL_DefaultGraphicsBackends(_this); |
| } |
| |
| if (flags & SDL_WINDOW_OPENGL) { |
| if (!_this->GL_CreateContext) { |
| SDL_ContextNotSupported("OpenGL"); |
| return NULL; |
| } |
| if (SDL_GL_LoadLibrary(NULL) < 0) { |
| return NULL; |
| } |
| } |
| |
| if (flags & SDL_WINDOW_VULKAN) { |
| if (!_this->Vulkan_CreateSurface) { |
| SDL_ContextNotSupported("Vulkan"); |
| return NULL; |
| } |
| if (SDL_Vulkan_LoadLibrary(NULL) < 0) { |
| return NULL; |
| } |
| } |
| |
| if (flags & SDL_WINDOW_METAL) { |
| if (!_this->Metal_CreateView) { |
| SDL_ContextNotSupported("Metal"); |
| 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 == NULL) { |
| 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); |
| |
| /* for real fullscreen we might switch the resolution, so get width and height |
| * from closest supported mode and use that instead of current resolution |
| */ |
| if ((flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP && (bounds.w != w || bounds.h != h)) { |
| SDL_DisplayMode fullscreen_mode, closest_mode; |
| SDL_zero(fullscreen_mode); |
| fullscreen_mode.w = w; |
| fullscreen_mode.h = h; |
| if (SDL_GetClosestDisplayModeForDisplay(display, &fullscreen_mode, &closest_mode) != NULL) { |
| bounds.w = closest_mode.w; |
| bounds.h = closest_mode.h; |
| } |
| } |
| window->fullscreen_mode.w = bounds.w; |
| window->fullscreen_mode.h = bounds.h; |
| 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; |
| window->display_index = SDL_GetWindowDisplayIndex(window); |
| |
| 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__) && !defined(__GDK__) |
| 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; |
| Uint32 flags = SDL_WINDOW_FOREIGN; |
| |
| if (_this == NULL) { |
| SDL_UninitializedVideo(); |
| return NULL; |
| } |
| if (!_this->CreateSDLWindowFrom) { |
| SDL_Unsupported(); |
| return NULL; |
| } |
| |
| if (SDL_GetHintBoolean(SDL_HINT_VIDEO_FOREIGN_WINDOW_OPENGL, SDL_FALSE)) { |
| if (!_this->GL_CreateContext) { |
| SDL_ContextNotSupported("OpenGL"); |
| return NULL; |
| } |
| if (SDL_GL_LoadLibrary(NULL) < 0) { |
| return NULL; |
| } |
| flags |= SDL_WINDOW_OPENGL; |
| } |
| |
| if (SDL_GetHintBoolean(SDL_HINT_VIDEO_FOREIGN_WINDOW_VULKAN, SDL_FALSE)) { |
| if (!_this->Vulkan_CreateSurface) { |
| SDL_ContextNotSupported("Vulkan"); |
| 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; |
| } |
| flags |= SDL_WINDOW_VULKAN; |
| } |
| |
| window = (SDL_Window *)SDL_calloc(1, sizeof(*window)); |
| if (window == NULL) { |
| SDL_OutOfMemory(); |
| return NULL; |
| } |
| window->magic = &_this->window_magic; |
| window->id = _this->next_object_id++; |
| window->flags = flags; |
| 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; |
| } |
| |
| window->display_index = SDL_GetWindowDisplayIndex(window); |
| PrepareDragAndDropSupport(window); |
| |
| return window; |
| } |
| |
| int SDL_RecreateWindow(SDL_Window *window, Uint32 flags) |
| { |
| SDL_bool loaded_opengl = SDL_FALSE; |
| SDL_bool need_gl_unload = SDL_FALSE; |
| SDL_bool need_gl_load = SDL_FALSE; |
| SDL_bool loaded_vulkan = SDL_FALSE; |
| SDL_bool need_vulkan_unload = SDL_FALSE; |
| SDL_bool need_vulkan_load = SDL_FALSE; |
| Uint32 graphics_flags; |
| |
| /* ensure no more than one of these flags is set */ |
| graphics_flags = flags & (SDL_WINDOW_OPENGL | SDL_WINDOW_METAL | SDL_WINDOW_VULKAN); |
| if (graphics_flags & (graphics_flags - 1)) { |
| return SDL_SetError("Conflicting window flags specified"); |
| } |
| |
| if ((flags & SDL_WINDOW_OPENGL) && !_this->GL_CreateContext) { |
| return SDL_ContextNotSupported("OpenGL"); |
| } |
| if ((flags & SDL_WINDOW_VULKAN) && !_this->Vulkan_CreateSurface) { |
| return SDL_ContextNotSupported("Vulkan"); |
| } |
| if ((flags & SDL_WINDOW_METAL) && !_this->Metal_CreateView) { |
| return SDL_ContextNotSupported("Metal"); |
| } |
| |
| 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. */ |
| if (!(window->flags & SDL_WINDOW_FOREIGN)) { |
| SDL_HideWindow(window); |
| } |
| |
| /* Tear down the old native window */ |
| SDL_DestroyWindowSurface(window); |
| |
| if (_this->checked_texture_framebuffer) { /* never checked? No framebuffer to destroy. Don't risk calling the wrong implementation. */ |
| 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) { |
| need_gl_load = SDL_TRUE; |
| } else { |
| need_gl_unload = SDL_TRUE; |
| } |
| } else if (window->flags & SDL_WINDOW_OPENGL) { |
| need_gl_unload = SDL_TRUE; |
| need_gl_load = SDL_TRUE; |
| } |
| |
| if ((window->flags & SDL_WINDOW_VULKAN) != (flags & SDL_WINDOW_VULKAN)) { |
| if (flags & SDL_WINDOW_VULKAN) { |
| need_vulkan_load = SDL_TRUE; |
| } else { |
| need_vulkan_unload = SDL_TRUE; |
| } |
| } else if (window->flags & SDL_WINDOW_VULKAN) { |
| need_vulkan_unload = SDL_TRUE; |
| need_vulkan_load = SDL_TRUE; |
| } |
| |
| if (need_gl_unload) { |
| SDL_GL_UnloadLibrary(); |
| } |
| |
| if (need_vulkan_unload) { |
| SDL_Vulkan_UnloadLibrary(); |
| } |
| |
| if (need_gl_load) { |
| if (SDL_GL_LoadLibrary(NULL) < 0) { |
| return -1; |
| } |
| loaded_opengl = SDL_TRUE; |
| } |
| |
| if (need_vulkan_load) { |
| if (SDL_Vulkan_LoadLibrary(NULL) < 0) { |
| return -1; |
| } |
| loaded_vulkan = SDL_TRUE; |
| } |
| |
| 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; |
| } |
| if (loaded_vulkan) { |
| SDL_Vulkan_UnloadLibrary(); |
| window->flags &= ~SDL_WINDOW_VULKAN; |
| } |
| 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 == NULL) { |
| 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 == NULL) { |
| 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->windowed.w) / 2; |
| } |
| if (SDL_WINDOWPOS_ISCENTERED(y)) { |
| y = bounds.y + (bounds.h - window->windowed.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); |
| } |
| } |
| } |
| |
| 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); |
| 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_SetWindowAlwaysOnTop(SDL_Window *window, SDL_bool on_top) |
| { |
| CHECK_WINDOW_MAGIC(window, ); |
| if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { |
| const int want = (on_top != SDL_FALSE); /* normalize the flag. */ |
| const int have = ((window->flags & SDL_WINDOW_ALWAYS_ON_TOP) != 0); |
| if ((want != have) && (_this->SetWindowAlwaysOnTop)) { |
| if (want) { |
| window->flags |= SDL_WINDOW_ALWAYS_ON_TOP; |
| } else { |
| window->flags &= ~SDL_WINDOW_ALWAYS_ON_TOP; |
| } |
| |
| _this->SetWindowAlwaysOnTop(_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 { |
| int old_w = window->w; |
| int old_h = window->h; |
| window->w = w; |
| window->h = h; |
| if (_this->SetWindowSize) { |
| _this->SetWindowSize(_this, window); |
| } |
| if (window->w != old_w || window->h != old_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 == NULL) { |
| top = &dummy; |
| } |
| if (left == NULL) { |
| left = &dummy; |
| } |
| if (right == NULL) { |
| right = &dummy; |
| } |
| if (bottom == NULL) { |
| 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_GetWindowSizeInPixels(SDL_Window *window, int *w, int *h) |
| { |
| int filter; |
| |
| CHECK_WINDOW_MAGIC(window, ); |
| |
| if (w == NULL) { |
| w = &filter; |
| } |
| |
| if (h == NULL) { |
| h = &filter; |
| } |
| |
| if (_this->GetWindowSizeInPixels) { |
| _this->GetWindowSizeInPixels(_this, window, w, h); |
| } else { |
| SDL_GetWindowSize(window, w, h); |
| } |
| } |
| |
| 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); |
| } |
| } |
| |
| static SDL_bool CanMinimizeWindow(SDL_Window *window) |
| { |
| if (!_this->MinimizeWindow) { |
| return SDL_FALSE; |
| } |
| return SDL_TRUE; |
| } |
| |
| void SDL_MinimizeWindow(SDL_Window *window) |
| { |
| CHECK_WINDOW_MAGIC(window, ); |
| |
| if (window->flags & SDL_WINDOW_MINIMIZED) { |
| return; |
| } |
| |
| if (!CanMinimizeWindow(window)) { |
| return; |
| } |
| |
| if (!DisableUnsetFullscreenOnMinimize(_this)) { |
| 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 = 0; |
| void *pixels = NULL; |
| int pitch = 0; |
| int bpp; |
| Uint32 Rmask, Gmask, Bmask, Amask; |
| SDL_bool created_framebuffer = SDL_FALSE; |
| int w, h; |
| |
| SDL_GetWindowSizeInPixels(window, &w, &h); |
| |
| /* This will switch the video backend from using a software surface to |
| using a GPU texture through the 2D render API, if we think this would |
| be more efficient. This only checks once, on demand. */ |
| if (!_this->checked_texture_framebuffer) { |
| SDL_bool attempt_texture_framebuffer = SDL_TRUE; |
| |
| /* See if the user or application wants to specifically disable the framebuffer */ |
| const char *hint = SDL_GetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION); |
| if (hint) { |
| if ((*hint == '0') || (SDL_strcasecmp(hint, "false") == 0) || (SDL_strcasecmp(hint, "software") == 0)) { |
| attempt_texture_framebuffer = SDL_FALSE; |
| } |
| } |
| |
| if (_this->is_dummy) { /* dummy driver never has GPU support, of course. */ |
| attempt_texture_framebuffer = SDL_FALSE; |
| } |
| |
| #if defined(__LINUX__) |
| /* On WSL, direct X11 is faster than using OpenGL for window framebuffers, so try to detect WSL and avoid texture framebuffer. */ |
| else if ((_this->CreateWindowFramebuffer != NULL) && (SDL_strcmp(_this->name, "x11") == 0)) { |
| struct stat sb; |
| if ((stat("/proc/sys/fs/binfmt_misc/WSLInterop", &sb) == 0) || (stat("/run/WSL", &sb) == 0)) { /* if either of these exist, we're on WSL. */ |
| attempt_texture_framebuffer = SDL_FALSE; |
| } |
| } |
| #endif |
| #if defined(__WIN32__) || defined(__WINGDK__) /* GDI BitBlt() is way faster than Direct3D dynamic textures right now. (!!! FIXME: is this still true?) */ |
| else if ((_this->CreateWindowFramebuffer != NULL) && (SDL_strcmp(_this->name, "windows") == 0)) { |
| attempt_texture_framebuffer = SDL_FALSE; |
| } |
| #endif |
| #if defined(__EMSCRIPTEN__) |
| else { |
| attempt_texture_framebuffer = SDL_FALSE; |
| } |
| #endif |
| |
| if (attempt_texture_framebuffer) { |
| if (SDL_CreateWindowTexture(_this, window, &format, &pixels, &pitch) == -1) { |
| /* !!! FIXME: if this failed halfway (made renderer, failed to make texture, etc), |
| !!! FIXME: we probably need to clean this up so it doesn't interfere with |
| !!! FIXME: a software fallback at the system level (can we blit to an |
| !!! FIXME: OpenGL window? etc). */ |
| } else { |
| /* future attempts will just try to use a texture framebuffer. */ |
| /* !!! FIXME: maybe we shouldn't override these but check if we used a texture |
| !!! FIXME: framebuffer at the right places; is it feasible we could have an |
| !!! FIXME: accelerated OpenGL window and a second ends up in software? */ |
| _this->CreateWindowFramebuffer = SDL_CreateWindowTexture; |
| _this->UpdateWindowFramebuffer = SDL_UpdateWindowTexture; |
| _this->DestroyWindowFramebuffer = SDL_DestroyWindowTexture; |
| created_framebuffer = SDL_TRUE; |
| } |
| } |
| |
| _this->checked_texture_framebuffer = SDL_TRUE; /* don't check this again. */ |
| } |
| |
| if (!created_framebuffer) { |
| if (!_this->CreateWindowFramebuffer || !_this->UpdateWindowFramebuffer) { |
| return NULL; |
| } |
| |
| if (_this->CreateWindowFramebuffer(_this, window, &format, &pixels, &pitch) < 0) { |
| return NULL; |
| } |
| } |
| |
| if (window->surface) { |
| return window->surface; |
| } |
| |
| if (!SDL_PixelFormatEnumToMasks(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) { |
| return NULL; |
| } |
| |
| return SDL_CreateRGBSurfaceFrom(pixels, w, h, bpp, pitch, Rmask, Gmask, Bmask, Amask); |
| } |
| |
| SDL_bool SDL_HasWindowSurface(SDL_Window *window) |
| { |
| CHECK_WINDOW_MAGIC(window, SDL_FALSE); |
| |
| return window->surface ? SDL_TRUE : SDL_FALSE; |
| } |
| |
| SDL_Surface *SDL_GetWindowSurface(SDL_Window *window) |
| { |
| CHECK_WINDOW_MAGIC(window, NULL); |
| |
| if (!window->surface_valid) { |
| SDL_DestroyWindowSurface(window); |
| 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; |
| SDL_GetWindowSizeInPixels(window, &full_rect.w, &full_rect.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"); |
| } |
| |
| SDL_assert(_this->checked_texture_framebuffer); /* we should have done this before we had a valid 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_DestroyWindowSurface(SDL_Window *window) |
| { |
| CHECK_WINDOW_MAGIC(window, -1); |
| |
| if (window->surface) { |
| window->surface->flags &= ~SDL_DONTFREE; |
| SDL_FreeSurface(window->surface); |
| window->surface = NULL; |
| window->surface_valid = SDL_FALSE; |
| } |
| return 0; |
| } |
| |
| 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_bool keyboard_grabbed, mouse_grabbed; |
| |
| if (window->flags & SDL_WINDOW_INPUT_FOCUS) { |
| if (SDL_GetMouse()->relative_mode || (window->flags & SDL_WINDOW_MOUSE_GRABBED)) { |
| mouse_grabbed = SDL_TRUE; |
| } else { |
| mouse_grabbed = SDL_FALSE; |
| } |
| |
| if (window->flags & SDL_WINDOW_KEYBOARD_GRABBED) { |
| keyboard_grabbed = SDL_TRUE; |
| } else { |
| keyboard_grabbed = SDL_FALSE; |
| } |
| } else { |
| mouse_grabbed = SDL_FALSE; |
| keyboard_grabbed = SDL_FALSE; |
| } |
| |
| if (mouse_grabbed || keyboard_grabbed) { |
| if (_this->grabbed_window && (_this->grabbed_window != window)) { |
| /* stealing a grab from another window! */ |
| _this->grabbed_window->flags &= ~(SDL_WINDOW_MOUSE_GRABBED | SDL_WINDOW_KEYBOARD_GRABBED); |
| if (_this->SetWindowMouseGrab) { |
| _this->SetWindowMouseGrab(_this, _this->grabbed_window, SDL_FALSE); |
| } |
| if (_this->SetWindowKeyboardGrab) { |
| _this->SetWindowKeyboardGrab(_this, _this->grabbed_window, SDL_FALSE); |
| } |
| } |
| _this->grabbed_window = window; |
| } else if (_this->grabbed_window == window) { |
| _this->grabbed_window = NULL; /* ungrabbing input. */ |
| } |
| |
| if (_this->SetWindowMouseGrab) { |
| _this->SetWindowMouseGrab(_this, window, mouse_grabbed); |
| } |
| if (_this->SetWindowKeyboardGrab) { |
| _this->SetWindowKeyboardGrab(_this, window, keyboard_grabbed); |
| } |
| } |
| |
| void SDL_SetWindowGrab(SDL_Window *window, SDL_bool grabbed) |
| { |
| CHECK_WINDOW_MAGIC(window, ); |
| |
| SDL_SetWindowMouseGrab(window, grabbed); |
| |
| if (SDL_GetHintBoolean(SDL_HINT_GRAB_KEYBOARD, SDL_FALSE)) { |
| SDL_SetWindowKeyboardGrab(window, grabbed); |
| } |
| } |
| |
| void SDL_SetWindowKeyboardGrab(SDL_Window *window, SDL_bool grabbed) |
| { |
| CHECK_WINDOW_MAGIC(window, ); |
| |
| if (!!grabbed == !!(window->flags & SDL_WINDOW_KEYBOARD_GRABBED)) { |
| return; |
| } |
| if (grabbed) { |
| window->flags |= SDL_WINDOW_KEYBOARD_GRABBED; |
| } else { |
| window->flags &= ~SDL_WINDOW_KEYBOARD_GRABBED; |
| } |
| SDL_UpdateWindowGrab(window); |
| } |
| |
| void SDL_SetWindowMouseGrab(SDL_Window *window, SDL_bool grabbed) |
| { |
| CHECK_WINDOW_MAGIC(window, ); |
| |
| if (!!grabbed == !!(window->flags & SDL_WINDOW_MOUSE_GRABBED)) { |
| return; |
| } |
| if (grabbed) { |
| window->flags |= SDL_WINDOW_MOUSE_GRABBED; |
| } else { |
| window->flags &= ~SDL_WINDOW_MOUSE_GRABBED; |
| } |
| SDL_UpdateWindowGrab(window); |
| } |
| |
| SDL_bool SDL_GetWindowGrab(SDL_Window *window) |
| { |
| return SDL_GetWindowKeyboardGrab(window) || SDL_GetWindowMouseGrab(window); |
| } |
| |
| SDL_bool SDL_GetWindowKeyboardGrab(SDL_Window *window) |
| { |
| CHECK_WINDOW_MAGIC(window |