Window.cpp 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510
  1. /**
  2. * Copyright (c) 2006-2022 LOVE Development Team
  3. *
  4. * This software is provided 'as-is', without any express or implied
  5. * warranty. In no event will the authors be held liable for any damages
  6. * arising from the use of this software.
  7. *
  8. * Permission is granted to anyone to use this software for any purpose,
  9. * including commercial applications, and to alter it and redistribute it
  10. * freely, subject to the following restrictions:
  11. *
  12. * 1. The origin of this software must not be misrepresented; you must not
  13. * claim that you wrote the original software. If you use this software
  14. * in a product, an acknowledgment in the product documentation would be
  15. * appreciated but is not required.
  16. * 2. Altered source versions must be plainly marked as such, and must not be
  17. * misrepresented as being the original software.
  18. * 3. This notice may not be removed or altered from any source distribution.
  19. **/
  20. // LOVE
  21. #include "common/config.h"
  22. #include "graphics/Graphics.h"
  23. #ifdef LOVE_GRAPHICS_VULKAN
  24. # include "graphics/vulkan/Graphics.h"
  25. # include "graphics/vulkan/Vulkan.h"
  26. #endif
  27. #include "Window.h"
  28. #ifdef LOVE_ANDROID
  29. #include "common/android.h"
  30. #endif
  31. #ifdef LOVE_IOS
  32. #include "common/ios.h"
  33. #endif
  34. // C++
  35. #include <iostream>
  36. #include <vector>
  37. #include <algorithm>
  38. // C
  39. #include <cstdio>
  40. // SDL
  41. #include <SDL_syswm.h>
  42. #if defined(LOVE_WINDOWS)
  43. #include <windows.h>
  44. #include <dwmapi.h>
  45. #include <VersionHelpers.h>
  46. #elif defined(LOVE_MACOS)
  47. #include "common/macos.h"
  48. #endif
  49. #ifndef APIENTRY
  50. #define APIENTRY
  51. #endif
  52. #ifndef SDL_HINT_WINDOWS_DPI_SCALING
  53. #define SDL_HINT_WINDOWS_DPI_SCALING "SDL_WINDOWS_DPI_SCALING"
  54. #endif
  55. namespace love
  56. {
  57. namespace window
  58. {
  59. // See src/modules/window/Window.cpp.
  60. void setHighDPIAllowedImplementation(bool enable)
  61. {
  62. #if defined(LOVE_WINDOWS)
  63. // Windows uses a different API than SDL_WINDOW_ALLOW_HIGHDPI.
  64. // This must be set before the video subsystem is initialized.
  65. SDL_SetHint(SDL_HINT_WINDOWS_DPI_SCALING, enable ? "1" : "0");
  66. #else
  67. LOVE_UNUSED(enable);
  68. #endif
  69. }
  70. namespace sdl
  71. {
  72. Window::Window()
  73. : open(false)
  74. , mouseGrabbed(false)
  75. , window(nullptr)
  76. , glcontext(nullptr)
  77. #ifdef LOVE_GRAPHICS_METAL
  78. , metalView(nullptr)
  79. #endif
  80. , displayedWindowError(false)
  81. , hasSDL203orEarlier(false)
  82. , contextAttribs()
  83. {
  84. if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
  85. throw love::Exception("Could not initialize SDL video subsystem (%s)", SDL_GetError());
  86. // Make sure the screensaver doesn't activate by default.
  87. setDisplaySleepEnabled(false);
  88. SDL_version version = {};
  89. SDL_GetVersion(&version);
  90. hasSDL203orEarlier = (version.major == 2 && version.minor == 0 && version.patch <= 3);
  91. }
  92. Window::~Window()
  93. {
  94. close(false);
  95. graphics.set(nullptr);
  96. SDL_QuitSubSystem(SDL_INIT_VIDEO);
  97. }
  98. void Window::setGraphics(graphics::Graphics *graphics)
  99. {
  100. this->graphics.set(graphics);
  101. }
  102. void Window::setGLFramebufferAttributes(bool sRGB)
  103. {
  104. // Set GL window / framebuffer attributes.
  105. SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
  106. SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
  107. SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
  108. SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
  109. SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  110. SDL_GL_SetAttribute(SDL_GL_RETAINED_BACKING, 0);
  111. // Always use 24/8 depth/stencil (make sure any Graphics implementations
  112. // that have their own backbuffer match this, too).
  113. // Changing this after initial window creation would need the context to be
  114. // destroyed and recreated, which we really don't want.
  115. SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
  116. SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
  117. // Backbuffer MSAA is handled by the love.graphics implementation.
  118. SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
  119. SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
  120. SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, sRGB ? 1 : 0);
  121. const char *driver = SDL_GetCurrentVideoDriver();
  122. if (driver && strstr(driver, "x11") == driver)
  123. {
  124. // Always disable the sRGB flag when GLX is used with older SDL versions,
  125. // because of this bug: https://bugzilla.libsdl.org/show_bug.cgi?id=2897
  126. // In practice GLX will always give an sRGB-capable framebuffer anyway.
  127. if (hasSDL203orEarlier)
  128. SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, 0);
  129. }
  130. #if defined(LOVE_WINDOWS)
  131. // Avoid the Microsoft OpenGL 1.1 software renderer on Windows. Apparently
  132. // older Intel drivers like to use it as a fallback when requesting some
  133. // unsupported framebuffer attribute values, rather than properly failing.
  134. SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
  135. #endif
  136. }
  137. void Window::setGLContextAttributes(const ContextAttribs &attribs)
  138. {
  139. int profilemask = 0;
  140. int contextflags = 0;
  141. if (attribs.gles)
  142. profilemask = SDL_GL_CONTEXT_PROFILE_ES;
  143. else if (attribs.versionMajor * 10 + attribs.versionMinor >= 32)
  144. profilemask |= SDL_GL_CONTEXT_PROFILE_CORE;
  145. else if (attribs.debug)
  146. profilemask = SDL_GL_CONTEXT_PROFILE_COMPATIBILITY;
  147. if (attribs.debug)
  148. contextflags |= SDL_GL_CONTEXT_DEBUG_FLAG;
  149. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, attribs.versionMajor);
  150. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, attribs.versionMinor);
  151. SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profilemask);
  152. SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, contextflags);
  153. }
  154. bool Window::checkGLVersion(const ContextAttribs &attribs, std::string &outversion)
  155. {
  156. typedef unsigned char GLubyte;
  157. typedef unsigned int GLenum;
  158. typedef const GLubyte *(APIENTRY *glGetStringPtr)(GLenum name);
  159. const GLenum GL_VENDOR_ENUM = 0x1F00;
  160. const GLenum GL_RENDERER_ENUM = 0x1F01;
  161. const GLenum GL_VERSION_ENUM = 0x1F02;
  162. // We don't have OpenGL headers or an automatic OpenGL function loader in
  163. // this module, so we have to get the glGetString function pointer ourselves.
  164. glGetStringPtr glGetStringFunc = (glGetStringPtr) SDL_GL_GetProcAddress("glGetString");
  165. if (!glGetStringFunc)
  166. return false;
  167. const char *glversion = (const char *) glGetStringFunc(GL_VERSION_ENUM);
  168. if (!glversion)
  169. return false;
  170. outversion = glversion;
  171. const char *glrenderer = (const char *) glGetStringFunc(GL_RENDERER_ENUM);
  172. if (glrenderer)
  173. outversion += " - " + std::string(glrenderer);
  174. const char *glvendor = (const char *) glGetStringFunc(GL_VENDOR_ENUM);
  175. if (glvendor)
  176. outversion += " (" + std::string(glvendor) + ")";
  177. int glmajor = 0;
  178. int glminor = 0;
  179. // glGetString(GL_VERSION) returns a string with the format "major.minor",
  180. // or "OpenGL ES major.minor" in GLES contexts.
  181. const char *format = "%d.%d";
  182. if (attribs.gles)
  183. format = "OpenGL ES %d.%d";
  184. if (sscanf(glversion, format, &glmajor, &glminor) != 2)
  185. return false;
  186. if (glmajor < attribs.versionMajor
  187. || (glmajor == attribs.versionMajor && glminor < attribs.versionMinor))
  188. return false;
  189. return true;
  190. }
  191. std::vector<Window::ContextAttribs> Window::getContextAttribsList() const
  192. {
  193. // If we already have a set of context attributes that we know work, just
  194. // return that. love.graphics doesn't really support switching GL versions
  195. // after the first initialization.
  196. if (contextAttribs.versionMajor > 0)
  197. return std::vector<ContextAttribs>{contextAttribs};
  198. bool preferGLES = false;
  199. #ifdef LOVE_GRAPHICS_USE_OPENGLES
  200. preferGLES = true;
  201. #endif
  202. const char *curdriver = SDL_GetCurrentVideoDriver();
  203. const char *glesdrivers[] = {"RPI", "Android", "uikit", "winrt", "emscripten"};
  204. // We always want to try OpenGL ES first on certain video backends.
  205. for (const char *glesdriver : glesdrivers)
  206. {
  207. if (curdriver && strstr(curdriver, glesdriver) == curdriver)
  208. {
  209. preferGLES = true;
  210. // Prior to SDL 2.0.4, backends that use OpenGL ES didn't properly
  211. // ask for a sRGB framebuffer when requested by SDL_GL_SetAttribute.
  212. // This doesn't account for windowing backends that sometimes use
  213. // EGL, e.g. the X11 and windows SDL backends.
  214. if (hasSDL203orEarlier)
  215. graphics::setGammaCorrect(false);
  216. break;
  217. }
  218. }
  219. const char *gleshint = SDL_GetHint("LOVE_GRAPHICS_USE_OPENGLES");
  220. if (gleshint != nullptr)
  221. preferGLES = (gleshint != nullptr && gleshint[0] != '0');
  222. // Do we want a debug context?
  223. bool debug = love::graphics::isDebugEnabled();
  224. const char *preferGL2hint = SDL_GetHint("LOVE_GRAPHICS_USE_GL2");
  225. bool preferGL2 = (preferGL2hint != nullptr && preferGL2hint[0] != '0');
  226. const char *preferGL3hint = SDL_GetHint("LOVE_GRAPHICS_USE_GL3");
  227. bool preferGL3 = (preferGL3hint != nullptr && preferGL3hint[0] != '0');
  228. std::vector<ContextAttribs> glcontexts =
  229. {
  230. {4, 3, false, debug},
  231. {3, 3, false, debug},
  232. {2, 1, false, debug},
  233. };
  234. std::vector<ContextAttribs> glescontexts =
  235. {
  236. {3, 2, true, debug},
  237. {3, 0, true, debug},
  238. {2, 0, true, debug}
  239. };
  240. if (preferGL2)
  241. {
  242. std::swap(glcontexts[0], glcontexts[2]);
  243. std::swap(glescontexts[0], glescontexts[2]);
  244. }
  245. else if (preferGL3)
  246. {
  247. std::swap(glcontexts[0], glcontexts[1]);
  248. std::swap(glescontexts[0], glescontexts[1]);
  249. }
  250. std::vector<ContextAttribs> attribslist;
  251. if (preferGLES)
  252. {
  253. attribslist.insert(attribslist.end(), glescontexts.begin(), glescontexts.end());
  254. attribslist.insert(attribslist.end(), glcontexts.begin(), glcontexts.end());
  255. }
  256. else
  257. {
  258. attribslist.insert(attribslist.end(), glcontexts.begin(), glcontexts.end());
  259. attribslist.insert(attribslist.end(), glescontexts.begin(), glescontexts.end());
  260. }
  261. return attribslist;
  262. }
  263. bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, graphics::Renderer renderer)
  264. {
  265. bool needsglcontext = (windowflags & SDL_WINDOW_OPENGL) != 0;
  266. #ifdef LOVE_GRAPHICS_METAL
  267. bool needsmetalview = (windowflags & SDL_WINDOW_METAL) != 0;
  268. #endif
  269. std::string windowerror;
  270. std::string contexterror;
  271. std::string glversion;
  272. // Unfortunately some OpenGL context settings are part of the internal
  273. // window state in the Windows and Linux SDL backends, so we have to
  274. // recreate the window when we want to change those settings...
  275. // Also, apparently some Intel drivers on Windows give back a Microsoft
  276. // OpenGL 1.1 software renderer context when high MSAA values are requested!
  277. const auto create = [&](const ContextAttribs *attribs) -> bool
  278. {
  279. if (glcontext)
  280. {
  281. SDL_GL_DeleteContext(glcontext);
  282. glcontext = nullptr;
  283. }
  284. #ifdef LOVE_GRAPHICS_METAL
  285. if (metalView)
  286. {
  287. SDL_Metal_DestroyView(metalView);
  288. metalView = nullptr;
  289. }
  290. #endif
  291. if (window)
  292. {
  293. SDL_DestroyWindow(window);
  294. SDL_FlushEvent(SDL_WINDOWEVENT);
  295. window = nullptr;
  296. }
  297. window = SDL_CreateWindow(title.c_str(), x, y, w, h, windowflags);
  298. if (!window)
  299. {
  300. windowerror = std::string(SDL_GetError());
  301. return false;
  302. }
  303. if (attribs != nullptr && renderer == love::graphics::Renderer::RENDERER_OPENGL)
  304. {
  305. #ifdef LOVE_MACOS
  306. love::macos::setWindowSRGBColorSpace(window);
  307. #endif
  308. glcontext = SDL_GL_CreateContext(window);
  309. if (!glcontext)
  310. contexterror = std::string(SDL_GetError());
  311. // Make sure the context's version is at least what we requested.
  312. if (glcontext && !checkGLVersion(*attribs, glversion))
  313. {
  314. SDL_GL_DeleteContext(glcontext);
  315. glcontext = nullptr;
  316. }
  317. if (!glcontext)
  318. {
  319. SDL_DestroyWindow(window);
  320. window = nullptr;
  321. return false;
  322. }
  323. }
  324. return true;
  325. };
  326. if (renderer == graphics::RENDERER_OPENGL)
  327. {
  328. std::vector<ContextAttribs> attribslist = getContextAttribsList();
  329. // Try each context profile in order.
  330. for (ContextAttribs attribs : attribslist)
  331. {
  332. bool curSRGB = love::graphics::isGammaCorrect();
  333. setGLFramebufferAttributes(curSRGB);
  334. setGLContextAttributes(attribs);
  335. windowerror.clear();
  336. contexterror.clear();
  337. create(&attribs);
  338. if (!window && curSRGB)
  339. {
  340. // The sRGB setting could have caused the failure.
  341. setGLFramebufferAttributes(false);
  342. if (create(&attribs))
  343. curSRGB = false;
  344. }
  345. if (window && glcontext)
  346. {
  347. // Store the successful context attributes so we can re-use them in
  348. // subsequent calls to createWindowAndContext.
  349. contextAttribs = attribs;
  350. love::graphics::setGammaCorrect(curSRGB);
  351. break;
  352. }
  353. }
  354. }
  355. #ifdef LOVE_GRAPHICS_METAL
  356. else if (renderer == graphics::RENDERER_METAL)
  357. {
  358. if (create(nullptr) && window != nullptr)
  359. metalView = SDL_Metal_CreateView(window);
  360. if (metalView == nullptr && window != nullptr)
  361. {
  362. contexterror = SDL_GetError();
  363. SDL_DestroyWindow(window);
  364. window = nullptr;
  365. }
  366. }
  367. #endif
  368. else
  369. {
  370. create(nullptr);
  371. }
  372. bool failed = window == nullptr;
  373. failed |= (needsglcontext && !glcontext);
  374. #ifdef LOVE_GRAPHICS_METAL
  375. failed |= (needsmetalview && !metalView);
  376. #endif
  377. if (failed)
  378. {
  379. std::string title = "Unable to create renderer";
  380. std::string message = "This program requires a graphics card and video drivers which support OpenGL 2.1 or OpenGL ES 2.";
  381. if (!glversion.empty())
  382. message += "\n\nDetected OpenGL version:\n" + glversion;
  383. else if (!contexterror.empty())
  384. message += "\n\nRenderer context creation error: " + contexterror;
  385. else if (!windowerror.empty())
  386. message += "\n\nSDL window creation error: " + windowerror;
  387. std::cerr << title << std::endl << message << std::endl;
  388. // Display a message box with the error, but only once.
  389. if (!displayedWindowError)
  390. {
  391. showMessageBox(title, message, MESSAGEBOX_ERROR, false);
  392. displayedWindowError = true;
  393. }
  394. close();
  395. return false;
  396. }
  397. open = true;
  398. return true;
  399. }
  400. bool Window::setWindow(int width, int height, WindowSettings *settings)
  401. {
  402. if (!graphics.get())
  403. graphics.set(Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS));
  404. if (graphics.get() && graphics->isRenderTargetActive())
  405. throw love::Exception("love.window.setMode cannot be called while a render target is active in love.graphics.");
  406. auto renderer = graphics != nullptr ? graphics->getRenderer() : graphics::RENDERER_NONE;
  407. if (isOpen())
  408. updateSettings(this->settings, false);
  409. WindowSettings f;
  410. if (settings)
  411. f = *settings;
  412. f.minwidth = std::max(f.minwidth, 1);
  413. f.minheight = std::max(f.minheight, 1);
  414. f.displayindex = std::min(std::max(f.displayindex, 0), getDisplayCount() - 1);
  415. // Use the desktop resolution if a width or height of 0 is specified.
  416. if (width == 0 || height == 0)
  417. {
  418. SDL_DisplayMode mode = {};
  419. SDL_GetDesktopDisplayMode(f.displayindex, &mode);
  420. width = mode.w;
  421. height = mode.h;
  422. }
  423. // On Android, disable fullscreen first on window creation so it's
  424. // possible to change the orientation by specifying portait width and
  425. // height, otherwise SDL will pick the current orientation dimensions when
  426. // fullscreen flag is set. Don't worry, we'll set it back later when user
  427. // also requested fullscreen after the window is created.
  428. // See https://github.com/love2d/love-android/issues/196
  429. #ifdef LOVE_ANDROID
  430. bool fullscreen = f.fullscreen;
  431. f.fullscreen = false;
  432. f.fstype = FULLSCREEN_DESKTOP;
  433. #endif
  434. int x = f.x;
  435. int y = f.y;
  436. if (f.useposition)
  437. {
  438. // The position needs to be in the global coordinate space.
  439. SDL_Rect displaybounds = {};
  440. SDL_GetDisplayBounds(f.displayindex, &displaybounds);
  441. x += displaybounds.x;
  442. y += displaybounds.y;
  443. }
  444. else
  445. {
  446. if (f.centered)
  447. x = y = SDL_WINDOWPOS_CENTERED_DISPLAY(f.displayindex);
  448. else
  449. x = y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(f.displayindex);
  450. }
  451. SDL_DisplayMode fsmode = {0, width, height, 0, nullptr};
  452. if (f.fullscreen && f.fstype == FULLSCREEN_EXCLUSIVE)
  453. {
  454. // Fullscreen window creation will bug out if no mode can be used.
  455. if (SDL_GetClosestDisplayMode(f.displayindex, &fsmode, &fsmode) == nullptr)
  456. {
  457. // GetClosestDisplayMode will fail if we request a size larger
  458. // than the largest available display mode, so we'll try to use
  459. // the largest (first) mode in that case.
  460. if (SDL_GetDisplayMode(f.displayindex, 0, &fsmode) < 0)
  461. return false;
  462. }
  463. }
  464. bool needsetmode = false;
  465. Uint32 sdlflags = 0;
  466. if (f.fullscreen)
  467. {
  468. if (f.fstype == FULLSCREEN_DESKTOP)
  469. sdlflags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
  470. else
  471. {
  472. sdlflags |= SDL_WINDOW_FULLSCREEN;
  473. width = fsmode.w;
  474. height = fsmode.h;
  475. }
  476. }
  477. if (renderer != windowRenderer && isOpen())
  478. close();
  479. if (isOpen())
  480. {
  481. if (SDL_SetWindowFullscreen(window, sdlflags) == 0 && renderer == graphics::RENDERER_OPENGL)
  482. SDL_GL_MakeCurrent(window, glcontext);
  483. if (!f.fullscreen)
  484. SDL_SetWindowSize(window, width, height);
  485. // On linux systems 2.0.5+ might not be available...
  486. // TODO: require at least 2.0.5?
  487. #if SDL_VERSION_ATLEAST(2, 0, 5)
  488. if (this->settings.resizable != f.resizable)
  489. SDL_SetWindowResizable(window, f.resizable ? SDL_TRUE : SDL_FALSE);
  490. #endif
  491. if (this->settings.borderless != f.borderless)
  492. SDL_SetWindowBordered(window, f.borderless ? SDL_FALSE : SDL_TRUE);
  493. }
  494. else
  495. {
  496. if (renderer == graphics::RENDERER_OPENGL)
  497. sdlflags |= SDL_WINDOW_OPENGL;
  498. #ifdef LOVE_GRAPHICS_METAL
  499. if (renderer == graphics::RENDERER_METAL)
  500. sdlflags |= SDL_WINDOW_METAL;
  501. #endif
  502. if (renderer == graphics::RENDERER_VULKAN)
  503. sdlflags |= SDL_WINDOW_VULKAN;
  504. if (f.resizable)
  505. sdlflags |= SDL_WINDOW_RESIZABLE;
  506. if (f.borderless)
  507. sdlflags |= SDL_WINDOW_BORDERLESS;
  508. // Note: this flag is ignored on Windows.
  509. if (isHighDPIAllowed())
  510. sdlflags |= SDL_WINDOW_ALLOW_HIGHDPI;
  511. if (!createWindowAndContext(x, y, width, height, sdlflags, renderer))
  512. return false;
  513. needsetmode = true;
  514. }
  515. // Make sure the window keeps any previously set icon.
  516. setIcon(icon.get());
  517. // Make sure the mouse keeps its previous grab setting.
  518. setMouseGrab(mouseGrabbed);
  519. // Enforce minimum window dimensions.
  520. SDL_SetWindowMinimumSize(window, f.minwidth, f.minheight);
  521. if (this->settings.displayindex != f.displayindex || f.useposition || f.centered)
  522. SDL_SetWindowPosition(window, x, y);
  523. SDL_RaiseWindow(window);
  524. setVSync(f.vsync);
  525. updateSettings(f, false);
  526. windowRenderer = renderer;
  527. if (graphics.get())
  528. {
  529. double scaledw, scaledh;
  530. fromPixels((double) pixelWidth, (double) pixelHeight, scaledw, scaledh);
  531. if (needsetmode)
  532. {
  533. void *context = nullptr;
  534. if (renderer == graphics::RENDERER_OPENGL)
  535. context = (void *) glcontext;
  536. #ifdef LOVE_GRAPHICS_METAL
  537. if (renderer == graphics::RENDERER_METAL && metalView)
  538. context = (void *) SDL_Metal_GetLayer(metalView);
  539. #endif
  540. graphics->setMode(context, (int) scaledw, (int) scaledh, pixelWidth, pixelHeight, f.stencil, f.msaa);
  541. this->settings.msaa = graphics->getBackbufferMSAA();
  542. }
  543. else
  544. {
  545. graphics->setViewportSize((int) scaledw, (int) scaledh, pixelWidth, pixelHeight);
  546. }
  547. }
  548. // Set fullscreen when user requested it before.
  549. // See above for explanation.
  550. #ifdef LOVE_ANDROID
  551. setFullscreen(fullscreen);
  552. love::android::setImmersive(fullscreen);
  553. #endif
  554. return true;
  555. }
  556. bool Window::onSizeChanged(int width, int height)
  557. {
  558. if (!window)
  559. return false;
  560. windowWidth = width;
  561. windowHeight = height;
  562. if (glcontext != nullptr)
  563. SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight);
  564. #ifdef LOVE_GRAPHICS_METAL
  565. else if (metalView != nullptr)
  566. SDL_Metal_GetDrawableSize(window, &pixelWidth, &pixelHeight);
  567. #endif
  568. else
  569. {
  570. pixelWidth = width;
  571. pixelHeight = height;
  572. }
  573. if (graphics.get())
  574. {
  575. double scaledw, scaledh;
  576. fromPixels((double) pixelWidth, (double) pixelHeight, scaledw, scaledh);
  577. graphics->setViewportSize((int) scaledw, (int) scaledh, pixelWidth, pixelHeight);
  578. }
  579. return true;
  580. }
  581. void Window::updateSettings(const WindowSettings &newsettings, bool updateGraphicsViewport)
  582. {
  583. Uint32 wflags = SDL_GetWindowFlags(window);
  584. // Set the new display mode as the current display mode.
  585. SDL_GetWindowSize(window, &windowWidth, &windowHeight);
  586. pixelWidth = windowWidth;
  587. pixelHeight = windowHeight;
  588. if ((wflags & SDL_WINDOW_OPENGL) != 0)
  589. SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight);
  590. #ifdef LOVE_GRAPHICS_METAL
  591. else if ((wflags & SDL_WINDOW_METAL) != 0)
  592. SDL_Metal_GetDrawableSize(window, &pixelWidth, &pixelHeight);
  593. #endif
  594. if ((wflags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)
  595. {
  596. settings.fullscreen = true;
  597. settings.fstype = FULLSCREEN_DESKTOP;
  598. }
  599. else if ((wflags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)
  600. {
  601. settings.fullscreen = true;
  602. settings.fstype = FULLSCREEN_EXCLUSIVE;
  603. }
  604. else
  605. {
  606. settings.fullscreen = false;
  607. settings.fstype = newsettings.fstype;
  608. }
  609. #ifdef LOVE_ANDROID
  610. settings.fullscreen = love::android::getImmersive();
  611. #endif
  612. // SDL_GetWindowMinimumSize gives back 0,0 sometimes...
  613. settings.minwidth = newsettings.minwidth;
  614. settings.minheight = newsettings.minheight;
  615. settings.resizable = (wflags & SDL_WINDOW_RESIZABLE) != 0;
  616. settings.borderless = (wflags & SDL_WINDOW_BORDERLESS) != 0;
  617. settings.centered = newsettings.centered;
  618. getPosition(settings.x, settings.y, settings.displayindex);
  619. setHighDPIAllowed((wflags & SDL_WINDOW_ALLOW_HIGHDPI) != 0);
  620. settings.usedpiscale = newsettings.usedpiscale;
  621. // Only minimize on focus loss if the window is in exclusive-fullscreen mode
  622. if (settings.fullscreen && settings.fstype == FULLSCREEN_EXCLUSIVE)
  623. SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "1");
  624. else
  625. SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
  626. settings.vsync = getVSync();
  627. settings.stencil = newsettings.stencil;
  628. settings.depth = newsettings.depth;
  629. SDL_DisplayMode dmode = {};
  630. SDL_GetCurrentDisplayMode(settings.displayindex, &dmode);
  631. // May be 0 if the refresh rate can't be determined.
  632. settings.refreshrate = (double) dmode.refresh_rate;
  633. // Update the viewport size now instead of waiting for event polling.
  634. if (updateGraphicsViewport && graphics.get())
  635. {
  636. double scaledw, scaledh;
  637. fromPixels((double) pixelWidth, (double) pixelHeight, scaledw, scaledh);
  638. graphics->setViewportSize((int) scaledw, (int) scaledh, pixelWidth, pixelHeight);
  639. }
  640. }
  641. void Window::getWindow(int &width, int &height, WindowSettings &newsettings)
  642. {
  643. // The window might have been modified (moved, resized, etc.) by the user.
  644. if (window)
  645. updateSettings(settings, true);
  646. width = windowWidth;
  647. height = windowHeight;
  648. newsettings = settings;
  649. }
  650. void Window::close()
  651. {
  652. close(true);
  653. }
  654. void Window::close(bool allowExceptions)
  655. {
  656. if (graphics.get())
  657. {
  658. if (allowExceptions && graphics->isRenderTargetActive())
  659. throw love::Exception("love.window.close cannot be called while a render target is active in love.graphics.");
  660. graphics->unSetMode();
  661. }
  662. if (glcontext)
  663. {
  664. SDL_GL_DeleteContext(glcontext);
  665. glcontext = nullptr;
  666. }
  667. #ifdef LOVE_GRAPHICS_METAL
  668. if (metalView)
  669. {
  670. SDL_Metal_DestroyView(metalView);
  671. metalView = nullptr;
  672. }
  673. #endif
  674. if (window)
  675. {
  676. SDL_DestroyWindow(window);
  677. window = nullptr;
  678. // The old window may have generated pending events which are no longer
  679. // relevant. Destroy them all!
  680. SDL_FlushEvent(SDL_WINDOWEVENT);
  681. }
  682. open = false;
  683. }
  684. bool Window::setFullscreen(bool fullscreen, FullscreenType fstype)
  685. {
  686. if (!window)
  687. return false;
  688. if (graphics.get() && graphics->isRenderTargetActive())
  689. throw love::Exception("love.window.setFullscreen cannot be called while a render target is active in love.graphics.");
  690. WindowSettings newsettings = settings;
  691. newsettings.fullscreen = fullscreen;
  692. newsettings.fstype = fstype;
  693. Uint32 sdlflags = 0;
  694. if (fullscreen)
  695. {
  696. if (fstype == FULLSCREEN_DESKTOP)
  697. sdlflags = SDL_WINDOW_FULLSCREEN_DESKTOP;
  698. else
  699. {
  700. sdlflags = SDL_WINDOW_FULLSCREEN;
  701. SDL_DisplayMode mode = {};
  702. mode.w = windowWidth;
  703. mode.h = windowHeight;
  704. SDL_GetClosestDisplayMode(SDL_GetWindowDisplayIndex(window), &mode, &mode);
  705. SDL_SetWindowDisplayMode(window, &mode);
  706. }
  707. }
  708. #ifdef LOVE_ANDROID
  709. love::android::setImmersive(fullscreen);
  710. #endif
  711. if (SDL_SetWindowFullscreen(window, sdlflags) == 0)
  712. {
  713. if (glcontext)
  714. SDL_GL_MakeCurrent(window, glcontext);
  715. updateSettings(newsettings, true);
  716. return true;
  717. }
  718. return false;
  719. }
  720. bool Window::setFullscreen(bool fullscreen)
  721. {
  722. return setFullscreen(fullscreen, settings.fstype);
  723. }
  724. int Window::getDisplayCount() const
  725. {
  726. return SDL_GetNumVideoDisplays();
  727. }
  728. const char *Window::getDisplayName(int displayindex) const
  729. {
  730. const char *name = SDL_GetDisplayName(displayindex);
  731. if (name == nullptr)
  732. throw love::Exception("Invalid display index: %d", displayindex + 1);
  733. return name;
  734. }
  735. Window::DisplayOrientation Window::getDisplayOrientation(int displayindex) const
  736. {
  737. // TODO: We can expose this everywhere, we just need to watch out for the
  738. // SDL binary being older than the headers on Linux.
  739. #if SDL_VERSION_ATLEAST(2, 0, 9) && (defined(LOVE_ANDROID) || !defined(LOVE_LINUX))
  740. switch (SDL_GetDisplayOrientation(displayindex))
  741. {
  742. case SDL_ORIENTATION_UNKNOWN: return ORIENTATION_UNKNOWN;
  743. case SDL_ORIENTATION_LANDSCAPE: return ORIENTATION_LANDSCAPE;
  744. case SDL_ORIENTATION_LANDSCAPE_FLIPPED: return ORIENTATION_LANDSCAPE_FLIPPED;
  745. case SDL_ORIENTATION_PORTRAIT: return ORIENTATION_PORTRAIT;
  746. case SDL_ORIENTATION_PORTRAIT_FLIPPED: return ORIENTATION_PORTRAIT_FLIPPED;
  747. }
  748. #else
  749. LOVE_UNUSED(displayindex);
  750. #endif
  751. return ORIENTATION_UNKNOWN;
  752. }
  753. std::vector<Window::WindowSize> Window::getFullscreenSizes(int displayindex) const
  754. {
  755. std::vector<WindowSize> sizes;
  756. for (int i = 0; i < SDL_GetNumDisplayModes(displayindex); i++)
  757. {
  758. SDL_DisplayMode mode = {};
  759. SDL_GetDisplayMode(displayindex, i, &mode);
  760. WindowSize w = {mode.w, mode.h};
  761. // SDL2's display mode list has multiple entries for modes of the same
  762. // size with different bits per pixel, so we need to filter those out.
  763. if (std::find(sizes.begin(), sizes.end(), w) == sizes.end())
  764. sizes.push_back(w);
  765. }
  766. return sizes;
  767. }
  768. void Window::getDesktopDimensions(int displayindex, int &width, int &height) const
  769. {
  770. if (displayindex >= 0 && displayindex < getDisplayCount())
  771. {
  772. SDL_DisplayMode mode = {};
  773. SDL_GetDesktopDisplayMode(displayindex, &mode);
  774. width = mode.w;
  775. height = mode.h;
  776. }
  777. else
  778. {
  779. width = 0;
  780. height = 0;
  781. }
  782. }
  783. void Window::setPosition(int x, int y, int displayindex)
  784. {
  785. if (!window)
  786. return;
  787. displayindex = std::min(std::max(displayindex, 0), getDisplayCount() - 1);
  788. SDL_Rect displaybounds = {};
  789. SDL_GetDisplayBounds(displayindex, &displaybounds);
  790. // The position needs to be in the global coordinate space.
  791. x += displaybounds.x;
  792. y += displaybounds.y;
  793. SDL_SetWindowPosition(window, x, y);
  794. settings.useposition = true;
  795. }
  796. void Window::getPosition(int &x, int &y, int &displayindex)
  797. {
  798. if (!window)
  799. {
  800. x = y = 0;
  801. displayindex = 0;
  802. return;
  803. }
  804. displayindex = std::max(SDL_GetWindowDisplayIndex(window), 0);
  805. SDL_GetWindowPosition(window, &x, &y);
  806. // In SDL <= 2.0.3, fullscreen windows are always reported as 0,0. In every
  807. // other case we need to convert the position from global coordinates to the
  808. // monitor's coordinate space.
  809. if (x != 0 || y != 0)
  810. {
  811. SDL_Rect displaybounds = {};
  812. SDL_GetDisplayBounds(displayindex, &displaybounds);
  813. x -= displaybounds.x;
  814. y -= displaybounds.y;
  815. }
  816. }
  817. Rect Window::getSafeArea() const
  818. {
  819. #if defined(LOVE_IOS)
  820. if (window != nullptr)
  821. return love::ios::getSafeArea(window);
  822. #elif defined(LOVE_ANDROID)
  823. if (window != nullptr)
  824. {
  825. int top, left, bottom, right;
  826. if (love::android::getSafeArea(top, left, bottom, right))
  827. {
  828. // DisplayCutout API returns safe area in pixels
  829. // and is affected by display orientation.
  830. double safeLeft, safeTop, safeWidth, safeHeight;
  831. fromPixels(left, top, safeLeft, safeTop);
  832. fromPixels(pixelWidth - left - right, pixelHeight - top - bottom, safeWidth, safeHeight);
  833. return {(int) safeLeft, (int) safeTop, (int) safeWidth, (int) safeHeight};
  834. }
  835. }
  836. #endif
  837. double dw, dh;
  838. fromPixels(pixelWidth, pixelHeight, dw, dh);
  839. return {0, 0, (int) dw, (int) dh};
  840. }
  841. bool Window::isOpen() const
  842. {
  843. return open;
  844. }
  845. void Window::setWindowTitle(const std::string &title)
  846. {
  847. this->title = title;
  848. if (window)
  849. SDL_SetWindowTitle(window, title.c_str());
  850. }
  851. const std::string &Window::getWindowTitle() const
  852. {
  853. return title;
  854. }
  855. bool Window::setIcon(love::image::ImageData *imgd)
  856. {
  857. if (!imgd)
  858. return false;
  859. if (imgd->getFormat() != PIXELFORMAT_RGBA8_UNORM)
  860. throw love::Exception("setIcon only accepts 32-bit RGBA images.");
  861. icon.set(imgd);
  862. if (!window)
  863. return false;
  864. Uint32 rmask, gmask, bmask, amask;
  865. #ifdef LOVE_BIG_ENDIAN
  866. rmask = 0xFF000000;
  867. gmask = 0x00FF0000;
  868. bmask = 0x0000FF00;
  869. amask = 0x000000FF;
  870. #else
  871. rmask = 0x000000FF;
  872. gmask = 0x0000FF00;
  873. bmask = 0x00FF0000;
  874. amask = 0xFF000000;
  875. #endif
  876. int w = imgd->getWidth();
  877. int h = imgd->getHeight();
  878. int bytesperpixel = (int) getPixelFormatBlockSize(imgd->getFormat());
  879. int pitch = w * bytesperpixel;
  880. SDL_Surface *sdlicon = nullptr;
  881. {
  882. // We don't want another thread modifying the ImageData mid-copy.
  883. love::thread::Lock lock(imgd->getMutex());
  884. sdlicon = SDL_CreateRGBSurfaceFrom(imgd->getData(), w, h, bytesperpixel * 8, pitch, rmask, gmask, bmask, amask);
  885. }
  886. if (!sdlicon)
  887. return false;
  888. SDL_SetWindowIcon(window, sdlicon);
  889. SDL_FreeSurface(sdlicon);
  890. return true;
  891. }
  892. love::image::ImageData *Window::getIcon()
  893. {
  894. return icon.get();
  895. }
  896. void Window::setVSync(int vsync)
  897. {
  898. if (glcontext != nullptr)
  899. {
  900. SDL_GL_SetSwapInterval(vsync);
  901. // Check if adaptive vsync was requested but not supported, and fall
  902. // back to regular vsync if so.
  903. if (vsync == -1 && SDL_GL_GetSwapInterval() != -1)
  904. SDL_GL_SetSwapInterval(1);
  905. }
  906. #ifdef LOVE_GRAPHICS_VULKAN
  907. love::graphics::vulkan::Vulkan::setVsync(vsync);
  908. #endif
  909. #if defined(LOVE_GRAPHICS_METAL) && defined(LOVE_MACOS)
  910. if (metalView != nullptr)
  911. {
  912. void *metallayer = SDL_Metal_GetLayer(metalView);
  913. love::macos::setMetalLayerVSync(metallayer, vsync != 0);
  914. }
  915. #endif
  916. }
  917. int Window::getVSync() const
  918. {
  919. if (glcontext != nullptr)
  920. return SDL_GL_GetSwapInterval();
  921. #if defined(LOVE_GRAPHICS_METAL)
  922. if (metalView != nullptr)
  923. {
  924. #ifdef LOVE_MACOS
  925. void *metallayer = SDL_Metal_GetLayer(metalView);
  926. return love::macos::getMetalLayerVSync(metallayer) ? 1 : 0;
  927. #else
  928. return 1;
  929. #endif
  930. }
  931. #endif
  932. return 0;
  933. }
  934. void Window::setDisplaySleepEnabled(bool enable)
  935. {
  936. if (enable)
  937. SDL_EnableScreenSaver();
  938. else
  939. SDL_DisableScreenSaver();
  940. }
  941. bool Window::isDisplaySleepEnabled() const
  942. {
  943. return SDL_IsScreenSaverEnabled() != SDL_FALSE;
  944. }
  945. void Window::minimize()
  946. {
  947. if (window != nullptr)
  948. SDL_MinimizeWindow(window);
  949. }
  950. void Window::maximize()
  951. {
  952. if (window != nullptr)
  953. {
  954. SDL_MaximizeWindow(window);
  955. updateSettings(settings, true);
  956. }
  957. }
  958. void Window::restore()
  959. {
  960. if (window != nullptr)
  961. {
  962. SDL_RestoreWindow(window);
  963. updateSettings(settings, true);
  964. }
  965. }
  966. bool Window::isMaximized() const
  967. {
  968. return window != nullptr && (SDL_GetWindowFlags(window) & SDL_WINDOW_MAXIMIZED);
  969. }
  970. bool Window::isMinimized() const
  971. {
  972. return window != nullptr && (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED);
  973. }
  974. void Window::swapBuffers()
  975. {
  976. if (glcontext)
  977. {
  978. #ifdef LOVE_WINDOWS
  979. bool useDwmFlush = false;
  980. int swapInterval = getVSync();
  981. // https://github.com/love2d/love/issues/1628
  982. // VSync can interact badly with Windows desktop composition (DWM) in windowed mode. DwmFlush can be used instead
  983. // of vsync, but it's much less flexible so we're very conservative here with where it's used:
  984. // - It won't work with exclusive or desktop fullscreen.
  985. // - DWM refreshes don't always match the refresh rate of the monitor the window is in (or the requested swap
  986. // interval), so we only use it when they do match.
  987. // - The user may force GL vsync, and DwmFlush shouldn't be used together with GL vsync.
  988. if (!settings.fullscreen && swapInterval == 1)
  989. {
  990. // Desktop composition is always enabled in Windows 8+. But DwmIsCompositionEnabled won't always return true...
  991. // (see DwmIsCompositionEnabled docs).
  992. BOOL compositionEnabled = IsWindows8OrGreater();
  993. if (compositionEnabled || (SUCCEEDED(DwmIsCompositionEnabled(&compositionEnabled)) && compositionEnabled))
  994. {
  995. DWM_TIMING_INFO info = {};
  996. info.cbSize = sizeof(DWM_TIMING_INFO);
  997. double dwmRefreshRate = 0;
  998. if (SUCCEEDED(DwmGetCompositionTimingInfo(nullptr, &info)))
  999. dwmRefreshRate = (double)info.rateRefresh.uiNumerator / (double)info.rateRefresh.uiDenominator;
  1000. SDL_DisplayMode dmode = {};
  1001. int displayindex = SDL_GetWindowDisplayIndex(window);
  1002. if (displayindex >= 0)
  1003. SDL_GetCurrentDisplayMode(displayindex, &dmode);
  1004. if (dmode.refresh_rate > 0 && dwmRefreshRate > 0 && (fabs(dmode.refresh_rate - dwmRefreshRate) < 2))
  1005. {
  1006. SDL_GL_SetSwapInterval(0);
  1007. if (SDL_GL_GetSwapInterval() == 0)
  1008. useDwmFlush = true;
  1009. else
  1010. SDL_GL_SetSwapInterval(swapInterval);
  1011. }
  1012. }
  1013. }
  1014. #endif
  1015. SDL_GL_SwapWindow(window);
  1016. #ifdef LOVE_WINDOWS
  1017. if (useDwmFlush)
  1018. {
  1019. DwmFlush();
  1020. SDL_GL_SetSwapInterval(swapInterval);
  1021. }
  1022. #endif
  1023. }
  1024. }
  1025. bool Window::hasFocus() const
  1026. {
  1027. return (window && SDL_GetKeyboardFocus() == window);
  1028. }
  1029. bool Window::hasMouseFocus() const
  1030. {
  1031. return (window && SDL_GetMouseFocus() == window);
  1032. }
  1033. bool Window::isVisible() const
  1034. {
  1035. return window && (SDL_GetWindowFlags(window) & SDL_WINDOW_SHOWN) != 0;
  1036. }
  1037. void Window::setMouseGrab(bool grab)
  1038. {
  1039. mouseGrabbed = grab;
  1040. if (window)
  1041. SDL_SetWindowGrab(window, (SDL_bool) grab);
  1042. }
  1043. bool Window::isMouseGrabbed() const
  1044. {
  1045. if (window)
  1046. return SDL_GetWindowGrab(window) != SDL_FALSE;
  1047. else
  1048. return mouseGrabbed;
  1049. }
  1050. int Window::getWidth() const
  1051. {
  1052. return windowWidth;
  1053. }
  1054. int Window::getHeight() const
  1055. {
  1056. return windowHeight;
  1057. }
  1058. int Window::getPixelWidth() const
  1059. {
  1060. return pixelWidth;
  1061. }
  1062. int Window::getPixelHeight() const
  1063. {
  1064. return pixelHeight;
  1065. }
  1066. void Window::windowToPixelCoords(double *x, double *y) const
  1067. {
  1068. if (x != nullptr)
  1069. *x = (*x) * ((double) pixelWidth / (double) windowWidth);
  1070. if (y != nullptr)
  1071. *y = (*y) * ((double) pixelHeight / (double) windowHeight);
  1072. }
  1073. void Window::pixelToWindowCoords(double *x, double *y) const
  1074. {
  1075. if (x != nullptr)
  1076. *x = (*x) * ((double) windowWidth / (double) pixelWidth);
  1077. if (y != nullptr)
  1078. *y = (*y) * ((double) windowHeight / (double) pixelHeight);
  1079. }
  1080. void Window::windowToDPICoords(double *x, double *y) const
  1081. {
  1082. double px = x != nullptr ? *x : 0.0;
  1083. double py = y != nullptr ? *y : 0.0;
  1084. windowToPixelCoords(&px, &py);
  1085. double dpix = 0.0;
  1086. double dpiy = 0.0;
  1087. fromPixels(px, py, dpix, dpiy);
  1088. if (x != nullptr)
  1089. *x = dpix;
  1090. if (y != nullptr)
  1091. *y = dpiy;
  1092. }
  1093. void Window::DPIToWindowCoords(double *x, double *y) const
  1094. {
  1095. double dpix = x != nullptr ? *x : 0.0;
  1096. double dpiy = y != nullptr ? *y : 0.0;
  1097. double px = 0.0;
  1098. double py = 0.0;
  1099. toPixels(dpix, dpiy, px, py);
  1100. pixelToWindowCoords(&px, &py);
  1101. if (x != nullptr)
  1102. *x = px;
  1103. if (y != nullptr)
  1104. *y = py;
  1105. }
  1106. double Window::getDPIScale() const
  1107. {
  1108. return settings.usedpiscale ? getNativeDPIScale() : 1.0;
  1109. }
  1110. double Window::getNativeDPIScale() const
  1111. {
  1112. #ifdef LOVE_ANDROID
  1113. return love::android::getScreenScale();
  1114. #else
  1115. return (double) pixelHeight / (double) windowHeight;
  1116. #endif
  1117. }
  1118. double Window::toPixels(double x) const
  1119. {
  1120. return x * getDPIScale();
  1121. }
  1122. void Window::toPixels(double wx, double wy, double &px, double &py) const
  1123. {
  1124. double scale = getDPIScale();
  1125. px = wx * scale;
  1126. py = wy * scale;
  1127. }
  1128. double Window::fromPixels(double x) const
  1129. {
  1130. return x / getDPIScale();
  1131. }
  1132. void Window::fromPixels(double px, double py, double &wx, double &wy) const
  1133. {
  1134. double scale = getDPIScale();
  1135. wx = px / scale;
  1136. wy = py / scale;
  1137. }
  1138. const void *Window::getHandle() const
  1139. {
  1140. return window;
  1141. }
  1142. SDL_MessageBoxFlags Window::convertMessageBoxType(MessageBoxType type) const
  1143. {
  1144. switch (type)
  1145. {
  1146. case MESSAGEBOX_ERROR:
  1147. return SDL_MESSAGEBOX_ERROR;
  1148. case MESSAGEBOX_WARNING:
  1149. return SDL_MESSAGEBOX_WARNING;
  1150. case MESSAGEBOX_INFO:
  1151. default:
  1152. return SDL_MESSAGEBOX_INFORMATION;
  1153. }
  1154. }
  1155. bool Window::showMessageBox(const std::string &title, const std::string &message, MessageBoxType type, bool attachtowindow)
  1156. {
  1157. SDL_MessageBoxFlags flags = convertMessageBoxType(type);
  1158. SDL_Window *sdlwindow = attachtowindow ? window : nullptr;
  1159. return SDL_ShowSimpleMessageBox(flags, title.c_str(), message.c_str(), sdlwindow) >= 0;
  1160. }
  1161. int Window::showMessageBox(const MessageBoxData &data)
  1162. {
  1163. SDL_MessageBoxData sdldata = {};
  1164. sdldata.flags = convertMessageBoxType(data.type);
  1165. sdldata.title = data.title.c_str();
  1166. sdldata.message = data.message.c_str();
  1167. sdldata.window = data.attachToWindow ? window : nullptr;
  1168. sdldata.numbuttons = (int) data.buttons.size();
  1169. std::vector<SDL_MessageBoxButtonData> sdlbuttons;
  1170. for (int i = 0; i < (int) data.buttons.size(); i++)
  1171. {
  1172. SDL_MessageBoxButtonData sdlbutton = {};
  1173. sdlbutton.buttonid = i;
  1174. sdlbutton.text = data.buttons[i].c_str();
  1175. if (i == data.enterButtonIndex)
  1176. sdlbutton.flags |= SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
  1177. if (i == data.escapeButtonIndex)
  1178. sdlbutton.flags |= SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
  1179. sdlbuttons.push_back(sdlbutton);
  1180. }
  1181. sdldata.buttons = &sdlbuttons[0];
  1182. int pressedbutton = -2;
  1183. SDL_ShowMessageBox(&sdldata, &pressedbutton);
  1184. return pressedbutton;
  1185. }
  1186. void Window::requestAttention(bool continuous)
  1187. {
  1188. #if defined(LOVE_WINDOWS) && !defined(LOVE_WINDOWS_UWP)
  1189. if (hasFocus())
  1190. return;
  1191. SDL_SysWMinfo wminfo = {};
  1192. SDL_VERSION(&wminfo.version);
  1193. if (SDL_GetWindowWMInfo(window, &wminfo))
  1194. {
  1195. FLASHWINFO flashinfo = {};
  1196. flashinfo.cbSize = sizeof(FLASHWINFO);
  1197. flashinfo.hwnd = wminfo.info.win.window;
  1198. flashinfo.uCount = 1;
  1199. flashinfo.dwFlags = FLASHW_ALL;
  1200. if (continuous)
  1201. {
  1202. flashinfo.uCount = 0;
  1203. flashinfo.dwFlags |= FLASHW_TIMERNOFG;
  1204. }
  1205. FlashWindowEx(&flashinfo);
  1206. }
  1207. #elif defined(LOVE_MACOS)
  1208. love::macos::requestAttention(continuous);
  1209. #else
  1210. LOVE_UNUSED(continuous);
  1211. #endif
  1212. // TODO: Linux?
  1213. }
  1214. const char *Window::getName() const
  1215. {
  1216. return "love.window.sdl";
  1217. }
  1218. } // sdl
  1219. } // window
  1220. } // love