2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
21 #include "../SDL_internal.h"
23 /* The high-level video driver subsystem */
26 #include "SDL_video.h"
27 #include "SDL_sysvideo.h"
29 #include "SDL_pixels_c.h"
30 #include "SDL_rect_c.h"
31 #include "../events/SDL_events_c.h"
32 #include "../timer/SDL_timer_c.h"
34 #include "SDL_syswm.h"
37 #include "SDL_opengl.h"
38 #endif /* SDL_VIDEO_OPENGL */
40 #if SDL_VIDEO_OPENGL_ES && !SDL_VIDEO_OPENGL
41 #include "SDL_opengles.h"
42 #endif /* SDL_VIDEO_OPENGL_ES && !SDL_VIDEO_OPENGL */
44 /* GL and GLES2 headers conflict on Linux 32 bits */
45 #if SDL_VIDEO_OPENGL_ES2 && !SDL_VIDEO_OPENGL
46 #include "SDL_opengles2.h"
47 #endif /* SDL_VIDEO_OPENGL_ES2 && !SDL_VIDEO_OPENGL */
50 #ifndef GL_CONTEXT_RELEASE_BEHAVIOR_KHR
51 #define GL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x82FB
56 #include <emscripten.h>
59 /* Available video drivers */
60 static VideoBootStrap *bootstrap[] = {
61 #if SDL_VIDEO_DRIVER_COCOA
64 #if SDL_VIDEO_DRIVER_X11
67 #if SDL_VIDEO_DRIVER_WAYLAND
70 #if SDL_VIDEO_DRIVER_VIVANTE
73 #if SDL_VIDEO_DRIVER_DIRECTFB
76 #if SDL_VIDEO_DRIVER_WINDOWS
79 #if SDL_VIDEO_DRIVER_WINRT
82 #if SDL_VIDEO_DRIVER_HAIKU
85 #if SDL_VIDEO_DRIVER_PANDORA
88 #if SDL_VIDEO_DRIVER_UIKIT
91 #if SDL_VIDEO_DRIVER_ANDROID
94 #if SDL_VIDEO_DRIVER_PSP
97 #if SDL_VIDEO_DRIVER_KMSDRM
99 &KMSDRM_LEGACY_bootstrap,
101 #if SDL_VIDEO_DRIVER_RPI
104 #if SDL_VIDEO_DRIVER_NACL
107 #if SDL_VIDEO_DRIVER_EMSCRIPTEN
108 &Emscripten_bootstrap,
110 #if SDL_VIDEO_DRIVER_QNX
113 #if SDL_VIDEO_DRIVER_OFFSCREEN
114 &OFFSCREEN_bootstrap,
116 #if SDL_VIDEO_DRIVER_OS2
120 #if SDL_VIDEO_DRIVER_DUMMY
126 static SDL_VideoDevice *_this = NULL;
128 #define CHECK_WINDOW_MAGIC(window, retval) \
130 SDL_UninitializedVideo(); \
133 SDL_assert(window && window->magic == &_this->window_magic); \
134 if (!window || window->magic != &_this->window_magic) { \
135 SDL_SetError("Invalid window"); \
139 #define CHECK_DISPLAY_INDEX(displayIndex, retval) \
141 SDL_UninitializedVideo(); \
144 SDL_assert(_this->displays != NULL); \
145 SDL_assert(displayIndex >= 0 && displayIndex < _this->num_displays); \
146 if (displayIndex < 0 || displayIndex >= _this->num_displays) { \
147 SDL_SetError("displayIndex must be in the range 0 - %d", \
148 _this->num_displays - 1); \
152 #define FULLSCREEN_MASK (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN)
155 /* Support for Mac OS X fullscreen spaces */
156 extern SDL_bool Cocoa_IsWindowInFullscreenSpace(SDL_Window * window);
157 extern SDL_bool Cocoa_SetWindowFullscreenSpace(SDL_Window * window, SDL_bool state);
161 /* Support for framebuffer emulation using an accelerated renderer */
163 #define SDL_WINDOWTEXTUREDATA "_SDL_WindowTextureData"
166 SDL_Renderer *renderer;
167 SDL_Texture *texture;
171 } SDL_WindowTextureData;
174 ShouldUseTextureFramebuffer()
178 /* If there's no native framebuffer support then there's no option */
179 if (!_this->CreateWindowFramebuffer) {
183 /* If this is the dummy driver there is no texture support */
184 if (_this->is_dummy) {
188 /* If the user has specified a software renderer we can't use a
189 texture framebuffer, or renderer creation will go recursive.
191 hint = SDL_GetHint(SDL_HINT_RENDER_DRIVER);
192 if (hint && SDL_strcasecmp(hint, "software") == 0) {
196 /* See if the user or application wants a specific behavior */
197 hint = SDL_GetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION);
199 if (*hint == '0' || SDL_strcasecmp(hint, "false") == 0) {
206 /* Each platform has different performance characteristics */
207 #if defined(__WIN32__)
208 /* GDI BitBlt() is way faster than Direct3D dynamic textures right now.
212 #elif defined(__MACOSX__)
213 /* Mac OS X uses OpenGL as the native fast path (for cocoa and X11) */
216 #elif defined(__LINUX__)
217 /* Properly configured OpenGL drivers are faster than MIT-SHM */
219 /* Ugh, find a way to cache this value! */
222 SDL_GLContext context;
223 SDL_bool hasAcceleratedOpenGL = SDL_FALSE;
225 window = SDL_CreateWindow("OpenGL test", -32, -32, 32, 32, SDL_WINDOW_OPENGL|SDL_WINDOW_HIDDEN);
227 context = SDL_GL_CreateContext(window);
229 const GLubyte *(APIENTRY * glGetStringFunc) (GLenum);
230 const char *vendor = NULL;
232 glGetStringFunc = SDL_GL_GetProcAddress("glGetString");
233 if (glGetStringFunc) {
234 vendor = (const char *) glGetStringFunc(GL_VENDOR);
236 /* Add more vendors here at will... */
238 (SDL_strstr(vendor, "ATI Technologies") ||
239 SDL_strstr(vendor, "NVIDIA"))) {
240 hasAcceleratedOpenGL = SDL_TRUE;
242 SDL_GL_DeleteContext(context);
244 SDL_DestroyWindow(window);
246 return hasAcceleratedOpenGL;
248 #elif SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
249 /* Let's be optimistic about this! */
256 /* Play it safe, assume that if there is a framebuffer driver that it's
257 optimized for the current platform.
264 SDL_CreateWindowTexture(SDL_VideoDevice *unused, SDL_Window * window, Uint32 * format, void ** pixels, int *pitch)
266 SDL_WindowTextureData *data;
268 data = SDL_GetWindowData(window, SDL_WINDOWTEXTUREDATA);
270 SDL_Renderer *renderer = NULL;
272 const char *hint = SDL_GetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION);
274 /* Check to see if there's a specific driver requested */
275 if (hint && *hint != '0' && *hint != '1' &&
276 SDL_strcasecmp(hint, "true") != 0 &&
277 SDL_strcasecmp(hint, "false") != 0 &&
278 SDL_strcasecmp(hint, "software") != 0) {
279 for (i = 0; i < SDL_GetNumRenderDrivers(); ++i) {
280 SDL_RendererInfo info;
281 SDL_GetRenderDriverInfo(i, &info);
282 if (SDL_strcasecmp(info.name, hint) == 0) {
283 renderer = SDL_CreateRenderer(window, i, 0);
290 for (i = 0; i < SDL_GetNumRenderDrivers(); ++i) {
291 SDL_RendererInfo info;
292 SDL_GetRenderDriverInfo(i, &info);
293 if (SDL_strcmp(info.name, "software") != 0) {
294 renderer = SDL_CreateRenderer(window, i, 0);
302 return SDL_SetError("No hardware accelerated renderers available");
305 /* Create the data after we successfully create the renderer (bug #1116) */
306 data = (SDL_WindowTextureData *)SDL_calloc(1, sizeof(*data));
308 SDL_DestroyRenderer(renderer);
309 return SDL_OutOfMemory();
311 SDL_SetWindowData(window, SDL_WINDOWTEXTUREDATA, data);
313 data->renderer = renderer;
316 /* Free any old texture and pixel data */
318 SDL_DestroyTexture(data->texture);
319 data->texture = NULL;
321 SDL_free(data->pixels);
325 SDL_RendererInfo info;
328 if (SDL_GetRendererInfo(data->renderer, &info) < 0) {
332 /* Find the first format without an alpha channel */
333 *format = info.texture_formats[0];
335 for (i = 0; i < info.num_texture_formats; ++i) {
336 if (!SDL_ISPIXELFORMAT_FOURCC(info.texture_formats[i]) &&
337 !SDL_ISPIXELFORMAT_ALPHA(info.texture_formats[i])) {
338 *format = info.texture_formats[i];
344 data->texture = SDL_CreateTexture(data->renderer, *format,
345 SDL_TEXTUREACCESS_STREAMING,
346 window->w, window->h);
347 if (!data->texture) {
351 /* Create framebuffer data */
352 data->bytes_per_pixel = SDL_BYTESPERPIXEL(*format);
353 data->pitch = (((window->w * data->bytes_per_pixel) + 3) & ~3);
356 /* Make static analysis happy about potential malloc(0) calls. */
357 const size_t allocsize = window->h * data->pitch;
358 data->pixels = SDL_malloc((allocsize > 0) ? allocsize : 1);
360 return SDL_OutOfMemory();
364 *pixels = data->pixels;
365 *pitch = data->pitch;
367 /* Make sure we're not double-scaling the viewport */
368 SDL_RenderSetViewport(data->renderer, NULL);
374 SDL_UpdateWindowTexture(SDL_VideoDevice *unused, SDL_Window * window, const SDL_Rect * rects, int numrects)
376 SDL_WindowTextureData *data;
380 data = SDL_GetWindowData(window, SDL_WINDOWTEXTUREDATA);
381 if (!data || !data->texture) {
382 return SDL_SetError("No window texture data");
385 /* Update a single rect that contains subrects for best DMA performance */
386 if (SDL_GetSpanEnclosingRect(window->w, window->h, numrects, rects, &rect)) {
387 src = (void *)((Uint8 *)data->pixels +
388 rect.y * data->pitch +
389 rect.x * data->bytes_per_pixel);
390 if (SDL_UpdateTexture(data->texture, &rect, src, data->pitch) < 0) {
394 if (SDL_RenderCopy(data->renderer, data->texture, NULL, NULL) < 0) {
398 SDL_RenderPresent(data->renderer);
404 SDL_DestroyWindowTexture(SDL_VideoDevice *unused, SDL_Window * window)
406 SDL_WindowTextureData *data;
408 data = SDL_SetWindowData(window, SDL_WINDOWTEXTUREDATA, NULL);
413 SDL_DestroyTexture(data->texture);
415 if (data->renderer) {
416 SDL_DestroyRenderer(data->renderer);
418 SDL_free(data->pixels);
424 cmpmodes(const void *A, const void *B)
426 const SDL_DisplayMode *a = (const SDL_DisplayMode *) A;
427 const SDL_DisplayMode *b = (const SDL_DisplayMode *) B;
430 } else if (a->w != b->w) {
432 } else if (a->h != b->h) {
434 } else if (SDL_BITSPERPIXEL(a->format) != SDL_BITSPERPIXEL(b->format)) {
435 return SDL_BITSPERPIXEL(b->format) - SDL_BITSPERPIXEL(a->format);
436 } else if (SDL_PIXELLAYOUT(a->format) != SDL_PIXELLAYOUT(b->format)) {
437 return SDL_PIXELLAYOUT(b->format) - SDL_PIXELLAYOUT(a->format);
438 } else if (a->refresh_rate != b->refresh_rate) {
439 return b->refresh_rate - a->refresh_rate;
445 SDL_UninitializedVideo()
447 return SDL_SetError("Video subsystem has not been initialized");
451 SDL_GetNumVideoDrivers(void)
453 return SDL_arraysize(bootstrap) - 1;
457 SDL_GetVideoDriver(int index)
459 if (index >= 0 && index < SDL_GetNumVideoDrivers()) {
460 return bootstrap[index]->name;
466 * Initialize the video and event subsystems -- determine native pixel format
469 SDL_VideoInit(const char *driver_name)
471 SDL_VideoDevice *video;
475 /* Check to make sure we don't overwrite '_this' */
480 #if !SDL_TIMERS_DISABLED
484 /* Start the event loop */
485 if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0 ||
486 SDL_KeyboardInit() < 0 ||
487 SDL_MouseInit() < 0 ||
488 SDL_TouchInit() < 0) {
492 /* Select the proper video driver */
495 if (driver_name == NULL) {
496 driver_name = SDL_getenv("SDL_VIDEODRIVER");
498 if (driver_name != NULL) {
499 for (i = 0; bootstrap[i]; ++i) {
500 if (SDL_strncasecmp(bootstrap[i]->name, driver_name, SDL_strlen(driver_name)) == 0) {
501 video = bootstrap[i]->create(index);
506 for (i = 0; bootstrap[i]; ++i) {
507 video = bootstrap[i]->create(index);
515 return SDL_SetError("%s not available", driver_name);
517 return SDL_SetError("No available video device");
520 _this->name = bootstrap[i]->name;
521 _this->next_object_id = 1;
524 /* Set some very sane GL defaults */
525 _this->gl_config.driver_loaded = 0;
526 _this->gl_config.dll_handle = NULL;
527 SDL_GL_ResetAttributes();
529 _this->current_glwin_tls = SDL_TLSCreate();
530 _this->current_glctx_tls = SDL_TLSCreate();
532 /* Initialize the video subsystem */
533 if (_this->VideoInit(_this) < 0) {
538 /* Make sure some displays were added */
539 if (_this->num_displays == 0) {
541 return SDL_SetError("The video driver did not add any displays");
544 /* Add the renderer framebuffer emulation if desired */
545 if (ShouldUseTextureFramebuffer()) {
546 _this->CreateWindowFramebuffer = SDL_CreateWindowTexture;
547 _this->UpdateWindowFramebuffer = SDL_UpdateWindowTexture;
548 _this->DestroyWindowFramebuffer = SDL_DestroyWindowTexture;
551 /* Disable the screen saver by default. This is a change from <= 2.0.1,
552 but most things using SDL are games or media players; you wouldn't
553 want a screensaver to trigger if you're playing exclusively with a
554 joystick, or passively watching a movie. Things that use SDL but
555 function more like a normal desktop app should explicitly reenable the
557 if (!SDL_GetHintBoolean(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, SDL_FALSE)) {
558 SDL_DisableScreenSaver();
561 /* If we don't use a screen keyboard, turn on text input by default,
562 otherwise programs that expect to get text events without enabling
563 UNICODE input won't get any events.
565 Actually, come to think of it, you needed to call SDL_EnableUNICODE(1)
566 in SDL 1.2 before you got text input events. Hmm...
568 if (!SDL_HasScreenKeyboardSupport()) {
569 SDL_StartTextInput();
572 /* We're ready to go! */
577 SDL_GetCurrentVideoDriver()
580 SDL_UninitializedVideo();
587 SDL_GetVideoDevice(void)
593 SDL_AddBasicVideoDisplay(const SDL_DisplayMode * desktop_mode)
595 SDL_VideoDisplay display;
599 display.desktop_mode = *desktop_mode;
601 display.current_mode = display.desktop_mode;
603 return SDL_AddVideoDisplay(&display, SDL_FALSE);
607 SDL_AddVideoDisplay(const SDL_VideoDisplay * display, SDL_bool send_event)
609 SDL_VideoDisplay *displays;
613 SDL_realloc(_this->displays,
614 (_this->num_displays + 1) * sizeof(*displays));
616 index = _this->num_displays++;
617 displays[index] = *display;
618 displays[index].device = _this;
619 _this->displays = displays;
622 displays[index].name = SDL_strdup(display->name);
626 SDL_itoa(index, name, 10);
627 displays[index].name = SDL_strdup(name);
631 SDL_SendDisplayEvent(&_this->displays[index], SDL_DISPLAYEVENT_CONNECTED, 0);
640 SDL_DelVideoDisplay(int index)
642 if (index < 0 || index >= _this->num_displays) {
646 SDL_SendDisplayEvent(&_this->displays[index], SDL_DISPLAYEVENT_DISCONNECTED, 0);
648 if (index < (_this->num_displays - 1)) {
649 SDL_memmove(&_this->displays[index], &_this->displays[index+1], (_this->num_displays - index - 1)*sizeof(_this->displays[index]));
651 --_this->num_displays;
655 SDL_GetNumVideoDisplays(void)
658 SDL_UninitializedVideo();
661 return _this->num_displays;
665 SDL_GetIndexOfDisplay(SDL_VideoDisplay *display)
669 for (displayIndex = 0; displayIndex < _this->num_displays; ++displayIndex) {
670 if (display == &_this->displays[displayIndex]) {
675 /* Couldn't find the display, just use index 0 */
680 SDL_GetDisplayDriverData(int displayIndex)
682 CHECK_DISPLAY_INDEX(displayIndex, NULL);
684 return _this->displays[displayIndex].driverdata;
688 SDL_IsVideoContextExternal(void)
690 return SDL_GetHintBoolean(SDL_HINT_VIDEO_EXTERNAL_CONTEXT, SDL_FALSE);
694 SDL_GetDisplayName(int displayIndex)
696 CHECK_DISPLAY_INDEX(displayIndex, NULL);
698 return _this->displays[displayIndex].name;
702 SDL_GetDisplayBounds(int displayIndex, SDL_Rect * rect)
704 CHECK_DISPLAY_INDEX(displayIndex, -1);
707 SDL_VideoDisplay *display = &_this->displays[displayIndex];
709 if (_this->GetDisplayBounds) {
710 if (_this->GetDisplayBounds(_this, display, rect) == 0) {
715 /* Assume that the displays are left to right */
716 if (displayIndex == 0) {
720 SDL_GetDisplayBounds(displayIndex-1, rect);
723 rect->w = display->current_mode.w;
724 rect->h = display->current_mode.h;
726 return 0; /* !!! FIXME: should this be an error if (rect==NULL) ? */
730 ParseDisplayUsableBoundsHint(SDL_Rect *rect)
732 const char *hint = SDL_GetHint(SDL_HINT_DISPLAY_USABLE_BOUNDS);
733 return hint && (SDL_sscanf(hint, "%d,%d,%d,%d", &rect->x, &rect->y, &rect->w, &rect->h) == 4);
737 SDL_GetDisplayUsableBounds(int displayIndex, SDL_Rect * rect)
739 CHECK_DISPLAY_INDEX(displayIndex, -1);
742 SDL_VideoDisplay *display = &_this->displays[displayIndex];
744 if ((displayIndex == 0) && ParseDisplayUsableBoundsHint(rect)) {
748 if (_this->GetDisplayUsableBounds) {
749 if (_this->GetDisplayUsableBounds(_this, display, rect) == 0) {
754 /* Oh well, just give the entire display bounds. */
755 return SDL_GetDisplayBounds(displayIndex, rect);
757 return 0; /* !!! FIXME: should this be an error if (rect==NULL) ? */
761 SDL_GetDisplayDPI(int displayIndex, float * ddpi, float * hdpi, float * vdpi)
763 SDL_VideoDisplay *display;
765 CHECK_DISPLAY_INDEX(displayIndex, -1);
767 display = &_this->displays[displayIndex];
769 if (_this->GetDisplayDPI) {
770 if (_this->GetDisplayDPI(_this, display, ddpi, hdpi, vdpi) == 0) {
774 return SDL_Unsupported();
780 SDL_DisplayOrientation
781 SDL_GetDisplayOrientation(int displayIndex)
783 SDL_VideoDisplay *display;
785 CHECK_DISPLAY_INDEX(displayIndex, SDL_ORIENTATION_UNKNOWN);
787 display = &_this->displays[displayIndex];
788 return display->orientation;
792 SDL_AddDisplayMode(SDL_VideoDisplay * display, const SDL_DisplayMode * mode)
794 SDL_DisplayMode *modes;
797 /* Make sure we don't already have the mode in the list */
798 modes = display->display_modes;
799 nmodes = display->num_display_modes;
800 for (i = 0; i < nmodes; ++i) {
801 if (cmpmodes(mode, &modes[i]) == 0) {
806 /* Go ahead and add the new mode */
807 if (nmodes == display->max_display_modes) {
810 (display->max_display_modes + 32) * sizeof(*modes));
814 display->display_modes = modes;
815 display->max_display_modes += 32;
817 modes[nmodes] = *mode;
818 display->num_display_modes++;
820 /* Re-sort video modes */
821 SDL_qsort(display->display_modes, display->num_display_modes,
822 sizeof(SDL_DisplayMode), cmpmodes);
828 SDL_GetNumDisplayModesForDisplay(SDL_VideoDisplay * display)
830 if (!display->num_display_modes && _this->GetDisplayModes) {
831 _this->GetDisplayModes(_this, display);
832 SDL_qsort(display->display_modes, display->num_display_modes,
833 sizeof(SDL_DisplayMode), cmpmodes);
835 return display->num_display_modes;
839 SDL_GetNumDisplayModes(int displayIndex)
841 CHECK_DISPLAY_INDEX(displayIndex, -1);
843 return SDL_GetNumDisplayModesForDisplay(&_this->displays[displayIndex]);
847 SDL_GetDisplayMode(int displayIndex, int index, SDL_DisplayMode * mode)
849 SDL_VideoDisplay *display;
851 CHECK_DISPLAY_INDEX(displayIndex, -1);
853 display = &_this->displays[displayIndex];
854 if (index < 0 || index >= SDL_GetNumDisplayModesForDisplay(display)) {
855 return SDL_SetError("index must be in the range of 0 - %d",
856 SDL_GetNumDisplayModesForDisplay(display) - 1);
859 *mode = display->display_modes[index];
865 SDL_GetDesktopDisplayMode(int displayIndex, SDL_DisplayMode * mode)
867 SDL_VideoDisplay *display;
869 CHECK_DISPLAY_INDEX(displayIndex, -1);
871 display = &_this->displays[displayIndex];
873 *mode = display->desktop_mode;
879 SDL_GetCurrentDisplayMode(int displayIndex, SDL_DisplayMode * mode)
881 SDL_VideoDisplay *display;
883 CHECK_DISPLAY_INDEX(displayIndex, -1);
885 display = &_this->displays[displayIndex];
887 *mode = display->current_mode;
892 static SDL_DisplayMode *
893 SDL_GetClosestDisplayModeForDisplay(SDL_VideoDisplay * display,
894 const SDL_DisplayMode * mode,
895 SDL_DisplayMode * closest)
897 Uint32 target_format;
898 int target_refresh_rate;
900 SDL_DisplayMode *current, *match;
902 if (!mode || !closest) {
903 SDL_SetError("Missing desired mode or closest mode parameter");
907 /* Default to the desktop format */
909 target_format = mode->format;
911 target_format = display->desktop_mode.format;
914 /* Default to the desktop refresh rate */
915 if (mode->refresh_rate) {
916 target_refresh_rate = mode->refresh_rate;
918 target_refresh_rate = display->desktop_mode.refresh_rate;
922 for (i = 0; i < SDL_GetNumDisplayModesForDisplay(display); ++i) {
923 current = &display->display_modes[i];
925 if (current->w && (current->w < mode->w)) {
926 /* Out of sorted modes large enough here */
929 if (current->h && (current->h < mode->h)) {
930 if (current->w && (current->w == mode->w)) {
931 /* Out of sorted modes large enough here */
934 /* Wider, but not tall enough, due to a different
935 aspect ratio. This mode must be skipped, but closer
936 modes may still follow. */
939 if (!match || current->w < match->w || current->h < match->h) {
943 if (current->format != match->format) {
944 /* Sorted highest depth to lowest */
945 if (current->format == target_format ||
946 (SDL_BITSPERPIXEL(current->format) >=
947 SDL_BITSPERPIXEL(target_format)
948 && SDL_PIXELTYPE(current->format) ==
949 SDL_PIXELTYPE(target_format))) {
954 if (current->refresh_rate != match->refresh_rate) {
955 /* Sorted highest refresh to lowest */
956 if (current->refresh_rate >= target_refresh_rate) {
963 closest->format = match->format;
965 closest->format = mode->format;
967 if (match->w && match->h) {
968 closest->w = match->w;
969 closest->h = match->h;
971 closest->w = mode->w;
972 closest->h = mode->h;
974 if (match->refresh_rate) {
975 closest->refresh_rate = match->refresh_rate;
977 closest->refresh_rate = mode->refresh_rate;
979 closest->driverdata = match->driverdata;
982 * Pick some reasonable defaults if the app and driver don't
985 if (!closest->format) {
986 closest->format = SDL_PIXELFORMAT_RGB888;
1000 SDL_GetClosestDisplayMode(int displayIndex,
1001 const SDL_DisplayMode * mode,
1002 SDL_DisplayMode * closest)
1004 SDL_VideoDisplay *display;
1006 CHECK_DISPLAY_INDEX(displayIndex, NULL);
1008 display = &_this->displays[displayIndex];
1009 return SDL_GetClosestDisplayModeForDisplay(display, mode, closest);
1013 SDL_SetDisplayModeForDisplay(SDL_VideoDisplay * display, const SDL_DisplayMode * mode)
1015 SDL_DisplayMode display_mode;
1016 SDL_DisplayMode current_mode;
1019 display_mode = *mode;
1021 /* Default to the current mode */
1022 if (!display_mode.format) {
1023 display_mode.format = display->current_mode.format;
1025 if (!display_mode.w) {
1026 display_mode.w = display->current_mode.w;
1028 if (!display_mode.h) {
1029 display_mode.h = display->current_mode.h;
1031 if (!display_mode.refresh_rate) {
1032 display_mode.refresh_rate = display->current_mode.refresh_rate;
1035 /* Get a good video mode, the closest one possible */
1036 if (!SDL_GetClosestDisplayModeForDisplay(display, &display_mode, &display_mode)) {
1037 return SDL_SetError("No video mode large enough for %dx%d",
1038 display_mode.w, display_mode.h);
1041 display_mode = display->desktop_mode;
1044 /* See if there's anything left to do */
1045 current_mode = display->current_mode;
1046 if (SDL_memcmp(&display_mode, ¤t_mode, sizeof(display_mode)) == 0) {
1050 /* Actually change the display mode */
1051 if (!_this->SetDisplayMode) {
1052 return SDL_SetError("SDL video driver doesn't support changing display mode");
1054 if (_this->SetDisplayMode(_this, display, &display_mode) < 0) {
1057 display->current_mode = display_mode;
1062 SDL_GetDisplay(int displayIndex)
1064 CHECK_DISPLAY_INDEX(displayIndex, NULL);
1066 return &_this->displays[displayIndex];
1070 SDL_GetWindowDisplayIndex(SDL_Window * window)
1075 int closest_dist = 0x7FFFFFFF;
1080 CHECK_WINDOW_MAGIC(window, -1);
1082 if (SDL_WINDOWPOS_ISUNDEFINED(window->x) ||
1083 SDL_WINDOWPOS_ISCENTERED(window->x)) {
1084 displayIndex = (window->x & 0xFFFF);
1085 if (displayIndex >= _this->num_displays) {
1088 return displayIndex;
1090 if (SDL_WINDOWPOS_ISUNDEFINED(window->y) ||
1091 SDL_WINDOWPOS_ISCENTERED(window->y)) {
1092 displayIndex = (window->y & 0xFFFF);
1093 if (displayIndex >= _this->num_displays) {
1096 return displayIndex;
1099 /* Find the display containing the window */
1100 for (i = 0; i < _this->num_displays; ++i) {
1101 SDL_VideoDisplay *display = &_this->displays[i];
1103 if (display->fullscreen_window == window) {
1107 center.x = window->x + window->w / 2;
1108 center.y = window->y + window->h / 2;
1109 for (i = 0; i < _this->num_displays; ++i) {
1110 SDL_GetDisplayBounds(i, &rect);
1111 if (SDL_EnclosePoints(¢er, 1, &rect, NULL)) {
1115 delta.x = center.x - (rect.x + rect.w / 2);
1116 delta.y = center.y - (rect.y + rect.h / 2);
1117 dist = (delta.x*delta.x + delta.y*delta.y);
1118 if (dist < closest_dist) {
1120 closest_dist = dist;
1124 SDL_SetError("Couldn't find any displays");
1130 SDL_GetDisplayForWindow(SDL_Window *window)
1132 int displayIndex = SDL_GetWindowDisplayIndex(window);
1133 if (displayIndex >= 0) {
1134 return &_this->displays[displayIndex];
1141 SDL_SetWindowDisplayMode(SDL_Window * window, const SDL_DisplayMode * mode)
1143 CHECK_WINDOW_MAGIC(window, -1);
1146 window->fullscreen_mode = *mode;
1148 SDL_zero(window->fullscreen_mode);
1151 if (FULLSCREEN_VISIBLE(window) && (window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP) {
1152 SDL_DisplayMode fullscreen_mode;
1153 if (SDL_GetWindowDisplayMode(window, &fullscreen_mode) == 0) {
1154 SDL_SetDisplayModeForDisplay(SDL_GetDisplayForWindow(window), &fullscreen_mode);
1161 SDL_GetWindowDisplayMode(SDL_Window * window, SDL_DisplayMode * mode)
1163 SDL_DisplayMode fullscreen_mode;
1164 SDL_VideoDisplay *display;
1166 CHECK_WINDOW_MAGIC(window, -1);
1169 return SDL_InvalidParamError("mode");
1172 fullscreen_mode = window->fullscreen_mode;
1173 if (!fullscreen_mode.w) {
1174 fullscreen_mode.w = window->windowed.w;
1176 if (!fullscreen_mode.h) {
1177 fullscreen_mode.h = window->windowed.h;
1180 display = SDL_GetDisplayForWindow(window);
1182 /* if in desktop size mode, just return the size of the desktop */
1183 if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
1184 fullscreen_mode = display->desktop_mode;
1185 } else if (!SDL_GetClosestDisplayModeForDisplay(SDL_GetDisplayForWindow(window),
1187 &fullscreen_mode)) {
1188 return SDL_SetError("Couldn't find display mode match");
1192 *mode = fullscreen_mode;
1198 SDL_GetWindowPixelFormat(SDL_Window * window)
1200 SDL_VideoDisplay *display;
1202 CHECK_WINDOW_MAGIC(window, SDL_PIXELFORMAT_UNKNOWN);
1204 display = SDL_GetDisplayForWindow(window);
1205 return display->current_mode.format;
1209 SDL_RestoreMousePosition(SDL_Window *window)
1213 if (window == SDL_GetMouseFocus()) {
1214 SDL_GetMouseState(&x, &y);
1215 SDL_WarpMouseInWindow(window, x, y);
1220 extern Uint32 WINRT_DetectWindowFlags(SDL_Window * window);
1224 SDL_UpdateFullscreenMode(SDL_Window * window, SDL_bool fullscreen)
1226 SDL_VideoDisplay *display;
1229 CHECK_WINDOW_MAGIC(window,-1);
1231 /* if we are in the process of hiding don't go back to fullscreen */
1232 if (window->is_hiding && fullscreen) {
1237 /* if the window is going away and no resolution change is necessary,
1238 do nothing, or else we may trigger an ugly double-transition
1240 if (SDL_strcmp(_this->name, "cocoa") == 0) { /* don't do this for X11, etc */
1241 if (window->is_destroying && (window->last_fullscreen_flags & FULLSCREEN_MASK) == SDL_WINDOW_FULLSCREEN_DESKTOP)
1244 /* If we're switching between a fullscreen Space and "normal" fullscreen, we need to get back to normal first. */
1245 if (fullscreen && ((window->last_fullscreen_flags & FULLSCREEN_MASK) == SDL_WINDOW_FULLSCREEN_DESKTOP) && ((window->flags & FULLSCREEN_MASK) == SDL_WINDOW_FULLSCREEN)) {
1246 if (!Cocoa_SetWindowFullscreenSpace(window, SDL_FALSE)) {
1249 } else if (fullscreen && ((window->last_fullscreen_flags & FULLSCREEN_MASK) == SDL_WINDOW_FULLSCREEN) && ((window->flags & FULLSCREEN_MASK) == SDL_WINDOW_FULLSCREEN_DESKTOP)) {
1250 display = SDL_GetDisplayForWindow(window);
1251 SDL_SetDisplayModeForDisplay(display, NULL);
1252 if (_this->SetWindowFullscreen) {
1253 _this->SetWindowFullscreen(_this, window, display, SDL_FALSE);
1257 if (Cocoa_SetWindowFullscreenSpace(window, fullscreen)) {
1258 if (Cocoa_IsWindowInFullscreenSpace(window) != fullscreen) {
1261 window->last_fullscreen_flags = window->flags;
1265 #elif __WINRT__ && (NTDDI_VERSION < NTDDI_WIN10)
1266 /* HACK: WinRT 8.x apps can't choose whether or not they are fullscreen
1267 or not. The user can choose this, via OS-provided UI, but this can't
1268 be set programmatically.
1270 Just look at what SDL's WinRT video backend code detected with regards
1271 to fullscreen (being active, or not), and figure out a return/error code
1274 if (fullscreen == !(WINRT_DetectWindowFlags(window) & FULLSCREEN_MASK)) {
1276 1. fullscreen was requested, and we're already windowed
1277 2. windowed-mode was requested, and we're already fullscreen
1279 WinRT 8.x can't resolve either programmatically, so we're
1284 /* Whatever was requested, fullscreen or windowed mode, is already
1291 display = SDL_GetDisplayForWindow(window);
1294 /* Hide any other fullscreen windows */
1295 if (display->fullscreen_window &&
1296 display->fullscreen_window != window) {
1297 SDL_MinimizeWindow(display->fullscreen_window);
1301 /* See if anything needs to be done now */
1302 if ((display->fullscreen_window == window) == fullscreen) {
1303 if ((window->last_fullscreen_flags & FULLSCREEN_MASK) == (window->flags & FULLSCREEN_MASK)) {
1308 /* See if there are any fullscreen windows */
1309 for (other = _this->windows; other; other = other->next) {
1310 SDL_bool setDisplayMode = SDL_FALSE;
1312 if (other == window) {
1313 setDisplayMode = fullscreen;
1314 } else if (FULLSCREEN_VISIBLE(other) &&
1315 SDL_GetDisplayForWindow(other) == display) {
1316 setDisplayMode = SDL_TRUE;
1319 if (setDisplayMode) {
1320 SDL_DisplayMode fullscreen_mode;
1322 SDL_zero(fullscreen_mode);
1324 if (SDL_GetWindowDisplayMode(other, &fullscreen_mode) == 0) {
1325 SDL_bool resized = SDL_TRUE;
1327 if (other->w == fullscreen_mode.w && other->h == fullscreen_mode.h) {
1328 resized = SDL_FALSE;
1331 /* only do the mode change if we want exclusive fullscreen */
1332 if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP) {
1333 if (SDL_SetDisplayModeForDisplay(display, &fullscreen_mode) < 0) {
1337 if (SDL_SetDisplayModeForDisplay(display, NULL) < 0) {
1342 if (_this->SetWindowFullscreen) {
1343 _this->SetWindowFullscreen(_this, other, display, SDL_TRUE);
1345 display->fullscreen_window = other;
1347 /* Generate a mode change event here */
1350 // Android may not resize the window to exactly what our fullscreen mode is, especially on
1351 // windowed Android environments like the Chromebook or Samsung DeX. Given this, we shouldn't
1352 // use fullscreen_mode.w and fullscreen_mode.h, but rather get our current native size. As such,
1353 // Android's SetWindowFullscreen will generate the window event for us with the proper final size.
1355 SDL_SendWindowEvent(other, SDL_WINDOWEVENT_RESIZED,
1356 fullscreen_mode.w, fullscreen_mode.h);
1359 SDL_OnWindowResized(other);
1362 SDL_RestoreMousePosition(other);
1364 window->last_fullscreen_flags = window->flags;
1370 /* Nope, restore the desktop mode */
1371 SDL_SetDisplayModeForDisplay(display, NULL);
1373 if (_this->SetWindowFullscreen) {
1374 _this->SetWindowFullscreen(_this, window, display, SDL_FALSE);
1376 display->fullscreen_window = NULL;
1378 /* Generate a mode change event here */
1379 SDL_OnWindowResized(window);
1381 /* Restore the cursor position */
1382 SDL_RestoreMousePosition(window);
1384 window->last_fullscreen_flags = window->flags;
1388 #define CREATE_FLAGS \
1389 (SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_SKIP_TASKBAR | SDL_WINDOW_POPUP_MENU | SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_VULKAN | SDL_WINDOW_MINIMIZED | SDL_WINDOW_METAL)
1391 static SDL_INLINE SDL_bool
1392 IsAcceptingDragAndDrop(void)
1394 if ((SDL_GetEventState(SDL_DROPFILE) == SDL_ENABLE) ||
1395 (SDL_GetEventState(SDL_DROPTEXT) == SDL_ENABLE)) {
1401 /* prepare a newly-created window */
1402 static SDL_INLINE void
1403 PrepareDragAndDropSupport(SDL_Window *window)
1405 if (_this->AcceptDragAndDrop) {
1406 _this->AcceptDragAndDrop(window, IsAcceptingDragAndDrop());
1410 /* toggle d'n'd for all existing windows. */
1412 SDL_ToggleDragAndDropSupport(void)
1414 if (_this && _this->AcceptDragAndDrop) {
1415 const SDL_bool enable = IsAcceptingDragAndDrop();
1417 for (window = _this->windows; window; window = window->next) {
1418 _this->AcceptDragAndDrop(window, enable);
1424 SDL_FinishWindowCreation(SDL_Window *window, Uint32 flags)
1426 PrepareDragAndDropSupport(window);
1428 if (flags & SDL_WINDOW_MAXIMIZED) {
1429 SDL_MaximizeWindow(window);
1431 if (flags & SDL_WINDOW_MINIMIZED) {
1432 SDL_MinimizeWindow(window);
1434 if (flags & SDL_WINDOW_FULLSCREEN) {
1435 SDL_SetWindowFullscreen(window, flags);
1437 if (flags & SDL_WINDOW_INPUT_GRABBED) {
1438 SDL_SetWindowGrab(window, SDL_TRUE);
1440 if (!(flags & SDL_WINDOW_HIDDEN)) {
1441 SDL_ShowWindow(window);
1446 SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags)
1451 /* Initialize the video system if needed */
1452 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
1457 if ((((flags & SDL_WINDOW_UTILITY) != 0) + ((flags & SDL_WINDOW_TOOLTIP) != 0) + ((flags & SDL_WINDOW_POPUP_MENU) != 0)) > 1) {
1458 SDL_SetError("Conflicting window flags specified");
1462 /* Some platforms can't create zero-sized windows */
1470 /* Some platforms blow up if the windows are too large. Raise it later? */
1471 if ((w > 16384) || (h > 16384)) {
1472 SDL_SetError("Window is too large.");
1476 /* Some platforms have OpenGL enabled by default */
1477 #if (SDL_VIDEO_OPENGL && __MACOSX__) || __IPHONEOS__ || __ANDROID__ || __NACL__
1478 if (!_this->is_dummy && !(flags & SDL_WINDOW_VULKAN) && !(flags & SDL_WINDOW_METAL) && !SDL_IsVideoContextExternal()) {
1479 flags |= SDL_WINDOW_OPENGL;
1482 if (flags & SDL_WINDOW_OPENGL) {
1483 if (!_this->GL_CreateContext) {
1484 SDL_SetError("OpenGL support is either not configured in SDL "
1485 "or not available in current SDL video driver "
1486 "(%s) or platform", _this->name);
1489 if (SDL_GL_LoadLibrary(NULL) < 0) {
1494 if (flags & SDL_WINDOW_VULKAN) {
1495 if (!_this->Vulkan_CreateSurface) {
1496 SDL_SetError("Vulkan support is either not configured in SDL "
1497 "or not available in current SDL video driver "
1498 "(%s) or platform", _this->name);
1501 if (flags & SDL_WINDOW_OPENGL) {
1502 SDL_SetError("Vulkan and OpenGL not supported on same window");
1505 if (SDL_Vulkan_LoadLibrary(NULL) < 0) {
1510 if (flags & SDL_WINDOW_METAL) {
1511 if (!_this->Metal_CreateView) {
1512 SDL_SetError("Metal support is either not configured in SDL "
1513 "or not available in current SDL video driver "
1514 "(%s) or platform", _this->name);
1517 if (flags & SDL_WINDOW_OPENGL) {
1518 SDL_SetError("Metal and OpenGL not supported on same window");
1521 if (flags & SDL_WINDOW_VULKAN) {
1522 SDL_SetError("Metal and Vulkan not supported on same window. "
1523 "To use MoltenVK, set SDL_WINDOW_VULKAN only.");
1528 /* Unless the user has specified the high-DPI disabling hint, respect the
1529 * SDL_WINDOW_ALLOW_HIGHDPI flag.
1531 if (flags & SDL_WINDOW_ALLOW_HIGHDPI) {
1532 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_HIGHDPI_DISABLED, SDL_FALSE)) {
1533 flags &= ~SDL_WINDOW_ALLOW_HIGHDPI;
1537 window = (SDL_Window *)SDL_calloc(1, sizeof(*window));
1542 window->magic = &_this->window_magic;
1543 window->id = _this->next_object_id++;
1548 if (SDL_WINDOWPOS_ISUNDEFINED(x) || SDL_WINDOWPOS_ISUNDEFINED(y) ||
1549 SDL_WINDOWPOS_ISCENTERED(x) || SDL_WINDOWPOS_ISCENTERED(y)) {
1550 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
1554 displayIndex = SDL_GetIndexOfDisplay(display);
1555 SDL_GetDisplayBounds(displayIndex, &bounds);
1556 if (SDL_WINDOWPOS_ISUNDEFINED(x) || SDL_WINDOWPOS_ISCENTERED(x)) {
1557 window->x = bounds.x + (bounds.w - w) / 2;
1559 if (SDL_WINDOWPOS_ISUNDEFINED(y) || SDL_WINDOWPOS_ISCENTERED(y)) {
1560 window->y = bounds.y + (bounds.h - h) / 2;
1563 window->windowed.x = window->x;
1564 window->windowed.y = window->y;
1565 window->windowed.w = window->w;
1566 window->windowed.h = window->h;
1568 if (flags & SDL_WINDOW_FULLSCREEN) {
1569 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
1573 displayIndex = SDL_GetIndexOfDisplay(display);
1574 SDL_GetDisplayBounds(displayIndex, &bounds);
1576 window->x = bounds.x;
1577 window->y = bounds.y;
1578 window->w = bounds.w;
1579 window->h = bounds.h;
1582 window->flags = ((flags & CREATE_FLAGS) | SDL_WINDOW_HIDDEN);
1583 window->last_fullscreen_flags = window->flags;
1584 window->opacity = 1.0f;
1585 window->brightness = 1.0f;
1586 window->next = _this->windows;
1587 window->is_destroying = SDL_FALSE;
1589 if (_this->windows) {
1590 _this->windows->prev = window;
1592 _this->windows = window;
1594 if (_this->CreateSDLWindow && _this->CreateSDLWindow(_this, window) < 0) {
1595 SDL_DestroyWindow(window);
1599 /* Clear minimized if not on windows, only windows handles it at create rather than FinishWindowCreation,
1600 * but it's important or window focus will get broken on windows!
1602 #if !defined(__WIN32__)
1603 if (window->flags & SDL_WINDOW_MINIMIZED) {
1604 window->flags &= ~SDL_WINDOW_MINIMIZED;
1608 #if __WINRT__ && (NTDDI_VERSION < NTDDI_WIN10)
1609 /* HACK: WinRT 8.x apps can't choose whether or not they are fullscreen
1610 or not. The user can choose this, via OS-provided UI, but this can't
1611 be set programmatically.
1613 Just look at what SDL's WinRT video backend code detected with regards
1614 to fullscreen (being active, or not), and figure out a return/error code
1617 flags = window->flags;
1621 SDL_SetWindowTitle(window, title);
1623 SDL_FinishWindowCreation(window, flags);
1625 /* If the window was created fullscreen, make sure the mode code matches */
1626 SDL_UpdateFullscreenMode(window, FULLSCREEN_VISIBLE(window));
1632 SDL_CreateWindowFrom(const void *data)
1637 SDL_UninitializedVideo();
1640 if (!_this->CreateSDLWindowFrom) {
1644 window = (SDL_Window *)SDL_calloc(1, sizeof(*window));
1649 window->magic = &_this->window_magic;
1650 window->id = _this->next_object_id++;
1651 window->flags = SDL_WINDOW_FOREIGN;
1652 window->last_fullscreen_flags = window->flags;
1653 window->is_destroying = SDL_FALSE;
1654 window->opacity = 1.0f;
1655 window->brightness = 1.0f;
1656 window->next = _this->windows;
1657 if (_this->windows) {
1658 _this->windows->prev = window;
1660 _this->windows = window;
1662 if (_this->CreateSDLWindowFrom(_this, window, data) < 0) {
1663 SDL_DestroyWindow(window);
1667 PrepareDragAndDropSupport(window);
1673 SDL_RecreateWindow(SDL_Window * window, Uint32 flags)
1675 SDL_bool loaded_opengl = SDL_FALSE;
1676 SDL_bool need_gl_unload = SDL_FALSE;
1677 SDL_bool need_gl_load = SDL_FALSE;
1678 SDL_bool loaded_vulkan = SDL_FALSE;
1679 SDL_bool need_vulkan_unload = SDL_FALSE;
1680 SDL_bool need_vulkan_load = SDL_FALSE;
1682 if ((flags & SDL_WINDOW_OPENGL) && !_this->GL_CreateContext) {
1683 return SDL_SetError("OpenGL support is either not configured in SDL "
1684 "or not available in current SDL video driver "
1685 "(%s) or platform", _this->name);
1688 if (window->flags & SDL_WINDOW_FOREIGN) {
1689 /* Can't destroy and re-create foreign windows, hrm */
1690 flags |= SDL_WINDOW_FOREIGN;
1692 flags &= ~SDL_WINDOW_FOREIGN;
1695 /* Restore video mode, etc. */
1696 SDL_HideWindow(window);
1698 /* Tear down the old native window */
1699 if (window->surface) {
1700 window->surface->flags &= ~SDL_DONTFREE;
1701 SDL_FreeSurface(window->surface);
1702 window->surface = NULL;
1703 window->surface_valid = SDL_FALSE;
1705 if (_this->DestroyWindowFramebuffer) {
1706 _this->DestroyWindowFramebuffer(_this, window);
1708 if (_this->DestroyWindow && !(flags & SDL_WINDOW_FOREIGN)) {
1709 _this->DestroyWindow(_this, window);
1712 if ((window->flags & SDL_WINDOW_OPENGL) != (flags & SDL_WINDOW_OPENGL)) {
1713 if (flags & SDL_WINDOW_OPENGL) {
1714 need_gl_load = SDL_TRUE;
1716 need_gl_unload = SDL_TRUE;
1718 } else if (window->flags & SDL_WINDOW_OPENGL) {
1719 need_gl_unload = SDL_TRUE;
1720 need_gl_load = SDL_TRUE;
1723 if ((window->flags & SDL_WINDOW_METAL) != (flags & SDL_WINDOW_METAL)) {
1724 if (flags & SDL_WINDOW_METAL) {
1725 need_gl_load = SDL_TRUE;
1727 need_gl_unload = SDL_TRUE;
1729 } else if (window->flags & SDL_WINDOW_METAL) {
1730 need_gl_unload = SDL_TRUE;
1731 need_gl_load = SDL_TRUE;
1734 if ((window->flags & SDL_WINDOW_VULKAN) != (flags & SDL_WINDOW_VULKAN)) {
1735 if (flags & SDL_WINDOW_VULKAN) {
1736 need_vulkan_load = SDL_TRUE;
1738 need_vulkan_unload = SDL_TRUE;
1740 } else if (window->flags & SDL_WINDOW_VULKAN) {
1741 need_vulkan_unload = SDL_TRUE;
1742 need_vulkan_load = SDL_TRUE;
1745 if ((flags & SDL_WINDOW_VULKAN) && (flags & SDL_WINDOW_OPENGL)) {
1746 SDL_SetError("Vulkan and OpenGL not supported on same window");
1750 if ((flags & SDL_WINDOW_METAL) && (flags & SDL_WINDOW_OPENGL)) {
1751 SDL_SetError("Metal and OpenGL not supported on same window");
1755 if ((flags & SDL_WINDOW_METAL) && (flags & SDL_WINDOW_VULKAN)) {
1756 SDL_SetError("Metal and Vulkan not supported on same window");
1760 if (need_gl_unload) {
1761 SDL_GL_UnloadLibrary();
1764 if (need_vulkan_unload) {
1765 SDL_Vulkan_UnloadLibrary();
1769 if (SDL_GL_LoadLibrary(NULL) < 0) {
1772 loaded_opengl = SDL_TRUE;
1775 if (need_vulkan_load) {
1776 if (SDL_Vulkan_LoadLibrary(NULL) < 0) {
1779 loaded_vulkan = SDL_TRUE;
1782 window->flags = ((flags & CREATE_FLAGS) | SDL_WINDOW_HIDDEN);
1783 window->last_fullscreen_flags = window->flags;
1784 window->is_destroying = SDL_FALSE;
1786 if (_this->CreateSDLWindow && !(flags & SDL_WINDOW_FOREIGN)) {
1787 if (_this->CreateSDLWindow(_this, window) < 0) {
1788 if (loaded_opengl) {
1789 SDL_GL_UnloadLibrary();
1790 window->flags &= ~SDL_WINDOW_OPENGL;
1792 if (loaded_vulkan) {
1793 SDL_Vulkan_UnloadLibrary();
1794 window->flags &= ~SDL_WINDOW_VULKAN;
1800 if (flags & SDL_WINDOW_FOREIGN) {
1801 window->flags |= SDL_WINDOW_FOREIGN;
1804 if (_this->SetWindowTitle && window->title) {
1805 _this->SetWindowTitle(_this, window);
1808 if (_this->SetWindowIcon && window->icon) {
1809 _this->SetWindowIcon(_this, window, window->icon);
1812 if (window->hit_test) {
1813 _this->SetWindowHitTest(window, SDL_TRUE);
1816 SDL_FinishWindowCreation(window, flags);
1822 SDL_HasWindows(void)
1824 return (_this && _this->windows != NULL);
1828 SDL_GetWindowID(SDL_Window * window)
1830 CHECK_WINDOW_MAGIC(window, 0);
1836 SDL_GetWindowFromID(Uint32 id)
1843 for (window = _this->windows; window; window = window->next) {
1844 if (window->id == id) {
1852 SDL_GetWindowFlags(SDL_Window * window)
1854 CHECK_WINDOW_MAGIC(window, 0);
1856 return window->flags;
1860 SDL_SetWindowTitle(SDL_Window * window, const char *title)
1862 CHECK_WINDOW_MAGIC(window,);
1864 if (title == window->title) {
1867 SDL_free(window->title);
1869 window->title = SDL_strdup(title ? title : "");
1871 if (_this->SetWindowTitle) {
1872 _this->SetWindowTitle(_this, window);
1877 SDL_GetWindowTitle(SDL_Window * window)
1879 CHECK_WINDOW_MAGIC(window, "");
1881 return window->title ? window->title : "";
1885 SDL_SetWindowIcon(SDL_Window * window, SDL_Surface * icon)
1887 CHECK_WINDOW_MAGIC(window,);
1893 SDL_FreeSurface(window->icon);
1895 /* Convert the icon into ARGB8888 */
1896 window->icon = SDL_ConvertSurfaceFormat(icon, SDL_PIXELFORMAT_ARGB8888, 0);
1897 if (!window->icon) {
1901 if (_this->SetWindowIcon) {
1902 _this->SetWindowIcon(_this, window, window->icon);
1907 SDL_SetWindowData(SDL_Window * window, const char *name, void *userdata)
1909 SDL_WindowUserData *prev, *data;
1911 CHECK_WINDOW_MAGIC(window, NULL);
1913 /* Input validation */
1914 if (name == NULL || name[0] == '\0') {
1915 SDL_InvalidParamError("name");
1919 /* See if the named data already exists */
1921 for (data = window->data; data; prev = data, data = data->next) {
1922 if (data->name && SDL_strcmp(data->name, name) == 0) {
1923 void *last_value = data->data;
1926 /* Set the new value */
1927 data->data = userdata;
1929 /* Delete this value */
1931 prev->next = data->next;
1933 window->data = data->next;
1935 SDL_free(data->name);
1942 /* Add new data to the window */
1944 data = (SDL_WindowUserData *)SDL_malloc(sizeof(*data));
1945 data->name = SDL_strdup(name);
1946 data->data = userdata;
1947 data->next = window->data;
1948 window->data = data;
1954 SDL_GetWindowData(SDL_Window * window, const char *name)
1956 SDL_WindowUserData *data;
1958 CHECK_WINDOW_MAGIC(window, NULL);
1960 /* Input validation */
1961 if (name == NULL || name[0] == '\0') {
1962 SDL_InvalidParamError("name");
1966 for (data = window->data; data; data = data->next) {
1967 if (data->name && SDL_strcmp(data->name, name) == 0) {
1975 SDL_SetWindowPosition(SDL_Window * window, int x, int y)
1977 CHECK_WINDOW_MAGIC(window,);
1979 if (SDL_WINDOWPOS_ISCENTERED(x) || SDL_WINDOWPOS_ISCENTERED(y)) {
1980 int displayIndex = (x & 0xFFFF);
1982 if (displayIndex >= _this->num_displays) {
1988 SDL_GetDisplayBounds(displayIndex, &bounds);
1989 if (SDL_WINDOWPOS_ISCENTERED(x)) {
1990 x = bounds.x + (bounds.w - window->w) / 2;
1992 if (SDL_WINDOWPOS_ISCENTERED(y)) {
1993 y = bounds.y + (bounds.h - window->h) / 2;
1997 if ((window->flags & SDL_WINDOW_FULLSCREEN)) {
1998 if (!SDL_WINDOWPOS_ISUNDEFINED(x)) {
1999 window->windowed.x = x;
2001 if (!SDL_WINDOWPOS_ISUNDEFINED(y)) {
2002 window->windowed.y = y;
2005 if (!SDL_WINDOWPOS_ISUNDEFINED(x)) {
2008 if (!SDL_WINDOWPOS_ISUNDEFINED(y)) {
2012 if (_this->SetWindowPosition) {
2013 _this->SetWindowPosition(_this, window);
2019 SDL_GetWindowPosition(SDL_Window * window, int *x, int *y)
2021 CHECK_WINDOW_MAGIC(window,);
2023 /* Fullscreen windows are always at their display's origin */
2024 if (window->flags & SDL_WINDOW_FULLSCREEN) {
2034 /* Find the window's monitor and update to the
2036 displayIndex = SDL_GetWindowDisplayIndex(window);
2037 if (displayIndex >= 0) {
2042 SDL_GetDisplayBounds(displayIndex, &bounds);
2061 SDL_SetWindowBordered(SDL_Window * window, SDL_bool bordered)
2063 CHECK_WINDOW_MAGIC(window,);
2064 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
2065 const int want = (bordered != SDL_FALSE); /* normalize the flag. */
2066 const int have = ((window->flags & SDL_WINDOW_BORDERLESS) == 0);
2067 if ((want != have) && (_this->SetWindowBordered)) {
2069 window->flags &= ~SDL_WINDOW_BORDERLESS;
2071 window->flags |= SDL_WINDOW_BORDERLESS;
2073 _this->SetWindowBordered(_this, window, (SDL_bool) want);
2079 SDL_SetWindowResizable(SDL_Window * window, SDL_bool resizable)
2081 CHECK_WINDOW_MAGIC(window,);
2082 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
2083 const int want = (resizable != SDL_FALSE); /* normalize the flag. */
2084 const int have = ((window->flags & SDL_WINDOW_RESIZABLE) != 0);
2085 if ((want != have) && (_this->SetWindowResizable)) {
2087 window->flags |= SDL_WINDOW_RESIZABLE;
2089 window->flags &= ~SDL_WINDOW_RESIZABLE;
2091 _this->SetWindowResizable(_this, window, (SDL_bool) want);
2097 SDL_SetWindowSize(SDL_Window * window, int w, int h)
2099 CHECK_WINDOW_MAGIC(window,);
2101 SDL_InvalidParamError("w");
2105 SDL_InvalidParamError("h");
2109 /* Make sure we don't exceed any window size limits */
2110 if (window->min_w && w < window->min_w) {
2113 if (window->max_w && w > window->max_w) {
2116 if (window->min_h && h < window->min_h) {
2119 if (window->max_h && h > window->max_h) {
2123 window->windowed.w = w;
2124 window->windowed.h = h;
2126 if (window->flags & SDL_WINDOW_FULLSCREEN) {
2127 if (FULLSCREEN_VISIBLE(window) && (window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP) {
2128 window->last_fullscreen_flags = 0;
2129 SDL_UpdateFullscreenMode(window, SDL_TRUE);
2134 if (_this->SetWindowSize) {
2135 _this->SetWindowSize(_this, window);
2137 if (window->w == w && window->h == h) {
2138 /* We didn't get a SDL_WINDOWEVENT_RESIZED event (by design) */
2139 SDL_OnWindowResized(window);
2145 SDL_GetWindowSize(SDL_Window * window, int *w, int *h)
2147 CHECK_WINDOW_MAGIC(window,);
2157 SDL_GetWindowBordersSize(SDL_Window * window, int *top, int *left, int *bottom, int *right)
2161 if (!top) { top = &dummy; }
2162 if (!left) { left = &dummy; }
2163 if (!right) { right = &dummy; }
2164 if (!bottom) { bottom = &dummy; }
2166 /* Always initialize, so applications don't have to care */
2167 *top = *left = *bottom = *right = 0;
2169 CHECK_WINDOW_MAGIC(window, -1);
2171 if (!_this->GetWindowBordersSize) {
2172 return SDL_Unsupported();
2175 return _this->GetWindowBordersSize(_this, window, top, left, bottom, right);
2179 SDL_SetWindowMinimumSize(SDL_Window * window, int min_w, int min_h)
2181 CHECK_WINDOW_MAGIC(window,);
2183 SDL_InvalidParamError("min_w");
2187 SDL_InvalidParamError("min_h");
2191 if ((window->max_w && min_w > window->max_w) ||
2192 (window->max_h && min_h > window->max_h)) {
2193 SDL_SetError("SDL_SetWindowMinimumSize(): Tried to set minimum size larger than maximum size");
2197 window->min_w = min_w;
2198 window->min_h = min_h;
2200 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
2201 if (_this->SetWindowMinimumSize) {
2202 _this->SetWindowMinimumSize(_this, window);
2204 /* Ensure that window is not smaller than minimal size */
2205 SDL_SetWindowSize(window, SDL_max(window->w, window->min_w), SDL_max(window->h, window->min_h));
2210 SDL_GetWindowMinimumSize(SDL_Window * window, int *min_w, int *min_h)
2212 CHECK_WINDOW_MAGIC(window,);
2214 *min_w = window->min_w;
2217 *min_h = window->min_h;
2222 SDL_SetWindowMaximumSize(SDL_Window * window, int max_w, int max_h)
2224 CHECK_WINDOW_MAGIC(window,);
2226 SDL_InvalidParamError("max_w");
2230 SDL_InvalidParamError("max_h");
2234 if (max_w < window->min_w || max_h < window->min_h) {
2235 SDL_SetError("SDL_SetWindowMaximumSize(): Tried to set maximum size smaller than minimum size");
2239 window->max_w = max_w;
2240 window->max_h = max_h;
2242 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
2243 if (_this->SetWindowMaximumSize) {
2244 _this->SetWindowMaximumSize(_this, window);
2246 /* Ensure that window is not larger than maximal size */
2247 SDL_SetWindowSize(window, SDL_min(window->w, window->max_w), SDL_min(window->h, window->max_h));
2252 SDL_GetWindowMaximumSize(SDL_Window * window, int *max_w, int *max_h)
2254 CHECK_WINDOW_MAGIC(window,);
2256 *max_w = window->max_w;
2259 *max_h = window->max_h;
2264 SDL_ShowWindow(SDL_Window * window)
2266 CHECK_WINDOW_MAGIC(window,);
2268 if (window->flags & SDL_WINDOW_SHOWN) {
2272 if (_this->ShowWindow) {
2273 _this->ShowWindow(_this, window);
2275 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_SHOWN, 0, 0);
2279 SDL_HideWindow(SDL_Window * window)
2281 CHECK_WINDOW_MAGIC(window,);
2283 if (!(window->flags & SDL_WINDOW_SHOWN)) {
2287 window->is_hiding = SDL_TRUE;
2288 SDL_UpdateFullscreenMode(window, SDL_FALSE);
2290 if (_this->HideWindow) {
2291 _this->HideWindow(_this, window);
2293 window->is_hiding = SDL_FALSE;
2294 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
2298 SDL_RaiseWindow(SDL_Window * window)
2300 CHECK_WINDOW_MAGIC(window,);
2302 if (!(window->flags & SDL_WINDOW_SHOWN)) {
2305 if (_this->RaiseWindow) {
2306 _this->RaiseWindow(_this, window);
2311 SDL_MaximizeWindow(SDL_Window * window)
2313 CHECK_WINDOW_MAGIC(window,);
2315 if (window->flags & SDL_WINDOW_MAXIMIZED) {
2319 /* !!! FIXME: should this check if the window is resizable? */
2321 if (_this->MaximizeWindow) {
2322 _this->MaximizeWindow(_this, window);
2327 CanMinimizeWindow(SDL_Window * window)
2329 if (!_this->MinimizeWindow) {
2336 SDL_MinimizeWindow(SDL_Window * window)
2338 CHECK_WINDOW_MAGIC(window,);
2340 if (window->flags & SDL_WINDOW_MINIMIZED) {
2344 if (!CanMinimizeWindow(window)) {
2348 SDL_UpdateFullscreenMode(window, SDL_FALSE);
2350 if (_this->MinimizeWindow) {
2351 _this->MinimizeWindow(_this, window);
2356 SDL_RestoreWindow(SDL_Window * window)
2358 CHECK_WINDOW_MAGIC(window,);
2360 if (!(window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) {
2364 if (_this->RestoreWindow) {
2365 _this->RestoreWindow(_this, window);
2370 SDL_SetWindowFullscreen(SDL_Window * window, Uint32 flags)
2373 CHECK_WINDOW_MAGIC(window, -1);
2375 flags &= FULLSCREEN_MASK;
2377 if (flags == (window->flags & FULLSCREEN_MASK)) {
2381 /* clear the previous flags and OR in the new ones */
2382 oldflags = window->flags & FULLSCREEN_MASK;
2383 window->flags &= ~FULLSCREEN_MASK;
2384 window->flags |= flags;
2386 if (SDL_UpdateFullscreenMode(window, FULLSCREEN_VISIBLE(window)) == 0) {
2390 window->flags &= ~FULLSCREEN_MASK;
2391 window->flags |= oldflags;
2395 static SDL_Surface *
2396 SDL_CreateWindowFramebuffer(SDL_Window * window)
2402 Uint32 Rmask, Gmask, Bmask, Amask;
2404 if (!_this->CreateWindowFramebuffer || !_this->UpdateWindowFramebuffer) {
2408 if (_this->CreateWindowFramebuffer(_this, window, &format, &pixels, &pitch) < 0) {
2412 if (window->surface) {
2413 return window->surface;
2416 if (!SDL_PixelFormatEnumToMasks(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) {
2420 return SDL_CreateRGBSurfaceFrom(pixels, window->w, window->h, bpp, pitch, Rmask, Gmask, Bmask, Amask);
2424 SDL_GetWindowSurface(SDL_Window * window)
2426 CHECK_WINDOW_MAGIC(window, NULL);
2428 if (!window->surface_valid) {
2429 if (window->surface) {
2430 window->surface->flags &= ~SDL_DONTFREE;
2431 SDL_FreeSurface(window->surface);
2432 window->surface = NULL;
2434 window->surface = SDL_CreateWindowFramebuffer(window);
2435 if (window->surface) {
2436 window->surface_valid = SDL_TRUE;
2437 window->surface->flags |= SDL_DONTFREE;
2440 return window->surface;
2444 SDL_UpdateWindowSurface(SDL_Window * window)
2448 CHECK_WINDOW_MAGIC(window, -1);
2452 full_rect.w = window->w;
2453 full_rect.h = window->h;
2454 return SDL_UpdateWindowSurfaceRects(window, &full_rect, 1);
2458 SDL_UpdateWindowSurfaceRects(SDL_Window * window, const SDL_Rect * rects,
2461 CHECK_WINDOW_MAGIC(window, -1);
2463 if (!window->surface_valid) {
2464 return SDL_SetError("Window surface is invalid, please call SDL_GetWindowSurface() to get a new surface");
2467 return _this->UpdateWindowFramebuffer(_this, window, rects, numrects);
2471 SDL_SetWindowBrightness(SDL_Window * window, float brightness)
2476 CHECK_WINDOW_MAGIC(window, -1);
2478 SDL_CalculateGammaRamp(brightness, ramp);
2479 status = SDL_SetWindowGammaRamp(window, ramp, ramp, ramp);
2481 window->brightness = brightness;
2487 SDL_GetWindowBrightness(SDL_Window * window)
2489 CHECK_WINDOW_MAGIC(window, 1.0f);
2491 return window->brightness;
2495 SDL_SetWindowOpacity(SDL_Window * window, float opacity)
2498 CHECK_WINDOW_MAGIC(window, -1);
2500 if (!_this->SetWindowOpacity) {
2501 return SDL_Unsupported();
2504 if (opacity < 0.0f) {
2506 } else if (opacity > 1.0f) {
2510 retval = _this->SetWindowOpacity(_this, window, opacity);
2512 window->opacity = opacity;
2519 SDL_GetWindowOpacity(SDL_Window * window, float * out_opacity)
2521 CHECK_WINDOW_MAGIC(window, -1);
2524 *out_opacity = window->opacity;
2531 SDL_SetWindowModalFor(SDL_Window * modal_window, SDL_Window * parent_window)
2533 CHECK_WINDOW_MAGIC(modal_window, -1);
2534 CHECK_WINDOW_MAGIC(parent_window, -1);
2536 if (!_this->SetWindowModalFor) {
2537 return SDL_Unsupported();
2540 return _this->SetWindowModalFor(_this, modal_window, parent_window);
2544 SDL_SetWindowInputFocus(SDL_Window * window)
2546 CHECK_WINDOW_MAGIC(window, -1);
2548 if (!_this->SetWindowInputFocus) {
2549 return SDL_Unsupported();
2552 return _this->SetWindowInputFocus(_this, window);
2557 SDL_SetWindowGammaRamp(SDL_Window * window, const Uint16 * red,
2558 const Uint16 * green,
2559 const Uint16 * blue)
2561 CHECK_WINDOW_MAGIC(window, -1);
2563 if (!_this->SetWindowGammaRamp) {
2564 return SDL_Unsupported();
2567 if (!window->gamma) {
2568 if (SDL_GetWindowGammaRamp(window, NULL, NULL, NULL) < 0) {
2571 SDL_assert(window->gamma != NULL);
2575 SDL_memcpy(&window->gamma[0*256], red, 256*sizeof(Uint16));
2578 SDL_memcpy(&window->gamma[1*256], green, 256*sizeof(Uint16));
2581 SDL_memcpy(&window->gamma[2*256], blue, 256*sizeof(Uint16));
2583 if (window->flags & SDL_WINDOW_INPUT_FOCUS) {
2584 return _this->SetWindowGammaRamp(_this, window, window->gamma);
2591 SDL_GetWindowGammaRamp(SDL_Window * window, Uint16 * red,
2595 CHECK_WINDOW_MAGIC(window, -1);
2597 if (!window->gamma) {
2600 window->gamma = (Uint16 *)SDL_malloc(256*6*sizeof(Uint16));
2601 if (!window->gamma) {
2602 return SDL_OutOfMemory();
2604 window->saved_gamma = window->gamma + 3*256;
2606 if (_this->GetWindowGammaRamp) {
2607 if (_this->GetWindowGammaRamp(_this, window, window->gamma) < 0) {
2611 /* Create an identity gamma ramp */
2612 for (i = 0; i < 256; ++i) {
2613 Uint16 value = (Uint16)((i << 8) | i);
2615 window->gamma[0*256+i] = value;
2616 window->gamma[1*256+i] = value;
2617 window->gamma[2*256+i] = value;
2620 SDL_memcpy(window->saved_gamma, window->gamma, 3*256*sizeof(Uint16));
2624 SDL_memcpy(red, &window->gamma[0*256], 256*sizeof(Uint16));
2627 SDL_memcpy(green, &window->gamma[1*256], 256*sizeof(Uint16));
2630 SDL_memcpy(blue, &window->gamma[2*256], 256*sizeof(Uint16));
2636 SDL_UpdateWindowGrab(SDL_Window * window)
2638 SDL_Window *grabbed_window;
2640 if ((SDL_GetMouse()->relative_mode || (window->flags & SDL_WINDOW_INPUT_GRABBED)) &&
2641 (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
2644 grabbed = SDL_FALSE;
2647 grabbed_window = _this->grabbed_window;
2649 if (grabbed_window && (grabbed_window != window)) {
2650 /* stealing a grab from another window! */
2651 grabbed_window->flags &= ~SDL_WINDOW_INPUT_GRABBED;
2652 if (_this->SetWindowGrab) {
2653 _this->SetWindowGrab(_this, grabbed_window, SDL_FALSE);
2656 _this->grabbed_window = window;
2657 } else if (grabbed_window == window) {
2658 _this->grabbed_window = NULL; /* ungrabbing. */
2661 if (_this->SetWindowGrab) {
2662 _this->SetWindowGrab(_this, window, grabbed);
2667 SDL_SetWindowGrab(SDL_Window * window, SDL_bool grabbed)
2669 CHECK_WINDOW_MAGIC(window,);
2671 if (!!grabbed == !!(window->flags & SDL_WINDOW_INPUT_GRABBED)) {
2675 window->flags |= SDL_WINDOW_INPUT_GRABBED;
2677 window->flags &= ~SDL_WINDOW_INPUT_GRABBED;
2679 SDL_UpdateWindowGrab(window);
2683 SDL_GetWindowGrab(SDL_Window * window)
2685 CHECK_WINDOW_MAGIC(window, SDL_FALSE);
2686 SDL_assert(!_this->grabbed_window || ((_this->grabbed_window->flags & SDL_WINDOW_INPUT_GRABBED) != 0));
2687 return window == _this->grabbed_window;
2691 SDL_GetGrabbedWindow(void)
2693 SDL_assert(!_this->grabbed_window || ((_this->grabbed_window->flags & SDL_WINDOW_INPUT_GRABBED) != 0));
2694 return _this->grabbed_window;
2698 SDL_OnWindowShown(SDL_Window * window)
2700 SDL_OnWindowRestored(window);
2704 SDL_OnWindowHidden(SDL_Window * window)
2706 SDL_UpdateFullscreenMode(window, SDL_FALSE);
2710 SDL_OnWindowResized(SDL_Window * window)
2712 window->surface_valid = SDL_FALSE;
2713 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_SIZE_CHANGED, window->w, window->h);
2717 SDL_OnWindowMinimized(SDL_Window * window)
2719 SDL_UpdateFullscreenMode(window, SDL_FALSE);
2723 SDL_OnWindowRestored(SDL_Window * window)
2726 * FIXME: Is this fine to just remove this, or should it be preserved just
2727 * for the fullscreen case? In principle it seems like just hiding/showing
2728 * windows shouldn't affect the stacking order; maybe the right fix is to
2729 * re-decouple OnWindowShown and OnWindowRestored.
2731 /*SDL_RaiseWindow(window);*/
2733 if (FULLSCREEN_VISIBLE(window)) {
2734 SDL_UpdateFullscreenMode(window, SDL_TRUE);
2739 SDL_OnWindowEnter(SDL_Window * window)
2741 if (_this->OnWindowEnter) {
2742 _this->OnWindowEnter(_this, window);
2747 SDL_OnWindowLeave(SDL_Window * window)
2752 SDL_OnWindowFocusGained(SDL_Window * window)
2754 SDL_Mouse *mouse = SDL_GetMouse();
2756 if (window->gamma && _this->SetWindowGammaRamp) {
2757 _this->SetWindowGammaRamp(_this, window, window->gamma);
2760 if (mouse && mouse->relative_mode) {
2761 SDL_SetMouseFocus(window);
2762 SDL_WarpMouseInWindow(window, window->w/2, window->h/2);
2765 SDL_UpdateWindowGrab(window);
2769 ShouldMinimizeOnFocusLoss(SDL_Window * window)
2771 if (!(window->flags & SDL_WINDOW_FULLSCREEN) || window->is_destroying) {
2776 if (SDL_strcmp(_this->name, "cocoa") == 0) { /* don't do this for X11, etc */
2777 if (Cocoa_IsWindowInFullscreenSpace(window)) {
2785 extern SDL_bool Android_JNI_ShouldMinimizeOnFocusLoss(void);
2786 if (! Android_JNI_ShouldMinimizeOnFocusLoss()) {
2792 return SDL_GetHintBoolean(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, SDL_FALSE);
2796 SDL_OnWindowFocusLost(SDL_Window * window)
2798 if (window->gamma && _this->SetWindowGammaRamp) {
2799 _this->SetWindowGammaRamp(_this, window, window->saved_gamma);
2802 SDL_UpdateWindowGrab(window);
2804 if (ShouldMinimizeOnFocusLoss(window)) {
2805 SDL_MinimizeWindow(window);
2809 /* !!! FIXME: is this different than SDL_GetKeyboardFocus()?
2810 !!! FIXME: Also, SDL_GetKeyboardFocus() is O(1), this isn't. */
2812 SDL_GetFocusWindow(void)
2819 for (window = _this->windows; window; window = window->next) {
2820 if (window->flags & SDL_WINDOW_INPUT_FOCUS) {
2828 SDL_DestroyWindow(SDL_Window * window)
2830 SDL_VideoDisplay *display;
2832 CHECK_WINDOW_MAGIC(window,);
2834 window->is_destroying = SDL_TRUE;
2836 /* Restore video mode, etc. */
2837 SDL_HideWindow(window);
2839 /* Make sure this window no longer has focus */
2840 if (SDL_GetKeyboardFocus() == window) {
2841 SDL_SetKeyboardFocus(NULL);
2843 if (SDL_GetMouseFocus() == window) {
2844 SDL_SetMouseFocus(NULL);
2847 /* make no context current if this is the current context window. */
2848 if (window->flags & SDL_WINDOW_OPENGL) {
2849 if (_this->current_glwin == window) {
2850 SDL_GL_MakeCurrent(window, NULL);
2854 if (window->surface) {
2855 window->surface->flags &= ~SDL_DONTFREE;
2856 SDL_FreeSurface(window->surface);
2857 window->surface = NULL;
2858 window->surface_valid = SDL_FALSE;
2860 if (_this->DestroyWindowFramebuffer) {
2861 _this->DestroyWindowFramebuffer(_this, window);
2863 if (_this->DestroyWindow) {
2864 _this->DestroyWindow(_this, window);
2866 if (window->flags & SDL_WINDOW_OPENGL) {
2867 SDL_GL_UnloadLibrary();
2869 if (window->flags & SDL_WINDOW_VULKAN) {
2870 SDL_Vulkan_UnloadLibrary();
2873 display = SDL_GetDisplayForWindow(window);
2874 if (display->fullscreen_window == window) {
2875 display->fullscreen_window = NULL;
2878 /* Now invalidate magic */
2879 window->magic = NULL;
2881 /* Free memory associated with the window */
2882 SDL_free(window->title);
2883 SDL_FreeSurface(window->icon);
2884 SDL_free(window->gamma);
2885 while (window->data) {
2886 SDL_WindowUserData *data = window->data;
2888 window->data = data->next;
2889 SDL_free(data->name);
2893 /* Unlink the window from the list */
2895 window->next->prev = window->prev;
2898 window->prev->next = window->next;
2900 _this->windows = window->next;
2907 SDL_IsScreenSaverEnabled()
2912 return _this->suspend_screensaver ? SDL_FALSE : SDL_TRUE;
2916 SDL_EnableScreenSaver()
2921 if (!_this->suspend_screensaver) {
2924 _this->suspend_screensaver = SDL_FALSE;
2925 if (_this->SuspendScreenSaver) {
2926 _this->SuspendScreenSaver(_this);
2931 SDL_DisableScreenSaver()
2936 if (_this->suspend_screensaver) {
2939 _this->suspend_screensaver = SDL_TRUE;
2940 if (_this->SuspendScreenSaver) {
2941 _this->SuspendScreenSaver(_this);
2954 /* Halt event processing before doing anything else */
2958 SDL_QuitSubSystem(SDL_INIT_EVENTS);
2960 SDL_EnableScreenSaver();
2962 /* Clean up the system video */
2963 while (_this->windows) {
2964 SDL_DestroyWindow(_this->windows);
2966 _this->VideoQuit(_this);
2968 for (i = 0; i < _this->num_displays; ++i) {
2969 SDL_VideoDisplay *display = &_this->displays[i];
2970 for (j = display->num_display_modes; j--;) {
2971 SDL_free(display->display_modes[j].driverdata);
2972 display->display_modes[j].driverdata = NULL;
2974 SDL_free(display->display_modes);
2975 display->display_modes = NULL;
2976 SDL_free(display->desktop_mode.driverdata);
2977 display->desktop_mode.driverdata = NULL;
2978 SDL_free(display->driverdata);
2979 display->driverdata = NULL;
2981 if (_this->displays) {
2982 for (i = 0; i < _this->num_displays; ++i) {
2983 SDL_free(_this->displays[i].name);
2985 SDL_free(_this->displays);
2986 _this->displays = NULL;
2987 _this->num_displays = 0;
2989 SDL_free(_this->clipboard_text);
2990 _this->clipboard_text = NULL;
2996 SDL_GL_LoadLibrary(const char *path)
3001 return SDL_UninitializedVideo();
3003 if (_this->gl_config.driver_loaded) {
3004 if (path && SDL_strcmp(path, _this->gl_config.driver_path) != 0) {
3005 return SDL_SetError("OpenGL library already loaded");
3009 if (!_this->GL_LoadLibrary) {
3010 return SDL_SetError("No dynamic GL support in current SDL video driver (%s)", _this->name);
3012 retval = _this->GL_LoadLibrary(_this, path);
3015 ++_this->gl_config.driver_loaded;
3017 if (_this->GL_UnloadLibrary) {
3018 _this->GL_UnloadLibrary(_this);
3025 SDL_GL_GetProcAddress(const char *proc)
3030 SDL_UninitializedVideo();
3034 if (_this->GL_GetProcAddress) {
3035 if (_this->gl_config.driver_loaded) {
3036 func = _this->GL_GetProcAddress(_this, proc);
3038 SDL_SetError("No GL driver has been loaded");
3041 SDL_SetError("No dynamic GL support in current SDL video driver (%s)", _this->name);
3047 SDL_GL_UnloadLibrary(void)
3050 SDL_UninitializedVideo();
3053 if (_this->gl_config.driver_loaded > 0) {
3054 if (--_this->gl_config.driver_loaded > 0) {
3057 if (_this->GL_UnloadLibrary) {
3058 _this->GL_UnloadLibrary(_this);
3063 #if SDL_VIDEO_OPENGL || SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
3064 static SDL_INLINE SDL_bool
3065 isAtLeastGL3(const char *verstr)
3067 return (verstr && (SDL_atoi(verstr) >= 3));
3072 SDL_GL_ExtensionSupported(const char *extension)
3074 #if SDL_VIDEO_OPENGL || SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
3075 const GLubyte *(APIENTRY * glGetStringFunc) (GLenum);
3076 const char *extensions;
3078 const char *where, *terminator;
3080 /* Extension names should not have spaces. */
3081 where = SDL_strchr(extension, ' ');
3082 if (where || *extension == '\0') {
3085 /* See if there's an environment variable override */
3086 start = SDL_getenv(extension);
3087 if (start && *start == '0') {
3091 /* Lookup the available extensions */
3093 glGetStringFunc = SDL_GL_GetProcAddress("glGetString");
3094 if (!glGetStringFunc) {
3098 if (isAtLeastGL3((const char *) glGetStringFunc(GL_VERSION))) {
3099 const GLubyte *(APIENTRY * glGetStringiFunc) (GLenum, GLuint);
3100 void (APIENTRY * glGetIntegervFunc) (GLenum pname, GLint * params);
3104 glGetStringiFunc = SDL_GL_GetProcAddress("glGetStringi");
3105 glGetIntegervFunc = SDL_GL_GetProcAddress("glGetIntegerv");
3106 if ((!glGetStringiFunc) || (!glGetIntegervFunc)) {
3110 #ifndef GL_NUM_EXTENSIONS
3111 #define GL_NUM_EXTENSIONS 0x821D
3113 glGetIntegervFunc(GL_NUM_EXTENSIONS, &num_exts);
3114 for (i = 0; i < num_exts; i++) {
3115 const char *thisext = (const char *) glGetStringiFunc(GL_EXTENSIONS, i);
3116 if (SDL_strcmp(thisext, extension) == 0) {
3124 /* Try the old way with glGetString(GL_EXTENSIONS) ... */
3126 extensions = (const char *) glGetStringFunc(GL_EXTENSIONS);
3131 * It takes a bit of care to be fool-proof about parsing the OpenGL
3132 * extensions string. Don't be fooled by sub-strings, etc.
3138 where = SDL_strstr(start, extension);
3142 terminator = where + SDL_strlen(extension);
3143 if (where == extensions || *(where - 1) == ' ')
3144 if (*terminator == ' ' || *terminator == '\0')
3155 /* Deduce supported ES profile versions from the supported
3156 ARB_ES*_compatibility extensions. There is no direct query.
3158 This is normally only called when the OpenGL driver supports
3159 {GLX,WGL}_EXT_create_context_es2_profile.
3162 SDL_GL_DeduceMaxSupportedESProfile(int* major, int* minor)
3164 /* THIS REQUIRES AN EXISTING GL CONTEXT THAT HAS BEEN MADE CURRENT. */
3165 /* Please refer to https://bugzilla.libsdl.org/show_bug.cgi?id=3725 for discussion. */
3166 #if SDL_VIDEO_OPENGL || SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
3167 /* XXX This is fragile; it will break in the event of release of
3168 * new versions of OpenGL ES.
3170 if (SDL_GL_ExtensionSupported("GL_ARB_ES3_2_compatibility")) {
3173 } else if (SDL_GL_ExtensionSupported("GL_ARB_ES3_1_compatibility")) {
3176 } else if (SDL_GL_ExtensionSupported("GL_ARB_ES3_compatibility")) {
3187 SDL_GL_ResetAttributes()
3193 _this->gl_config.red_size = 3;
3194 _this->gl_config.green_size = 3;
3195 _this->gl_config.blue_size = 2;
3196 _this->gl_config.alpha_size = 0;
3197 _this->gl_config.buffer_size = 0;
3198 _this->gl_config.depth_size = 16;
3199 _this->gl_config.stencil_size = 0;
3200 _this->gl_config.double_buffer = 1;
3201 _this->gl_config.accum_red_size = 0;
3202 _this->gl_config.accum_green_size = 0;
3203 _this->gl_config.accum_blue_size = 0;
3204 _this->gl_config.accum_alpha_size = 0;
3205 _this->gl_config.stereo = 0;
3206 _this->gl_config.multisamplebuffers = 0;
3207 _this->gl_config.multisamplesamples = 0;
3208 _this->gl_config.retained_backing = 1;
3209 _this->gl_config.accelerated = -1; /* accelerated or not, both are fine */
3211 #if SDL_VIDEO_OPENGL
3212 _this->gl_config.major_version = 2;
3213 _this->gl_config.minor_version = 1;
3214 _this->gl_config.profile_mask = 0;
3215 #elif SDL_VIDEO_OPENGL_ES2
3216 _this->gl_config.major_version = 2;
3217 _this->gl_config.minor_version = 0;
3218 _this->gl_config.profile_mask = SDL_GL_CONTEXT_PROFILE_ES;
3219 #elif SDL_VIDEO_OPENGL_ES
3220 _this->gl_config.major_version = 1;
3221 _this->gl_config.minor_version = 1;
3222 _this->gl_config.profile_mask = SDL_GL_CONTEXT_PROFILE_ES;
3225 if (_this->GL_DefaultProfileConfig) {
3226 _this->GL_DefaultProfileConfig(_this, &_this->gl_config.profile_mask,
3227 &_this->gl_config.major_version,
3228 &_this->gl_config.minor_version);
3231 _this->gl_config.flags = 0;
3232 _this->gl_config.framebuffer_srgb_capable = 0;
3233 _this->gl_config.no_error = 0;
3234 _this->gl_config.release_behavior = SDL_GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH;
3235 _this->gl_config.reset_notification = SDL_GL_CONTEXT_RESET_NO_NOTIFICATION;
3237 _this->gl_config.share_with_current_context = 0;
3241 SDL_GL_SetAttribute(SDL_GLattr attr, int value)
3243 #if SDL_VIDEO_OPENGL || SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
3247 return SDL_UninitializedVideo();
3251 case SDL_GL_RED_SIZE:
3252 _this->gl_config.red_size = value;
3254 case SDL_GL_GREEN_SIZE:
3255 _this->gl_config.green_size = value;
3257 case SDL_GL_BLUE_SIZE:
3258 _this->gl_config.blue_size = value;
3260 case SDL_GL_ALPHA_SIZE:
3261 _this->gl_config.alpha_size = value;
3263 case SDL_GL_DOUBLEBUFFER:
3264 _this->gl_config.double_buffer = value;
3266 case SDL_GL_BUFFER_SIZE:
3267 _this->gl_config.buffer_size = value;
3269 case SDL_GL_DEPTH_SIZE:
3270 _this->gl_config.depth_size = value;
3272 case SDL_GL_STENCIL_SIZE:
3273 _this->gl_config.stencil_size = value;
3275 case SDL_GL_ACCUM_RED_SIZE:
3276 _this->gl_config.accum_red_size = value;
3278 case SDL_GL_ACCUM_GREEN_SIZE:
3279 _this->gl_config.accum_green_size = value;
3281 case SDL_GL_ACCUM_BLUE_SIZE:
3282 _this->gl_config.accum_blue_size = value;
3284 case SDL_GL_ACCUM_ALPHA_SIZE:
3285 _this->gl_config.accum_alpha_size = value;
3288 _this->gl_config.stereo = value;
3290 case SDL_GL_MULTISAMPLEBUFFERS:
3291 _this->gl_config.multisamplebuffers = value;
3293 case SDL_GL_MULTISAMPLESAMPLES:
3294 _this->gl_config.multisamplesamples = value;
3296 case SDL_GL_ACCELERATED_VISUAL:
3297 _this->gl_config.accelerated = value;
3299 case SDL_GL_RETAINED_BACKING:
3300 _this->gl_config.retained_backing = value;
3302 case SDL_GL_CONTEXT_MAJOR_VERSION:
3303 _this->gl_config.major_version = value;
3305 case SDL_GL_CONTEXT_MINOR_VERSION:
3306 _this->gl_config.minor_version = value;
3308 case SDL_GL_CONTEXT_EGL:
3309 /* FIXME: SDL_GL_CONTEXT_EGL to be deprecated in SDL 2.1 */
3311 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
3313 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
3316 case SDL_GL_CONTEXT_FLAGS:
3317 if (value & ~(SDL_GL_CONTEXT_DEBUG_FLAG |
3318 SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG |
3319 SDL_GL_CONTEXT_ROBUST_ACCESS_FLAG |
3320 SDL_GL_CONTEXT_RESET_ISOLATION_FLAG)) {
3321 retval = SDL_SetError("Unknown OpenGL context flag %d", value);
3324 _this->gl_config.flags = value;
3326 case SDL_GL_CONTEXT_PROFILE_MASK:
3328 value != SDL_GL_CONTEXT_PROFILE_CORE &&
3329 value != SDL_GL_CONTEXT_PROFILE_COMPATIBILITY &&
3330 value != SDL_GL_CONTEXT_PROFILE_ES) {
3331 retval = SDL_SetError("Unknown OpenGL context profile %d", value);
3334 _this->gl_config.profile_mask = value;
3336 case SDL_GL_SHARE_WITH_CURRENT_CONTEXT:
3337 _this->gl_config.share_with_current_context = value;
3339 case SDL_GL_FRAMEBUFFER_SRGB_CAPABLE:
3340 _this->gl_config.framebuffer_srgb_capable = value;
3342 case SDL_GL_CONTEXT_RELEASE_BEHAVIOR:
3343 _this->gl_config.release_behavior = value;
3345 case SDL_GL_CONTEXT_RESET_NOTIFICATION:
3346 _this->gl_config.reset_notification = value;
3348 case SDL_GL_CONTEXT_NO_ERROR:
3349 _this->gl_config.no_error = value;
3352 retval = SDL_SetError("Unknown OpenGL attribute");
3357 return SDL_Unsupported();
3358 #endif /* SDL_VIDEO_OPENGL */
3362 SDL_GL_GetAttribute(SDL_GLattr attr, int *value)
3364 #if SDL_VIDEO_OPENGL || SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
3365 GLenum (APIENTRY *glGetErrorFunc) (void);
3370 * Some queries in Core Profile desktop OpenGL 3+ contexts require
3371 * glGetFramebufferAttachmentParameteriv instead of glGetIntegerv. Note that
3372 * the enums we use for the former function don't exist in OpenGL ES 2, and
3373 * the function itself doesn't exist prior to OpenGL 3 and OpenGL ES 2.
3375 #if SDL_VIDEO_OPENGL
3376 const GLubyte *(APIENTRY *glGetStringFunc) (GLenum name);
3377 void (APIENTRY *glGetFramebufferAttachmentParameterivFunc) (GLenum target, GLenum attachment, GLenum pname, GLint* params);
3378 GLenum attachment = GL_BACK_LEFT;
3379 GLenum attachmentattrib = 0;
3383 return SDL_InvalidParamError("value");
3386 /* Clear value in any case */
3390 return SDL_UninitializedVideo();
3394 case SDL_GL_RED_SIZE:
3395 #if SDL_VIDEO_OPENGL
3396 attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE;
3398 attrib = GL_RED_BITS;
3400 case SDL_GL_BLUE_SIZE:
3401 #if SDL_VIDEO_OPENGL
3402 attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE;
3404 attrib = GL_BLUE_BITS;
3406 case SDL_GL_GREEN_SIZE:
3407 #if SDL_VIDEO_OPENGL
3408 attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE;
3410 attrib = GL_GREEN_BITS;
3412 case SDL_GL_ALPHA_SIZE:
3413 #if SDL_VIDEO_OPENGL
3414 attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE;
3416 attrib = GL_ALPHA_BITS;
3418 case SDL_GL_DOUBLEBUFFER:
3419 #if SDL_VIDEO_OPENGL
3420 attrib = GL_DOUBLEBUFFER;
3423 /* OpenGL ES 1.0 and above specifications have EGL_SINGLE_BUFFER */
3424 /* parameter which switches double buffer to single buffer. OpenGL ES */
3425 /* SDL driver must set proper value after initialization */
3426 *value = _this->gl_config.double_buffer;
3429 case SDL_GL_DEPTH_SIZE:
3430 #if SDL_VIDEO_OPENGL
3431 attachment = GL_DEPTH;
3432 attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE;
3434 attrib = GL_DEPTH_BITS;
3436 case SDL_GL_STENCIL_SIZE:
3437 #if SDL_VIDEO_OPENGL
3438 attachment = GL_STENCIL;
3439 attachmentattrib = GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE;
3441 attrib = GL_STENCIL_BITS;
3443 #if SDL_VIDEO_OPENGL
3444 case SDL_GL_ACCUM_RED_SIZE:
3445 attrib = GL_ACCUM_RED_BITS;
3447 case SDL_GL_ACCUM_GREEN_SIZE:
3448 attrib = GL_ACCUM_GREEN_BITS;
3450 case SDL_GL_ACCUM_BLUE_SIZE:
3451 attrib = GL_ACCUM_BLUE_BITS;
3453 case SDL_GL_ACCUM_ALPHA_SIZE:
3454 attrib = GL_ACCUM_ALPHA_BITS;
3460 case SDL_GL_ACCUM_RED_SIZE:
3461 case SDL_GL_ACCUM_GREEN_SIZE:
3462 case SDL_GL_ACCUM_BLUE_SIZE:
3463 case SDL_GL_ACCUM_ALPHA_SIZE:
3465 /* none of these are supported in OpenGL ES */
3469 case SDL_GL_MULTISAMPLEBUFFERS:
3470 attrib = GL_SAMPLE_BUFFERS;
3472 case SDL_GL_MULTISAMPLESAMPLES:
3473 attrib = GL_SAMPLES;
3475 case SDL_GL_CONTEXT_RELEASE_BEHAVIOR:
3476 #if SDL_VIDEO_OPENGL
3477 attrib = GL_CONTEXT_RELEASE_BEHAVIOR;
3479 attrib = GL_CONTEXT_RELEASE_BEHAVIOR_KHR;
3482 case SDL_GL_BUFFER_SIZE:
3484 int rsize = 0, gsize = 0, bsize = 0, asize = 0;
3486 /* There doesn't seem to be a single flag in OpenGL for this! */
3487 if (SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &rsize) < 0) {
3490 if (SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &gsize) < 0) {
3493 if (SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &bsize) < 0) {
3496 if (SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &asize) < 0) {
3500 *value = rsize + gsize + bsize + asize;
3503 case SDL_GL_ACCELERATED_VISUAL:
3505 /* FIXME: How do we get this information? */
3506 *value = (_this->gl_config.accelerated != 0);
3509 case SDL_GL_RETAINED_BACKING:
3511 *value = _this->gl_config.retained_backing;
3514 case SDL_GL_CONTEXT_MAJOR_VERSION:
3516 *value = _this->gl_config.major_version;
3519 case SDL_GL_CONTEXT_MINOR_VERSION:
3521 *value = _this->gl_config.minor_version;
3524 case SDL_GL_CONTEXT_EGL:
3525 /* FIXME: SDL_GL_CONTEXT_EGL to be deprecated in SDL 2.1 */
3527 if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
3535 case SDL_GL_CONTEXT_FLAGS:
3537 *value = _this->gl_config.flags;
3540 case SDL_GL_CONTEXT_PROFILE_MASK:
3542 *value = _this->gl_config.profile_mask;
3545 case SDL_GL_SHARE_WITH_CURRENT_CONTEXT:
3547 *value = _this->gl_config.share_with_current_context;
3550 case SDL_GL_FRAMEBUFFER_SRGB_CAPABLE:
3552 *value = _this->gl_config.framebuffer_srgb_capable;
3555 case SDL_GL_CONTEXT_NO_ERROR:
3557 *value = _this->gl_config.no_error;
3561 return SDL_SetError("Unknown OpenGL attribute");
3564 #if SDL_VIDEO_OPENGL
3565 glGetStringFunc = SDL_GL_GetProcAddress("glGetString");
3566 if (!glGetStringFunc) {
3570 if (attachmentattrib && isAtLeastGL3((const char *) glGetStringFunc(GL_VERSION))) {
3571 glGetFramebufferAttachmentParameterivFunc = SDL_GL_GetProcAddress("glGetFramebufferAttachmentParameteriv");
3573 if (glGetFramebufferAttachmentParameterivFunc) {
3574 glGetFramebufferAttachmentParameterivFunc(GL_FRAMEBUFFER, attachment, attachmentattrib, (GLint *) value);
3581 void (APIENTRY *glGetIntegervFunc) (GLenum pname, GLint * params);
3582 glGetIntegervFunc = SDL_GL_GetProcAddress("glGetIntegerv");
3583 if (glGetIntegervFunc) {
3584 glGetIntegervFunc(attrib, (GLint *) value);
3590 glGetErrorFunc = SDL_GL_GetProcAddress("glGetError");
3591 if (!glGetErrorFunc) {
3595 error = glGetErrorFunc();
3596 if (error != GL_NO_ERROR) {
3597 if (error == GL_INVALID_ENUM) {
3598 return SDL_SetError("OpenGL error: GL_INVALID_ENUM");
3599 } else if (error == GL_INVALID_VALUE) {
3600 return SDL_SetError("OpenGL error: GL_INVALID_VALUE");
3602 return SDL_SetError("OpenGL error: %08X", error);
3606 return SDL_Unsupported();
3607 #endif /* SDL_VIDEO_OPENGL */
3611 SDL_GL_CreateContext(SDL_Window * window)
3613 SDL_GLContext ctx = NULL;
3614 CHECK_WINDOW_MAGIC(window, NULL);
3616 if (!(window->flags & SDL_WINDOW_OPENGL)) {
3617 SDL_SetError("The specified window isn't an OpenGL window");
3621 ctx = _this->GL_CreateContext(_this, window);
3623 /* Creating a context is assumed to make it current in the SDL driver. */
3625 _this->current_glwin = window;
3626 _this->current_glctx = ctx;
3627 SDL_TLSSet(_this->current_glwin_tls, window, NULL);
3628 SDL_TLSSet(_this->current_glctx_tls, ctx, NULL);
3634 SDL_GL_MakeCurrent(SDL_Window * window, SDL_GLContext ctx)
3638 if (window == SDL_GL_GetCurrentWindow() &&
3639 ctx == SDL_GL_GetCurrentContext()) {
3640 /* We're already current. */
3646 } else if (window) {
3647 CHECK_WINDOW_MAGIC(window, -1);
3649 if (!(window->flags & SDL_WINDOW_OPENGL)) {
3650 return SDL_SetError("The specified window isn't an OpenGL window");
3652 } else if (!_this->gl_allow_no_surface) {
3653 return SDL_SetError("Use of OpenGL without a window is not supported on this platform");
3656 retval = _this->GL_MakeCurrent(_this, window, ctx);
3658 _this->current_glwin = window;
3659 _this->current_glctx = ctx;
3660 SDL_TLSSet(_this->current_glwin_tls, window, NULL);
3661 SDL_TLSSet(_this->current_glctx_tls, ctx, NULL);
3667 SDL_GL_GetCurrentWindow(void)
3670 SDL_UninitializedVideo();
3673 return (SDL_Window *)SDL_TLSGet(_this->current_glwin_tls);
3677 SDL_GL_GetCurrentContext(void)
3680 SDL_UninitializedVideo();
3683 return (SDL_GLContext)SDL_TLSGet(_this->current_glctx_tls);
3686 void SDL_GL_GetDrawableSize(SDL_Window * window, int *w, int *h)
3688 CHECK_WINDOW_MAGIC(window,);
3690 if (_this->GL_GetDrawableSize) {
3691 _this->GL_GetDrawableSize(_this, window, w, h);
3693 SDL_GetWindowSize(window, w, h);
3698 SDL_GL_SetSwapInterval(int interval)
3701 return SDL_UninitializedVideo();
3702 } else if (SDL_GL_GetCurrentContext() == NULL) {
3703 return SDL_SetError("No OpenGL context has been made current");
3704 } else if (_this->GL_SetSwapInterval) {
3705 return _this->GL_SetSwapInterval(_this, interval);
3707 return SDL_SetError("Setting the swap interval is not supported");
3712 SDL_GL_GetSwapInterval(void)
3716 } else if (SDL_GL_GetCurrentContext() == NULL) {
3718 } else if (_this->GL_GetSwapInterval) {
3719 return _this->GL_GetSwapInterval(_this);
3726 SDL_GL_SwapWindow(SDL_Window * window)
3728 CHECK_WINDOW_MAGIC(window,);
3730 if (!(window->flags & SDL_WINDOW_OPENGL)) {
3731 SDL_SetError("The specified window isn't an OpenGL window");
3735 if (SDL_GL_GetCurrentWindow() != window) {
3736 SDL_SetError("The specified window has not been made current");
3740 _this->GL_SwapWindow(_this, window);
3744 SDL_GL_DeleteContext(SDL_GLContext context)
3746 if (!_this || !context) {
3750 if (SDL_GL_GetCurrentContext() == context) {
3751 SDL_GL_MakeCurrent(NULL, NULL);
3754 _this->GL_DeleteContext(_this, context);
3759 * Utility function used by SDL_WM_SetIcon(); flags & 1 for color key, flags
3760 * & 2 for alpha channel.
3763 CreateMaskFromColorKeyOrAlpha(SDL_Surface * icon, Uint8 * mask, int flags)
3767 #define SET_MASKBIT(icon, x, y, mask) \
3768 mask[(y*((icon->w+7)/8))+(x/8)] &= ~(0x01<<(7-(x%8)))
3770 colorkey = icon->format->colorkey;
3771 switch (icon->format->BytesPerPixel) {
3775 for (y = 0; y < icon->h; ++y) {
3776 pixels = (Uint8 *) icon->pixels + y * icon->pitch;
3777 for (x = 0; x < icon->w; ++x) {
3778 if (*pixels++ == colorkey) {
3779 SET_MASKBIT(icon, x, y, mask);
3789 for (y = 0; y < icon->h; ++y) {
3790 pixels = (Uint16 *) icon->pixels + y * icon->pitch / 2;
3791 for (x = 0; x < icon->w; ++x) {
3792 if ((flags & 1) && *pixels == colorkey) {
3793 SET_MASKBIT(icon, x, y, mask);
3794 } else if ((flags & 2)
3795 && (*pixels & icon->format->Amask) == 0) {
3796 SET_MASKBIT(icon, x, y, mask);
3807 for (y = 0; y < icon->h; ++y) {
3808 pixels = (Uint32 *) icon->pixels + y * icon->pitch / 4;
3809 for (x = 0; x < icon->w; ++x) {
3810 if ((flags & 1) && *pixels == colorkey) {
3811 SET_MASKBIT(icon, x, y, mask);
3812 } else if ((flags & 2)
3813 && (*pixels & icon->format->Amask) == 0) {
3814 SET_MASKBIT(icon, x, y, mask);
3825 * Sets the window manager icon for the display window.
3828 SDL_WM_SetIcon(SDL_Surface * icon, Uint8 * mask)
3830 if (icon && _this->SetIcon) {
3831 /* Generate a mask if necessary, and create the icon! */
3833 int mask_len = icon->h * (icon->w + 7) / 8;
3835 mask = (Uint8 *) SDL_malloc(mask_len);
3839 SDL_memset(mask, ~0, mask_len);
3840 if (icon->flags & SDL_SRCCOLORKEY)
3842 if (icon->flags & SDL_SRCALPHA)
3845 CreateMaskFromColorKeyOrAlpha(icon, mask, flags);
3847 _this->SetIcon(_this, icon, mask);
3850 _this->SetIcon(_this, icon, mask);
3857 SDL_GetWindowWMInfo(SDL_Window * window, struct SDL_SysWMinfo *info)
3859 CHECK_WINDOW_MAGIC(window, SDL_FALSE);
3862 SDL_InvalidParamError("info");
3865 info->subsystem = SDL_SYSWM_UNKNOWN;
3867 if (!_this->GetWindowWMInfo) {
3871 return (_this->GetWindowWMInfo(_this, window, info));
3875 SDL_StartTextInput(void)
3879 /* First, enable text events */
3880 SDL_EventState(SDL_TEXTINPUT, SDL_ENABLE);
3881 SDL_EventState(SDL_TEXTEDITING, SDL_ENABLE);
3883 /* Then show the on-screen keyboard, if any */
3884 window = SDL_GetFocusWindow();
3885 if (window && _this && _this->ShowScreenKeyboard) {
3886 _this->ShowScreenKeyboard(_this, window);
3889 /* Finally start the text input system */
3890 if (_this && _this->StartTextInput) {
3891 _this->StartTextInput(_this);
3896 SDL_IsTextInputActive(void)
3898 return (SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE);
3902 SDL_StopTextInput(void)
3906 /* Stop the text input system */
3907 if (_this && _this->StopTextInput) {
3908 _this->StopTextInput(_this);
3911 /* Hide the on-screen keyboard, if any */
3912 window = SDL_GetFocusWindow();
3913 if (window && _this && _this->HideScreenKeyboard) {
3914 _this->HideScreenKeyboard(_this, window);
3917 /* Finally disable text events */
3918 SDL_EventState(SDL_TEXTINPUT, SDL_DISABLE);
3919 SDL_EventState(SDL_TEXTEDITING, SDL_DISABLE);
3923 SDL_SetTextInputRect(SDL_Rect *rect)
3925 if (_this && _this->SetTextInputRect) {
3926 _this->SetTextInputRect(_this, rect);
3931 SDL_HasScreenKeyboardSupport(void)
3933 if (_this && _this->HasScreenKeyboardSupport) {
3934 return _this->HasScreenKeyboardSupport(_this);
3940 SDL_IsScreenKeyboardShown(SDL_Window *window)
3942 if (window && _this && _this->IsScreenKeyboardShown) {
3943 return _this->IsScreenKeyboardShown(_this, window);
3948 #if SDL_VIDEO_DRIVER_ANDROID
3949 #include "android/SDL_androidmessagebox.h"
3951 #if SDL_VIDEO_DRIVER_WINDOWS
3952 #include "windows/SDL_windowsmessagebox.h"
3954 #if SDL_VIDEO_DRIVER_WINRT
3955 #include "winrt/SDL_winrtmessagebox.h"
3957 #if SDL_VIDEO_DRIVER_COCOA
3958 #include "cocoa/SDL_cocoamessagebox.h"
3960 #if SDL_VIDEO_DRIVER_UIKIT
3961 #include "uikit/SDL_uikitmessagebox.h"
3963 #if SDL_VIDEO_DRIVER_X11
3964 #include "x11/SDL_x11messagebox.h"
3966 #if SDL_VIDEO_DRIVER_HAIKU
3967 #include "haiku/SDL_bmessagebox.h"
3969 #if SDL_VIDEO_DRIVER_OS2
3970 #include "os2/SDL_os2messagebox.h"
3973 #if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT || SDL_VIDEO_DRIVER_COCOA || SDL_VIDEO_DRIVER_UIKIT || SDL_VIDEO_DRIVER_X11 || SDL_VIDEO_DRIVER_HAIKU || SDL_VIDEO_DRIVER_OS2
3974 static SDL_bool SDL_MessageboxValidForDriver(const SDL_MessageBoxData *messageboxdata, SDL_SYSWM_TYPE drivertype)
3977 SDL_Window *window = messageboxdata->window;
3983 SDL_VERSION(&info.version);
3984 if (!SDL_GetWindowWMInfo(window, &info)) {
3987 return (info.subsystem == drivertype);
3993 SDL_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
3997 SDL_bool relative_mode;
3998 int show_cursor_prev;
3999 SDL_bool mouse_captured;
4000 SDL_Window *current_window;
4001 SDL_MessageBoxData mbdata;
4003 if (!messageboxdata) {
4004 return SDL_InvalidParamError("messageboxdata");
4005 } else if (messageboxdata->numbuttons < 0) {
4006 return SDL_SetError("Invalid number of buttons");
4009 current_window = SDL_GetKeyboardFocus();
4010 mouse_captured = current_window && ((SDL_GetWindowFlags(current_window) & SDL_WINDOW_MOUSE_CAPTURE) != 0);
4011 relative_mode = SDL_GetRelativeMouseMode();
4012 SDL_CaptureMouse(SDL_FALSE);
4013 SDL_SetRelativeMouseMode(SDL_FALSE);
4014 show_cursor_prev = SDL_ShowCursor(1);
4015 SDL_ResetKeyboard();
4018 buttonid = &dummybutton;
4021 SDL_memcpy(&mbdata, messageboxdata, sizeof(*messageboxdata));
4022 if (!mbdata.title) mbdata.title = "";
4023 if (!mbdata.message) mbdata.message = "";
4024 messageboxdata = &mbdata;
4026 if (_this && _this->ShowMessageBox) {
4027 retval = _this->ShowMessageBox(_this, messageboxdata, buttonid);
4030 /* It's completely fine to call this function before video is initialized */
4031 #if SDL_VIDEO_DRIVER_ANDROID
4033 Android_ShowMessageBox(messageboxdata, buttonid) == 0) {
4037 #if SDL_VIDEO_DRIVER_WINDOWS
4039 SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_WINDOWS) &&
4040 WIN_ShowMessageBox(messageboxdata, buttonid) == 0) {
4044 #if SDL_VIDEO_DRIVER_WINRT
4046 SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_WINRT) &&
4047 WINRT_ShowMessageBox(messageboxdata, buttonid) == 0) {
4051 #if SDL_VIDEO_DRIVER_COCOA
4053 SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_COCOA) &&
4054 Cocoa_ShowMessageBox(messageboxdata, buttonid) == 0) {
4058 #if SDL_VIDEO_DRIVER_UIKIT
4060 SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_UIKIT) &&
4061 UIKit_ShowMessageBox(messageboxdata, buttonid) == 0) {
4065 #if SDL_VIDEO_DRIVER_X11
4067 SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_X11) &&
4068 X11_ShowMessageBox(messageboxdata, buttonid) == 0) {
4072 #if SDL_VIDEO_DRIVER_HAIKU
4074 SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_HAIKU) &&
4075 HAIKU_ShowMessageBox(messageboxdata, buttonid) == 0) {
4079 #if SDL_VIDEO_DRIVER_OS2
4081 SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_OS2) &&
4082 OS2_ShowMessageBox(messageboxdata, buttonid) == 0) {
4087 SDL_SetError("No message system available");
4090 if (current_window) {
4091 SDL_RaiseWindow(current_window);
4092 if (mouse_captured) {
4093 SDL_CaptureMouse(SDL_TRUE);
4097 SDL_ShowCursor(show_cursor_prev);
4098 SDL_SetRelativeMouseMode(relative_mode);
4104 SDL_ShowSimpleMessageBox(Uint32 flags, const char *title, const char *message, SDL_Window *window)
4106 #ifdef __EMSCRIPTEN__
4107 /* !!! FIXME: propose a browser API for this, get this #ifdef out of here? */
4108 /* Web browsers don't (currently) have an API for a custom message box
4109 that can block, but for the most common case (SDL_ShowSimpleMessageBox),
4110 we can use the standard Javascript alert() function. */
4111 if (!title) title = "";
4112 if (!message) message = "";
4114 alert(UTF8ToString($0) + "\n\n" + UTF8ToString($1));
4118 SDL_MessageBoxData data;
4119 SDL_MessageBoxButtonData button;
4124 data.message = message;
4125 data.numbuttons = 1;
4126 data.buttons = &button;
4127 data.window = window;
4130 button.flags |= SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
4131 button.flags |= SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
4134 return SDL_ShowMessageBox(&data, NULL);
4139 SDL_ShouldAllowTopmost(void)
4141 return SDL_GetHintBoolean(SDL_HINT_ALLOW_TOPMOST, SDL_TRUE);
4145 SDL_SetWindowHitTest(SDL_Window * window, SDL_HitTest callback, void *userdata)
4147 CHECK_WINDOW_MAGIC(window, -1);
4149 if (!_this->SetWindowHitTest) {
4150 return SDL_Unsupported();
4151 } else if (_this->SetWindowHitTest(window, callback != NULL) == -1) {
4155 window->hit_test = callback;
4156 window->hit_test_data = userdata;
4162 SDL_ComputeDiagonalDPI(int hpix, int vpix, float hinches, float vinches)
4164 float den2 = hinches * hinches + vinches * vinches;
4169 return (float)(SDL_sqrt((double)hpix * (double)hpix + (double)vpix * (double)vpix) /
4170 SDL_sqrt((double)den2));
4174 * Functions used by iOS application delegates
4176 void SDL_OnApplicationWillTerminate(void)
4178 SDL_SendAppEvent(SDL_APP_TERMINATING);
4181 void SDL_OnApplicationDidReceiveMemoryWarning(void)
4183 SDL_SendAppEvent(SDL_APP_LOWMEMORY);
4186 void SDL_OnApplicationWillResignActive(void)
4190 for (window = _this->windows; window != NULL; window = window->next) {
4191 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
4192 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
4195 SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND);
4198 void SDL_OnApplicationDidEnterBackground(void)
4200 SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND);
4203 void SDL_OnApplicationWillEnterForeground(void)
4205 SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
4208 void SDL_OnApplicationDidBecomeActive(void)
4210 SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
4214 for (window = _this->windows; window != NULL; window = window->next) {
4215 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
4216 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0);
4221 #define NOT_A_VULKAN_WINDOW "The specified window isn't a Vulkan window"
4223 int SDL_Vulkan_LoadLibrary(const char *path)
4227 SDL_UninitializedVideo();
4230 if (_this->vulkan_config.loader_loaded) {
4231 if (path && SDL_strcmp(path, _this->vulkan_config.loader_path) != 0) {
4232 return SDL_SetError("Vulkan loader library already loaded");
4236 if (!_this->Vulkan_LoadLibrary) {
4237 return SDL_SetError("Vulkan support is either not configured in SDL "
4238 "or not available in current SDL video driver "
4239 "(%s) or platform", _this->name);
4241 retval = _this->Vulkan_LoadLibrary(_this, path);
4244 _this->vulkan_config.loader_loaded++;
4249 void *SDL_Vulkan_GetVkGetInstanceProcAddr(void)
4252 SDL_UninitializedVideo();
4255 if (!_this->vulkan_config.loader_loaded) {
4256 SDL_SetError("No Vulkan loader has been loaded");
4259 return _this->vulkan_config.vkGetInstanceProcAddr;
4262 void SDL_Vulkan_UnloadLibrary(void)
4265 SDL_UninitializedVideo();
4268 if (_this->vulkan_config.loader_loaded > 0) {
4269 if (--_this->vulkan_config.loader_loaded > 0) {
4272 if (_this->Vulkan_UnloadLibrary) {
4273 _this->Vulkan_UnloadLibrary(_this);
4278 SDL_bool SDL_Vulkan_GetInstanceExtensions(SDL_Window *window, unsigned *count, const char **names)
4281 CHECK_WINDOW_MAGIC(window, SDL_FALSE);
4283 if (!(window->flags & SDL_WINDOW_VULKAN))
4285 SDL_SetError(NOT_A_VULKAN_WINDOW);
4291 SDL_InvalidParamError("count");
4295 return _this->Vulkan_GetInstanceExtensions(_this, window, count, names);
4298 SDL_bool SDL_Vulkan_CreateSurface(SDL_Window *window,
4299 VkInstance instance,
4300 VkSurfaceKHR *surface)
4302 CHECK_WINDOW_MAGIC(window, SDL_FALSE);
4304 if (!(window->flags & SDL_WINDOW_VULKAN)) {
4305 SDL_SetError(NOT_A_VULKAN_WINDOW);
4310 SDL_InvalidParamError("instance");
4315 SDL_InvalidParamError("surface");
4319 return _this->Vulkan_CreateSurface(_this, window, instance, surface);
4322 void SDL_Vulkan_GetDrawableSize(SDL_Window * window, int *w, int *h)
4324 CHECK_WINDOW_MAGIC(window,);
4326 if (_this->Vulkan_GetDrawableSize) {
4327 _this->Vulkan_GetDrawableSize(_this, window, w, h);
4329 SDL_GetWindowSize(window, w, h);
4334 SDL_Metal_CreateView(SDL_Window * window)
4336 CHECK_WINDOW_MAGIC(window, NULL);
4338 if (!(window->flags & SDL_WINDOW_METAL)) {
4339 SDL_SetError("The specified window isn't a Metal window");
4343 if (_this->Metal_CreateView) {
4344 return _this->Metal_CreateView(_this, window);
4346 SDL_SetError("Metal is not supported.");
4352 SDL_Metal_DestroyView(SDL_MetalView view)
4354 if (_this && view && _this->Metal_DestroyView) {
4355 _this->Metal_DestroyView(_this, view);
4360 SDL_Metal_GetLayer(SDL_MetalView view)
4362 if (_this && _this->Metal_GetLayer) {
4364 return _this->Metal_GetLayer(_this, view);
4366 SDL_InvalidParamError("view");
4370 SDL_SetError("Metal is not supported.");
4375 void SDL_Metal_GetDrawableSize(SDL_Window * window, int *w, int *h)
4377 CHECK_WINDOW_MAGIC(window,);
4379 if (_this->Metal_GetDrawableSize) {
4380 _this->Metal_GetDrawableSize(_this, window, w, h);
4382 SDL_GetWindowSize(window, w, h);
4386 /* vi: set ts=4 sw=4 expandtab: */