12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538 |
- /**
- * Copyright (c) 2006-2024 LOVE Development Team
- *
- * This software is provided 'as-is', without any express or implied
- * warranty. In no event will the authors be held liable for any damages
- * arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not
- * claim that you wrote the original software. If you use this software
- * in a product, an acknowledgment in the product documentation would be
- * appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- * misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- **/
- // LOVE
- #include "common/config.h"
- #include "graphics/Graphics.h"
- #ifdef LOVE_GRAPHICS_VULKAN
- # include "graphics/vulkan/Graphics.h"
- # include "graphics/vulkan/Vulkan.h"
- #endif
- #include "Window.h"
- #ifdef LOVE_ANDROID
- #include "common/android.h"
- #endif
- #ifdef LOVE_IOS
- #include "common/ios.h"
- #endif
- // C++
- #include <iostream>
- #include <vector>
- #include <algorithm>
- // C
- #include <cstdio>
- #ifdef LOVE_GRAPHICS_VULKAN
- #include <SDL3/SDL_vulkan.h>
- #endif
- #if defined(LOVE_WINDOWS)
- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
- #include <dwmapi.h>
- #include <VersionHelpers.h>
- #elif defined(LOVE_MACOS)
- #include "common/macos.h"
- #endif
- #ifndef APIENTRY
- #define APIENTRY
- #endif
- #ifndef SDL_HINT_WINDOWS_DPI_SCALING
- #define SDL_HINT_WINDOWS_DPI_SCALING "SDL_WINDOWS_DPI_SCALING"
- #endif
- namespace love
- {
- namespace window
- {
- // See src/modules/window/Window.cpp.
- void setHighDPIAllowedImplementation(bool enable)
- {
- #if defined(LOVE_WINDOWS)
- // Windows uses a different API than SDL_WINDOW_ALLOW_HIGHDPI.
- // This must be set before the video subsystem is initialized.
- // FIXME: How does this work in SDL3?
- SDL_SetHint(SDL_HINT_WINDOWS_DPI_SCALING, enable ? "1" : "0");
- #else
- LOVE_UNUSED(enable);
- #endif
- }
- namespace sdl
- {
- Window::Window()
- : love::window::Window("love.window.sdl")
- , open(false)
- , mouseGrabbed(false)
- , window(nullptr)
- , glcontext(nullptr)
- #ifdef LOVE_GRAPHICS_METAL
- , metalView(nullptr)
- #endif
- , displayedWindowError(false)
- , contextAttribs()
- {
- if (!SDL_InitSubSystem(SDL_INIT_VIDEO))
- throw love::Exception("Could not initialize SDL video subsystem (%s)", SDL_GetError());
- // Make sure the screensaver doesn't activate by default.
- setDisplaySleepEnabled(false);
- #ifdef LOVE_WINDOWS
- // Turned off by default, because it (ironically) causes stuttering issues
- // on some setups. More investigation is needed before enabling it.
- canUseDwmFlush = SDL_GetHintBoolean("LOVE_GRAPHICS_VSYNC_DWM", false);
- #endif
- }
- Window::~Window()
- {
- close(false);
- graphics.set(nullptr);
- SDL_QuitSubSystem(SDL_INIT_VIDEO);
- }
- void Window::setGraphics(graphics::Graphics *graphics)
- {
- this->graphics.set(graphics);
- }
- void Window::setGLFramebufferAttributes(bool sRGB)
- {
- // Set GL window / framebuffer attributes.
- SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
- SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
- SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
- SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
- SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
- SDL_GL_SetAttribute(SDL_GL_RETAINED_BACKING, 0);
- // Always use 24/8 depth/stencil.
- // Changing this after initial window creation would need the context to be
- // destroyed and recreated, which we really don't want.
- SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
- SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
- // Backbuffer MSAA is handled by the love.graphics implementation.
- SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
- SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
- SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, sRGB ? 1 : 0);
- #if defined(LOVE_WINDOWS)
- // Avoid the Microsoft OpenGL 1.1 software renderer on Windows. Apparently
- // older Intel drivers like to use it as a fallback when requesting some
- // unsupported framebuffer attribute values, rather than properly failing.
- SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
- #endif
- }
- void Window::setGLContextAttributes(const ContextAttribs &attribs)
- {
- int profilemask = 0;
- int contextflags = 0;
- if (attribs.gles)
- profilemask = SDL_GL_CONTEXT_PROFILE_ES;
- else if (attribs.versionMajor * 10 + attribs.versionMinor >= 32)
- profilemask |= SDL_GL_CONTEXT_PROFILE_CORE;
- else if (attribs.debug)
- profilemask = SDL_GL_CONTEXT_PROFILE_COMPATIBILITY;
- if (attribs.debug)
- contextflags |= SDL_GL_CONTEXT_DEBUG_FLAG;
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, attribs.versionMajor);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, attribs.versionMinor);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profilemask);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, contextflags);
- }
- bool Window::checkGLVersion(const ContextAttribs &attribs, std::string &outversion)
- {
- typedef unsigned char GLubyte;
- typedef unsigned int GLenum;
- typedef const GLubyte *(APIENTRY *glGetStringPtr)(GLenum name);
- const GLenum GL_VENDOR_ENUM = 0x1F00;
- const GLenum GL_RENDERER_ENUM = 0x1F01;
- const GLenum GL_VERSION_ENUM = 0x1F02;
- // We don't have OpenGL headers or an automatic OpenGL function loader in
- // this module, so we have to get the glGetString function pointer ourselves.
- glGetStringPtr glGetStringFunc = (glGetStringPtr) SDL_GL_GetProcAddress("glGetString");
- if (!glGetStringFunc)
- return false;
- const char *glversion = (const char *) glGetStringFunc(GL_VERSION_ENUM);
- if (!glversion)
- return false;
- outversion = glversion;
- const char *glrenderer = (const char *) glGetStringFunc(GL_RENDERER_ENUM);
- if (glrenderer)
- outversion += " - " + std::string(glrenderer);
- const char *glvendor = (const char *) glGetStringFunc(GL_VENDOR_ENUM);
- if (glvendor)
- outversion += " (" + std::string(glvendor) + ")";
- int glmajor = 0;
- int glminor = 0;
- // glGetString(GL_VERSION) returns a string with the format "major.minor",
- // or "OpenGL ES major.minor" in GLES contexts.
- const char *format = "%d.%d";
- if (attribs.gles)
- format = "OpenGL ES %d.%d";
- if (sscanf(glversion, format, &glmajor, &glminor) != 2)
- return false;
- if (glmajor < attribs.versionMajor
- || (glmajor == attribs.versionMajor && glminor < attribs.versionMinor))
- return false;
- return true;
- }
- std::vector<Window::ContextAttribs> Window::getContextAttribsList() const
- {
- // If we already have a set of context attributes that we know work, just
- // return that. love.graphics doesn't really support switching GL versions
- // after the first initialization.
- if (contextAttribs.versionMajor > 0)
- return std::vector<ContextAttribs>{contextAttribs};
- bool preferGLES = false;
- #ifdef LOVE_GRAPHICS_USE_OPENGLES
- preferGLES = true;
- #endif
- const char *curdriver = SDL_GetCurrentVideoDriver();
- const char *glesdrivers[] = {"RPI", "Android", "uikit", "winrt", "emscripten"};
- // We always want to try OpenGL ES first on certain video backends.
- for (const char *glesdriver : glesdrivers)
- {
- if (curdriver && strstr(curdriver, glesdriver) == curdriver)
- {
- preferGLES = true;
- break;
- }
- }
- const char *gleshint = SDL_GetHint("LOVE_GRAPHICS_USE_OPENGLES");
- if (gleshint != nullptr)
- preferGLES = (gleshint != nullptr && gleshint[0] != '0');
- // Do we want a debug context?
- bool debug = love::graphics::isDebugEnabled();
- const char *preferGL3hint = SDL_GetHint("LOVE_GRAPHICS_USE_GL3");
- bool preferGL3 = (preferGL3hint != nullptr && preferGL3hint[0] != '0');
- std::vector<ContextAttribs> glcontexts =
- {
- {4, 3, false, debug},
- {3, 3, false, debug},
- };
- std::vector<ContextAttribs> glescontexts =
- {
- {3, 2, true, debug},
- {3, 0, true, debug},
- };
- if (preferGL3)
- {
- std::swap(glcontexts[0], glcontexts[1]);
- std::swap(glescontexts[0], glescontexts[1]);
- }
- std::vector<ContextAttribs> attribslist;
- if (preferGLES)
- {
- attribslist.insert(attribslist.end(), glescontexts.begin(), glescontexts.end());
- attribslist.insert(attribslist.end(), glcontexts.begin(), glcontexts.end());
- }
- else
- {
- attribslist.insert(attribslist.end(), glcontexts.begin(), glcontexts.end());
- attribslist.insert(attribslist.end(), glescontexts.begin(), glescontexts.end());
- }
- return attribslist;
- }
- bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, graphics::Renderer renderer)
- {
- bool needsglcontext = (windowflags & SDL_WINDOW_OPENGL) != 0;
- #ifdef LOVE_GRAPHICS_METAL
- bool needsmetalview = (windowflags & SDL_WINDOW_METAL) != 0;
- #endif
- std::string windowerror;
- std::string contexterror;
- std::string glversion;
- // Unfortunately some OpenGL context settings are part of the internal
- // window state in the Windows and Linux SDL backends, so we have to
- // recreate the window when we want to change those settings...
- // Also, apparently some Intel drivers on Windows give back a Microsoft
- // OpenGL 1.1 software renderer context when high MSAA values are requested!
- const auto create = [&](const ContextAttribs *attribs) -> bool
- {
- if (glcontext)
- {
- SDL_GL_DestroyContext(glcontext);
- glcontext = nullptr;
- }
- #ifdef LOVE_GRAPHICS_METAL
- if (metalView)
- {
- SDL_Metal_DestroyView(metalView);
- metalView = nullptr;
- }
- #endif
- if (window)
- {
- SDL_DestroyWindow(window);
- SDL_FlushEvents(SDL_EVENT_WINDOW_FIRST, SDL_EVENT_WINDOW_LAST);
- window = nullptr;
- }
- window = SDL_CreateWindow(title.c_str(), w, h, windowflags);
- if (!window)
- {
- windowerror = std::string(SDL_GetError());
- return false;
- }
- SDL_SetWindowPosition(window, x, y);
- if (attribs != nullptr && renderer == love::graphics::Renderer::RENDERER_OPENGL)
- {
- #ifdef LOVE_MACOS
- love::macos::setWindowSRGBColorSpace(window);
- #endif
- glcontext = SDL_GL_CreateContext(window);
- if (!glcontext)
- contexterror = std::string(SDL_GetError());
- // Make sure the context's version is at least what we requested.
- if (glcontext && !checkGLVersion(*attribs, glversion))
- {
- SDL_GL_DestroyContext(glcontext);
- glcontext = nullptr;
- }
- if (!glcontext)
- {
- SDL_DestroyWindow(window);
- window = nullptr;
- return false;
- }
- }
- return true;
- };
- if (renderer == graphics::RENDERER_OPENGL)
- {
- std::vector<ContextAttribs> attribslist = getContextAttribsList();
- // Try each context profile in order.
- for (ContextAttribs attribs : attribslist)
- {
- bool curSRGB = love::graphics::isGammaCorrect();
- setGLFramebufferAttributes(curSRGB);
- setGLContextAttributes(attribs);
- windowerror.clear();
- contexterror.clear();
- create(&attribs);
- if (!window && curSRGB)
- {
- // The sRGB setting could have caused the failure.
- setGLFramebufferAttributes(false);
- if (create(&attribs))
- curSRGB = false;
- }
- if (window && glcontext)
- {
- // Store the successful context attributes so we can re-use them in
- // subsequent calls to createWindowAndContext.
- contextAttribs = attribs;
- love::graphics::setGammaCorrect(curSRGB);
- break;
- }
- }
- }
- #ifdef LOVE_GRAPHICS_METAL
- else if (renderer == graphics::RENDERER_METAL)
- {
- if (create(nullptr) && window != nullptr)
- metalView = SDL_Metal_CreateView(window);
- if (metalView == nullptr && window != nullptr)
- {
- contexterror = SDL_GetError();
- SDL_DestroyWindow(window);
- window = nullptr;
- }
- }
- #endif
- else
- {
- create(nullptr);
- }
- bool failed = window == nullptr;
- failed |= (needsglcontext && !glcontext);
- #ifdef LOVE_GRAPHICS_METAL
- failed |= (needsmetalview && !metalView);
- #endif
- if (failed)
- {
- std::string title = "Unable to create renderer";
- std::string message = "This program requires a graphics card and video drivers which support OpenGL 3.3 or OpenGL ES 3.0.";
- if (!glversion.empty())
- message += "\n\nDetected OpenGL version:\n" + glversion;
- else if (!contexterror.empty())
- message += "\n\nRenderer context creation error: " + contexterror;
- else if (!windowerror.empty())
- message += "\n\nSDL window creation error: " + windowerror;
- std::cerr << title << std::endl << message << std::endl;
- // Display a message box with the error, but only once.
- if (!displayedWindowError)
- {
- showMessageBox(title, message, MESSAGEBOX_ERROR, false);
- displayedWindowError = true;
- }
- close();
- return false;
- }
- open = true;
- return true;
- }
- struct SDLDisplayIDs
- {
- SDLDisplayIDs()
- {
- ids = SDL_GetDisplays(&count);
- }
- ~SDLDisplayIDs()
- {
- if (ids)
- SDL_free(ids);
- }
- int count = 0;
- SDL_DisplayID *ids = nullptr;
- };
- static SDL_DisplayID GetSDLDisplayIDForIndex(int displayindex)
- {
- SDLDisplayIDs displayids;
- if (displayindex < 0 || displayindex >= displayids.count)
- return (SDL_DisplayID) 0;
- return displayids.ids[displayindex];
- }
- bool Window::setWindow(int width, int height, WindowSettings *settings)
- {
- if (!graphics.get())
- graphics.set(Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS));
- if (graphics.get() && graphics->isRenderTargetActive())
- throw love::Exception("love.window.setMode cannot be called while a render target is active in love.graphics.");
- auto renderer = graphics != nullptr ? graphics->getRenderer() : graphics::RENDERER_NONE;
- if (isOpen())
- updateSettings(this->settings, false);
- WindowSettings f;
- if (settings)
- f = *settings;
- f.minwidth = std::max(f.minwidth, 1);
- f.minheight = std::max(f.minheight, 1);
- SDLDisplayIDs displays;
- int displaycount = displays.count;
- f.displayindex = std::min(std::max(f.displayindex, 0), displaycount - 1);
- // Use the desktop resolution if a width or height of 0 is specified.
- if (width == 0 || height == 0)
- {
- const SDL_DisplayMode *mode = SDL_GetDesktopDisplayMode(displays.ids[f.displayindex]);
- width = mode->w;
- height = mode->h;
- }
- // On Android, disable fullscreen first on window creation so it's
- // possible to change the orientation by specifying portait width and
- // height, otherwise SDL will pick the current orientation dimensions when
- // fullscreen flag is set. Don't worry, we'll set it back later when user
- // also requested fullscreen after the window is created.
- // See https://github.com/love2d/love-android/issues/196
- #ifdef LOVE_ANDROID
- bool fullscreen = f.fullscreen;
- f.fullscreen = false;
- f.fstype = FULLSCREEN_DESKTOP;
- #endif
- int x = f.x;
- int y = f.y;
- if (f.useposition)
- {
- // The position needs to be in the global coordinate space.
- SDL_Rect displaybounds = {};
- SDL_GetDisplayBounds(f.displayindex, &displaybounds);
- x += displaybounds.x;
- y += displaybounds.y;
- }
- else
- {
- if (f.centered)
- x = y = SDL_WINDOWPOS_CENTERED_DISPLAY(f.displayindex);
- else
- x = y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(f.displayindex);
- }
- Uint32 sdlflags = 0;
- SDL_DisplayMode fsmode = {};
- if (f.fullscreen)
- {
- sdlflags |= SDL_WINDOW_FULLSCREEN;
- if (f.fstype == FULLSCREEN_EXCLUSIVE)
- {
- SDL_DisplayID display = displays.ids[f.displayindex];
- if (!SDL_GetClosestFullscreenDisplayMode(display, width, height, 0, isHighDPIAllowed(), &fsmode))
- {
- // GetClosestDisplayMode will fail if we request a size larger
- // than the largest available display mode, so we'll try to use
- // the largest (first) mode in that case.
- int modecount = 0;
- SDL_DisplayMode **modes = SDL_GetFullscreenDisplayModes(display, &modecount);
- if (modecount > 0)
- fsmode = *modes[0];
- SDL_free(modes);
- if (fsmode.w == 0 || fsmode.h == 0)
- return false;
- }
- }
- }
- bool needsetmode = false;
- if (renderer != windowRenderer && isOpen())
- close();
- if (isOpen())
- {
- if (fsmode.w > 0 && fsmode.h > 0)
- SDL_SetWindowFullscreenMode(window, &fsmode);
- else
- SDL_SetWindowFullscreenMode(window, nullptr);
- if (SDL_SetWindowFullscreen(window, (sdlflags & SDL_WINDOW_FULLSCREEN) != 0) && renderer == graphics::RENDERER_OPENGL)
- SDL_GL_MakeCurrent(window, glcontext);
- // TODO: should we make this conditional, to avoid love.resize events when the size doesn't change?
- SDL_SetWindowSize(window, width, height);
- if (this->settings.resizable != f.resizable)
- SDL_SetWindowResizable(window, f.resizable);
- if (this->settings.borderless != f.borderless)
- SDL_SetWindowBordered(window, !f.borderless);
- }
- else
- {
- if (renderer == graphics::RENDERER_OPENGL)
- sdlflags |= SDL_WINDOW_OPENGL;
- #ifdef LOVE_GRAPHICS_METAL
- if (renderer == graphics::RENDERER_METAL)
- sdlflags |= SDL_WINDOW_METAL;
- #endif
- if (renderer == graphics::RENDERER_VULKAN)
- sdlflags |= SDL_WINDOW_VULKAN;
- if (f.resizable)
- sdlflags |= SDL_WINDOW_RESIZABLE;
- if (f.borderless)
- sdlflags |= SDL_WINDOW_BORDERLESS;
- // Note: this flag is ignored on Windows.
- if (isHighDPIAllowed())
- sdlflags |= SDL_WINDOW_HIGH_PIXEL_DENSITY;
- Uint32 createflags = sdlflags & (~SDL_WINDOW_FULLSCREEN);
- if (!createWindowAndContext(x, y, width, height, createflags, renderer))
- return false;
- if (f.fullscreen)
- {
- if (fsmode.w > 0 && fsmode.h > 0)
- SDL_SetWindowFullscreenMode(window, &fsmode);
- else
- SDL_SetWindowFullscreenMode(window, nullptr);
- SDL_SetWindowFullscreen(window, true);
- }
- needsetmode = true;
- }
- // Make sure the window keeps any previously set icon.
- setIcon(icon.get());
- // Make sure the mouse keeps its previous grab setting.
- setMouseGrab(mouseGrabbed);
- // Enforce minimum window dimensions.
- SDL_SetWindowMinimumSize(window, f.minwidth, f.minheight);
- if (this->settings.displayindex != f.displayindex || f.useposition || f.centered)
- SDL_SetWindowPosition(window, x, y);
- SDL_RaiseWindow(window);
- setVSync(f.vsync);
- updateSettings(f, false);
- windowRenderer = renderer;
- if (graphics.get())
- {
- double scaledw, scaledh;
- fromPixels((double) pixelWidth, (double) pixelHeight, scaledw, scaledh);
- if (needsetmode)
- {
- void *context = nullptr;
- if (renderer == graphics::RENDERER_OPENGL)
- context = (void *) glcontext;
- #ifdef LOVE_GRAPHICS_METAL
- if (renderer == graphics::RENDERER_METAL && metalView)
- context = (void *) SDL_Metal_GetLayer(metalView);
- #endif
- // TODO: try/catch
- graphics->setMode(context, (int) scaledw, (int) scaledh, pixelWidth, pixelHeight, f.stencil, f.depth, f.msaa);
- }
- else
- {
- graphics->backbufferChanged((int) scaledw, (int) scaledh, pixelWidth, pixelHeight, f.stencil, f.depth, f.msaa);
- }
- this->settings.msaa = graphics->getBackbufferMSAA();
- }
- // Set fullscreen when user requested it before.
- // See above for explanation.
- #ifdef LOVE_ANDROID
- setFullscreen(fullscreen);
- love::android::setImmersive(fullscreen);
- #endif
- SDL_SyncWindow(window);
- return true;
- }
- bool Window::onSizeChanged(int width, int height)
- {
- if (!window)
- return false;
- SDL_GetWindowSize(window, &windowWidth, &windowHeight);
- if (!SDL_GetWindowSizeInPixels(window, &pixelWidth, &pixelHeight))
- {
- pixelWidth = width;
- pixelHeight = height;
- }
- if (graphics.get())
- {
- double scaledw, scaledh;
- fromPixels((double) pixelWidth, (double) pixelHeight, scaledw, scaledh);
- graphics->backbufferChanged((int) scaledw, (int) scaledh, pixelWidth, pixelHeight);
- }
- return true;
- }
- void Window::updateSettings(const WindowSettings &newsettings, bool updateGraphicsViewport)
- {
- SDL_SyncWindow(window);
- Uint32 wflags = SDL_GetWindowFlags(window);
- // Set the new display mode as the current display mode.
- SDL_GetWindowSize(window, &windowWidth, &windowHeight);
- pixelWidth = windowWidth;
- pixelHeight = windowHeight;
- SDL_GetWindowSizeInPixels(window, &pixelWidth, &pixelHeight);
- if (((wflags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN) && SDL_GetWindowFullscreenMode(window) == nullptr)
- {
- settings.fullscreen = true;
- settings.fstype = FULLSCREEN_DESKTOP;
- }
- else if ((wflags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)
- {
- settings.fullscreen = true;
- settings.fstype = FULLSCREEN_EXCLUSIVE;
- }
- else
- {
- settings.fullscreen = false;
- settings.fstype = newsettings.fstype;
- }
- #ifdef LOVE_ANDROID
- settings.fullscreen = love::android::getImmersive();
- #endif
- // SDL_GetWindowMinimumSize gives back 0,0 sometimes...
- settings.minwidth = newsettings.minwidth;
- settings.minheight = newsettings.minheight;
- settings.resizable = (wflags & SDL_WINDOW_RESIZABLE) != 0;
- settings.borderless = (wflags & SDL_WINDOW_BORDERLESS) != 0;
- settings.centered = newsettings.centered;
- getPosition(settings.x, settings.y, settings.displayindex);
- setHighDPIAllowed((wflags & SDL_WINDOW_HIGH_PIXEL_DENSITY) != 0);
- settings.usedpiscale = newsettings.usedpiscale;
- // Only minimize on focus loss if the window is in exclusive-fullscreen mode
- if (settings.fullscreen && settings.fstype == FULLSCREEN_EXCLUSIVE)
- SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "1");
- else
- SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
- settings.vsync = getVSync();
- settings.stencil = newsettings.stencil;
- settings.depth = newsettings.depth;
- SDLDisplayIDs displayids;
- const SDL_DisplayMode *dmode = SDL_GetCurrentDisplayMode(displayids.ids[settings.displayindex]);
- // May be 0 if the refresh rate can't be determined.
- settings.refreshrate = dmode->refresh_rate;
- // Update the viewport size now instead of waiting for event polling.
- if (updateGraphicsViewport && graphics.get())
- {
- double scaledw, scaledh;
- fromPixels((double) pixelWidth, (double) pixelHeight, scaledw, scaledh);
- graphics->backbufferChanged((int) scaledw, (int) scaledh, pixelWidth, pixelHeight);
- }
- }
- void Window::getWindow(int &width, int &height, WindowSettings &newsettings)
- {
- // The window might have been modified (moved, resized, etc.) by the user.
- if (window)
- updateSettings(settings, true);
- width = windowWidth;
- height = windowHeight;
- newsettings = settings;
- }
- void Window::close()
- {
- close(true);
- }
- void Window::close(bool allowExceptions)
- {
- if (graphics.get())
- {
- if (allowExceptions && graphics->isRenderTargetActive())
- throw love::Exception("love.window.close cannot be called while a render target is active in love.graphics.");
- graphics->unSetMode();
- }
- if (glcontext)
- {
- SDL_GL_DestroyContext(glcontext);
- glcontext = nullptr;
- }
- #ifdef LOVE_GRAPHICS_METAL
- if (metalView)
- {
- SDL_Metal_DestroyView(metalView);
- metalView = nullptr;
- }
- #endif
- if (window)
- {
- SDL_DestroyWindow(window);
- window = nullptr;
- // The old window may have generated pending events which are no longer
- // relevant. Destroy them all!
- SDL_FlushEvents(SDL_EVENT_WINDOW_FIRST, SDL_EVENT_WINDOW_LAST);
- }
- open = false;
- }
- bool Window::setFullscreen(bool fullscreen, FullscreenType fstype)
- {
- if (!window)
- return false;
- if (graphics.get() && graphics->isRenderTargetActive())
- throw love::Exception("love.window.setFullscreen cannot be called while a render target is active in love.graphics.");
- WindowSettings newsettings = settings;
- newsettings.fullscreen = fullscreen;
- newsettings.fstype = fstype;
- bool sdlflags = fullscreen;
- if (fullscreen)
- {
- if (fstype == FULLSCREEN_DESKTOP)
- SDL_SetWindowFullscreenMode(window, nullptr);
- else
- {
- SDL_DisplayID displayid = SDL_GetDisplayForWindow(window);
- SDL_DisplayMode mode = {};
- if (SDL_GetClosestFullscreenDisplayMode(displayid, windowWidth, windowHeight, 0, isHighDPIAllowed(), &mode))
- SDL_SetWindowFullscreenMode(window, &mode);
- }
- }
- #ifdef LOVE_ANDROID
- love::android::setImmersive(fullscreen);
- #endif
- if (SDL_SetWindowFullscreen(window, sdlflags))
- {
- if (glcontext)
- SDL_GL_MakeCurrent(window, glcontext);
- updateSettings(newsettings, true);
- return true;
- }
- return false;
- }
- bool Window::setFullscreen(bool fullscreen)
- {
- return setFullscreen(fullscreen, settings.fstype);
- }
- int Window::getDisplayCount() const
- {
- SDLDisplayIDs displayids;
- return displayids.count;
- }
- const char *Window::getDisplayName(int displayindex) const
- {
- const char *name = SDL_GetDisplayName(GetSDLDisplayIDForIndex(displayindex));
- if (name == nullptr)
- throw love::Exception("Invalid display index: %d", displayindex + 1);
- return name;
- }
- Window::DisplayOrientation Window::getDisplayOrientation(int displayindex) const
- {
- switch (SDL_GetCurrentDisplayOrientation(GetSDLDisplayIDForIndex(displayindex)))
- {
- case SDL_ORIENTATION_UNKNOWN: return ORIENTATION_UNKNOWN;
- case SDL_ORIENTATION_LANDSCAPE: return ORIENTATION_LANDSCAPE;
- case SDL_ORIENTATION_LANDSCAPE_FLIPPED: return ORIENTATION_LANDSCAPE_FLIPPED;
- case SDL_ORIENTATION_PORTRAIT: return ORIENTATION_PORTRAIT;
- case SDL_ORIENTATION_PORTRAIT_FLIPPED: return ORIENTATION_PORTRAIT_FLIPPED;
- }
- return ORIENTATION_UNKNOWN;
- }
- std::vector<Window::WindowSize> Window::getFullscreenSizes(int displayindex) const
- {
- std::vector<WindowSize> sizes;
- int count = 0;
- SDL_DisplayMode **modes = SDL_GetFullscreenDisplayModes(GetSDLDisplayIDForIndex(displayindex), &count);
- for (int i = 0; i < count; i++)
- {
- // TODO: other mode properties?
- WindowSize w = {modes[i]->w, modes[i]->h};
- // SDL2's display mode list has multiple entries for modes of the same
- // size with different bits per pixel, so we need to filter those out.
- if (std::find(sizes.begin(), sizes.end(), w) == sizes.end())
- sizes.push_back(w);
- }
- SDL_free(modes);
- return sizes;
- }
- void Window::getDesktopDimensions(int displayindex, int &width, int &height) const
- {
- const SDL_DisplayMode *mode = SDL_GetDesktopDisplayMode(GetSDLDisplayIDForIndex(displayindex));
- if (mode != nullptr)
- {
- // TODO: other properties?
- width = mode->w;
- height = mode->h;
- }
- else
- {
- width = 0;
- height = 0;
- }
- }
- void Window::setPosition(int x, int y, int displayindex)
- {
- if (!window)
- return;
- displayindex = std::min(std::max(displayindex, 0), getDisplayCount() - 1);
- SDL_Rect displaybounds = {};
- SDL_GetDisplayBounds(displayindex, &displaybounds);
- // The position needs to be in the global coordinate space.
- x += displaybounds.x;
- y += displaybounds.y;
- SDL_SetWindowPosition(window, x, y);
- SDL_SyncWindow(window);
- settings.useposition = true;
- }
- void Window::getPosition(int &x, int &y, int &displayindex)
- {
- if (!window)
- {
- x = y = 0;
- displayindex = 0;
- return;
- }
- SDL_DisplayID displayid = SDL_GetDisplayForWindow(window);
- SDLDisplayIDs displayids;
- displayindex = 0;
- for (int i = 0; i < displayids.count; i++)
- {
- if (displayids.ids[i] == displayid)
- {
- displayindex = i;
- break;
- }
- }
- SDL_GetWindowPosition(window, &x, &y);
- // In SDL <= 2.0.3, fullscreen windows are always reported as 0,0. In every
- // other case we need to convert the position from global coordinates to the
- // monitor's coordinate space.
- if (x != 0 || y != 0)
- {
- SDL_Rect displaybounds = {};
- SDL_GetDisplayBounds(displayid, &displaybounds);
- x -= displaybounds.x;
- y -= displaybounds.y;
- }
- }
- Rect Window::getSafeArea() const
- {
- #if defined(LOVE_IOS)
- if (window != nullptr)
- return love::ios::getSafeArea(window);
- #elif defined(LOVE_ANDROID)
- if (window != nullptr)
- {
- int top, left, bottom, right;
- if (love::android::getSafeArea(top, left, bottom, right))
- {
- // DisplayCutout API returns safe area in pixels
- // and is affected by display orientation.
- double safeLeft, safeTop, safeWidth, safeHeight;
- fromPixels(left, top, safeLeft, safeTop);
- fromPixels(pixelWidth - left - right, pixelHeight - top - bottom, safeWidth, safeHeight);
- return {(int) safeLeft, (int) safeTop, (int) safeWidth, (int) safeHeight};
- }
- }
- #endif
- double dw, dh;
- fromPixels(pixelWidth, pixelHeight, dw, dh);
- return {0, 0, (int) dw, (int) dh};
- }
- bool Window::isOpen() const
- {
- return open;
- }
- void Window::setWindowTitle(const std::string &title)
- {
- this->title = title;
- if (window)
- SDL_SetWindowTitle(window, title.c_str());
- }
- const std::string &Window::getWindowTitle() const
- {
- return title;
- }
- bool Window::setIcon(love::image::ImageData *imgd)
- {
- if (!imgd)
- return false;
- if (imgd->getFormat() != PIXELFORMAT_RGBA8_UNORM)
- throw love::Exception("setIcon only accepts 32-bit RGBA images.");
- icon.set(imgd);
- if (!window)
- return false;
- int w = imgd->getWidth();
- int h = imgd->getHeight();
- int bytesperpixel = (int) getPixelFormatBlockSize(imgd->getFormat());
- int pitch = w * bytesperpixel;
- SDL_Surface *sdlicon = SDL_CreateSurfaceFrom(w, h, SDL_PIXELFORMAT_RGBA8888, imgd->getData(), pitch);
- if (!sdlicon)
- return false;
- SDL_SetWindowIcon(window, sdlicon);
- SDL_DestroySurface(sdlicon);
- return true;
- }
- love::image::ImageData *Window::getIcon()
- {
- return icon.get();
- }
- void Window::setVSync(int vsync)
- {
- if (glcontext != nullptr)
- {
- SDL_GL_SetSwapInterval(vsync);
- // Check if adaptive vsync was requested but not supported, and fall
- // back to regular vsync if so.
- if (vsync == -1)
- {
- int actualvsync = 0;
- SDL_GL_GetSwapInterval(&actualvsync);
- if (actualvsync != -1)
- SDL_GL_SetSwapInterval(1);
- }
- }
- #ifdef LOVE_GRAPHICS_VULKAN
- if (windowRenderer == love::graphics::RENDERER_VULKAN)
- {
- auto vgfx = dynamic_cast<love::graphics::vulkan::Graphics*>(graphics.get());
- vgfx->setVsync(vsync);
- }
- #endif
- #if defined(LOVE_GRAPHICS_METAL) && defined(LOVE_MACOS)
- if (metalView != nullptr)
- {
- void *metallayer = SDL_Metal_GetLayer(metalView);
- love::macos::setMetalLayerVSync(metallayer, vsync != 0);
- }
- #endif
- }
- int Window::getVSync() const
- {
- if (glcontext != nullptr)
- {
- int interval = 0;
- SDL_GL_GetSwapInterval(&interval);
- return interval;
- }
- #if defined(LOVE_GRAPHICS_METAL)
- if (metalView != nullptr)
- {
- #ifdef LOVE_MACOS
- void *metallayer = SDL_Metal_GetLayer(metalView);
- return love::macos::getMetalLayerVSync(metallayer) ? 1 : 0;
- #else
- return 1;
- #endif
- }
- #endif
- #ifdef LOVE_GRAPHICS_VULKAN
- if (windowRenderer == love::graphics::RENDERER_VULKAN)
- {
- auto vgfx = dynamic_cast<love::graphics::vulkan::Graphics*>(graphics.get());
- return vgfx->getVsync();
- }
- #endif
- return 0;
- }
- void Window::setDisplaySleepEnabled(bool enable)
- {
- if (enable)
- SDL_EnableScreenSaver();
- else
- SDL_DisableScreenSaver();
- }
- bool Window::isDisplaySleepEnabled() const
- {
- return SDL_ScreenSaverEnabled();
- }
- void Window::minimize()
- {
- if (window != nullptr)
- {
- SDL_MinimizeWindow(window);
- updateSettings(settings, true);
- }
- }
- void Window::maximize()
- {
- if (window != nullptr)
- {
- SDL_MaximizeWindow(window);
- updateSettings(settings, true);
- }
- }
- void Window::restore()
- {
- if (window != nullptr)
- {
- SDL_RestoreWindow(window);
- updateSettings(settings, true);
- }
- }
- void Window::focus()
- {
- if (window != nullptr)
- {
- SDL_RaiseWindow(window);
- updateSettings(settings, true);
- }
- }
- bool Window::isMaximized() const
- {
- return window != nullptr && (SDL_GetWindowFlags(window) & SDL_WINDOW_MAXIMIZED);
- }
- bool Window::isMinimized() const
- {
- return window != nullptr && (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED);
- }
- void Window::swapBuffers()
- {
- if (glcontext)
- {
- #ifdef LOVE_WINDOWS
- bool useDwmFlush = false;
- int swapInterval = getVSync();
- // https://github.com/love2d/love/issues/1628
- // VSync can interact badly with Windows desktop composition (DWM) in windowed mode. DwmFlush can be used instead
- // of vsync, but it's much less flexible so we're very conservative here with where it's used:
- // - It won't work with exclusive or desktop fullscreen.
- // - DWM refreshes don't always match the refresh rate of the monitor the window is in (or the requested swap
- // interval), so we only use it when they do match.
- // - The user may force GL vsync, and DwmFlush shouldn't be used together with GL vsync.
- if (canUseDwmFlush && !settings.fullscreen && swapInterval == 1)
- {
- // Desktop composition is always enabled in Windows 8+. But DwmIsCompositionEnabled won't always return true...
- // (see DwmIsCompositionEnabled docs).
- BOOL compositionEnabled = IsWindows8OrGreater();
- if (compositionEnabled || (SUCCEEDED(DwmIsCompositionEnabled(&compositionEnabled)) && compositionEnabled))
- {
- DWM_TIMING_INFO info = {};
- info.cbSize = sizeof(DWM_TIMING_INFO);
- double dwmRefreshRate = 0;
- if (SUCCEEDED(DwmGetCompositionTimingInfo(nullptr, &info)))
- dwmRefreshRate = (double)info.rateRefresh.uiNumerator / (double)info.rateRefresh.uiDenominator;
- SDL_DisplayMode dmode = {};
- SDL_DisplayID display = SDL_GetDisplayForWindow(window);
- const SDL_DisplayMode* modePtr = SDL_GetCurrentDisplayMode(display);
- if (modePtr)
- dmode = *modePtr;
- if (dmode.refresh_rate > 0 && dwmRefreshRate > 0 && (fabs(dmode.refresh_rate - dwmRefreshRate) < 2))
- {
- SDL_GL_SetSwapInterval(0);
- int interval = 0;
- if (SDL_GL_GetSwapInterval(&interval) == 0 && interval == 0)
- useDwmFlush = true;
- else
- SDL_GL_SetSwapInterval(swapInterval);
- }
- }
- }
- #endif
- SDL_GL_SwapWindow(window);
- #ifdef LOVE_WINDOWS
- if (useDwmFlush)
- {
- DwmFlush();
- SDL_GL_SetSwapInterval(swapInterval);
- }
- #endif
- }
- }
- bool Window::hasFocus() const
- {
- return (window && SDL_GetKeyboardFocus() == window);
- }
- bool Window::hasMouseFocus() const
- {
- return (window && SDL_GetMouseFocus() == window);
- }
- bool Window::isVisible() const
- {
- return window && (SDL_GetWindowFlags(window) & (SDL_WINDOW_HIDDEN | SDL_WINDOW_MINIMIZED)) == 0;
- }
- bool Window::isOccluded() const
- {
- return window && (SDL_GetWindowFlags(window) & SDL_WINDOW_OCCLUDED) != 0;
- }
- void Window::setMouseGrab(bool grab)
- {
- mouseGrabbed = grab;
- if (window)
- SDL_SetWindowMouseGrab(window, grab);
- }
- bool Window::isMouseGrabbed() const
- {
- if (window)
- return SDL_GetWindowMouseGrab(window);
- else
- return mouseGrabbed;
- }
- int Window::getWidth() const
- {
- return windowWidth;
- }
- int Window::getHeight() const
- {
- return windowHeight;
- }
- int Window::getPixelWidth() const
- {
- return pixelWidth;
- }
- int Window::getPixelHeight() const
- {
- return pixelHeight;
- }
- void Window::clampPositionInWindow(double *wx, double *wy) const
- {
- if (wx != nullptr)
- *wx = std::min(std::max(0.0, *wx), (double) getWidth() - 1);
- if (wy != nullptr)
- *wy = std::min(std::max(0.0, *wy), (double) getHeight() - 1);
- }
- void Window::windowToPixelCoords(double *x, double *y) const
- {
- if (x != nullptr)
- *x = (*x) * ((double) pixelWidth / (double) windowWidth);
- if (y != nullptr)
- *y = (*y) * ((double) pixelHeight / (double) windowHeight);
- }
- void Window::pixelToWindowCoords(double *x, double *y) const
- {
- if (x != nullptr)
- *x = (*x) * ((double) windowWidth / (double) pixelWidth);
- if (y != nullptr)
- *y = (*y) * ((double) windowHeight / (double) pixelHeight);
- }
- void Window::windowToDPICoords(double *x, double *y) const
- {
- double px = x != nullptr ? *x : 0.0;
- double py = y != nullptr ? *y : 0.0;
- windowToPixelCoords(&px, &py);
- double dpix = 0.0;
- double dpiy = 0.0;
- fromPixels(px, py, dpix, dpiy);
- if (x != nullptr)
- *x = dpix;
- if (y != nullptr)
- *y = dpiy;
- }
- void Window::DPIToWindowCoords(double *x, double *y) const
- {
- double dpix = x != nullptr ? *x : 0.0;
- double dpiy = y != nullptr ? *y : 0.0;
- double px = 0.0;
- double py = 0.0;
- toPixels(dpix, dpiy, px, py);
- pixelToWindowCoords(&px, &py);
- if (x != nullptr)
- *x = px;
- if (y != nullptr)
- *y = py;
- }
- double Window::getDPIScale() const
- {
- return settings.usedpiscale ? getNativeDPIScale() : 1.0;
- }
- double Window::getNativeDPIScale() const
- {
- #ifdef LOVE_ANDROID
- return love::android::getScreenScale();
- #else
- return (double) pixelHeight / (double) windowHeight;
- #endif
- }
- double Window::toPixels(double x) const
- {
- return x * getDPIScale();
- }
- void Window::toPixels(double wx, double wy, double &px, double &py) const
- {
- double scale = getDPIScale();
- px = wx * scale;
- py = wy * scale;
- }
- double Window::fromPixels(double x) const
- {
- return x / getDPIScale();
- }
- void Window::fromPixels(double px, double py, double &wx, double &wy) const
- {
- double scale = getDPIScale();
- wx = px / scale;
- wy = py / scale;
- }
- void *Window::getHandle() const
- {
- return window;
- }
- SDL_MessageBoxFlags Window::convertMessageBoxType(MessageBoxType type) const
- {
- switch (type)
- {
- case MESSAGEBOX_ERROR:
- return SDL_MESSAGEBOX_ERROR;
- case MESSAGEBOX_WARNING:
- return SDL_MESSAGEBOX_WARNING;
- case MESSAGEBOX_INFO:
- default:
- return SDL_MESSAGEBOX_INFORMATION;
- }
- }
- bool Window::showMessageBox(const std::string &title, const std::string &message, MessageBoxType type, bool attachtowindow)
- {
- SDL_MessageBoxFlags flags = convertMessageBoxType(type);
- SDL_Window *sdlwindow = attachtowindow ? window : nullptr;
- return SDL_ShowSimpleMessageBox(flags, title.c_str(), message.c_str(), sdlwindow);
- }
- int Window::showMessageBox(const MessageBoxData &data)
- {
- SDL_MessageBoxData sdldata = {};
- sdldata.flags = convertMessageBoxType(data.type);
- sdldata.title = data.title.c_str();
- sdldata.message = data.message.c_str();
- sdldata.window = data.attachToWindow ? window : nullptr;
- sdldata.numbuttons = (int) data.buttons.size();
- std::vector<SDL_MessageBoxButtonData> sdlbuttons;
- for (int i = 0; i < (int) data.buttons.size(); i++)
- {
- SDL_MessageBoxButtonData sdlbutton = {};
- sdlbutton.buttonID = i;
- sdlbutton.text = data.buttons[i].c_str();
- if (i == data.enterButtonIndex)
- sdlbutton.flags |= SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
- if (i == data.escapeButtonIndex)
- sdlbutton.flags |= SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
- sdlbuttons.push_back(sdlbutton);
- }
- sdldata.buttons = &sdlbuttons[0];
- int pressedbutton = -2;
- SDL_ShowMessageBox(&sdldata, &pressedbutton);
- return pressedbutton;
- }
- void Window::requestAttention(bool continuous)
- {
- #if defined(LOVE_WINDOWS) && !defined(LOVE_WINDOWS_UWP)
- if (hasFocus())
- return;
- FLASHWINFO flashinfo = { sizeof(FLASHWINFO) };
- flashinfo.hwnd = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr);
- flashinfo.uCount = 1;
- flashinfo.dwFlags = FLASHW_ALL;
- if (continuous)
- {
- flashinfo.uCount = 0;
- flashinfo.dwFlags |= FLASHW_TIMERNOFG;
- }
- FlashWindowEx(&flashinfo);
- #elif defined(LOVE_MACOS)
- love::macos::requestAttention(continuous);
- #else
- LOVE_UNUSED(continuous);
-
- #endif
-
- // TODO: Linux?
- }
- } // sdl
- } // window
- } // love
|