Window.cpp 25 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075
  1. /**
  2. * Copyright (c) 2006-2015 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. #include "Window.h"
  24. #ifdef LOVE_ANDROID
  25. #include "common/android.h"
  26. #endif
  27. // C++
  28. #include <iostream>
  29. #include <vector>
  30. #include <algorithm>
  31. // C
  32. #include <cstdio>
  33. // SDL
  34. #include <SDL_syswm.h>
  35. #if defined(LOVE_WINDOWS)
  36. #include <windows.h>
  37. #elif defined(LOVE_MACOSX)
  38. #include "common/macosx.h"
  39. #endif
  40. #ifndef APIENTRY
  41. #define APIENTRY
  42. #endif
  43. namespace love
  44. {
  45. namespace window
  46. {
  47. namespace sdl
  48. {
  49. Window::Window()
  50. : open(false)
  51. , mouseGrabbed(false)
  52. , window(nullptr)
  53. , context(nullptr)
  54. , displayedWindowError(false)
  55. , displayedContextError(false)
  56. , hasSDL203orEarlier(false)
  57. {
  58. if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
  59. throw love::Exception("Could not initialize SDL video subsystem (%s)", SDL_GetError());
  60. // Make sure the screensaver doesn't activate by default.
  61. setDisplaySleepEnabled(false);
  62. SDL_version version = {};
  63. SDL_GetVersion(&version);
  64. hasSDL203orEarlier = (version.major == 2 && version.minor == 0 && version.patch <= 3);
  65. }
  66. Window::~Window()
  67. {
  68. close();
  69. SDL_QuitSubSystem(SDL_INIT_VIDEO);
  70. }
  71. void Window::setGLFramebufferAttributes(int msaa, bool sRGB)
  72. {
  73. // Set GL window / framebuffer attributes.
  74. SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
  75. SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
  76. SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
  77. SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
  78. SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  79. SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
  80. SDL_GL_SetAttribute(SDL_GL_RETAINED_BACKING, 0);
  81. SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, (msaa > 0) ? 1 : 0);
  82. SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, (msaa > 0) ? msaa : 0);
  83. SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, sRGB ? 1 : 0);
  84. const char *driver = SDL_GetCurrentVideoDriver();
  85. if (driver && strstr(driver, "x11") == driver)
  86. {
  87. // Always disable the sRGB flag when GLX is used with older SDL versions,
  88. // because of this bug: https://bugzilla.libsdl.org/show_bug.cgi?id=2897
  89. // In practice GLX will always give an sRGB-capable framebuffer anyway.
  90. if (hasSDL203orEarlier)
  91. SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, 0);
  92. }
  93. #if defined(LOVE_WINDOWS)
  94. // Avoid the Microsoft OpenGL 1.1 software renderer on Windows. Apparently
  95. // older Intel drivers like to use it as a fallback when requesting some
  96. // unsupported framebuffer attribute values, rather than properly failing.
  97. SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
  98. #endif
  99. }
  100. void Window::setGLContextAttributes(const ContextAttribs &attribs)
  101. {
  102. int profilemask = 0;
  103. int contextflags = 0;
  104. if (attribs.gles)
  105. profilemask = SDL_GL_CONTEXT_PROFILE_ES;
  106. else if (attribs.debug)
  107. profilemask = SDL_GL_CONTEXT_PROFILE_COMPATIBILITY;
  108. if (attribs.debug)
  109. contextflags |= SDL_GL_CONTEXT_DEBUG_FLAG;
  110. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, attribs.versionMajor);
  111. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, attribs.versionMinor);
  112. SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profilemask);
  113. SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, contextflags);
  114. }
  115. bool Window::checkGLVersion(const ContextAttribs &attribs, std::string &outversion)
  116. {
  117. typedef unsigned char GLubyte;
  118. typedef unsigned int GLenum;
  119. typedef const GLubyte *(APIENTRY *glGetStringPtr)(GLenum name);
  120. const GLenum GL_VENDOR_ENUM = 0x1F00;
  121. const GLenum GL_RENDERER_ENUM = 0x1F01;
  122. const GLenum GL_VERSION_ENUM = 0x1F02;
  123. // We don't have OpenGL headers or an automatic OpenGL function loader in
  124. // this module, so we have to get the glGetString function pointer ourselves.
  125. glGetStringPtr glGetStringFunc = (glGetStringPtr) SDL_GL_GetProcAddress("glGetString");
  126. if (!glGetStringFunc)
  127. return false;
  128. const char *glversion = (const char *) glGetStringFunc(GL_VERSION_ENUM);
  129. if (!glversion)
  130. return false;
  131. outversion = glversion;
  132. const char *glrenderer = (const char *) glGetStringFunc(GL_RENDERER_ENUM);
  133. if (glrenderer)
  134. outversion += " - " + std::string(glrenderer);
  135. const char *glvendor = (const char *) glGetStringFunc(GL_VENDOR_ENUM);
  136. if (glvendor)
  137. outversion += " (" + std::string(glvendor) + ")";
  138. int glmajor = 0;
  139. int glminor = 0;
  140. // glGetString(GL_VERSION) returns a string with the format "major.minor",
  141. // or "OpenGL ES major.minor" in GLES contexts.
  142. const char *format = "%d.%d";
  143. if (attribs.gles)
  144. format = "OpenGL ES %d.%d";
  145. if (sscanf(glversion, format, &glmajor, &glminor) != 2)
  146. return false;
  147. if (glmajor < attribs.versionMajor
  148. || (glmajor == attribs.versionMajor && glminor < attribs.versionMinor))
  149. return false;
  150. return true;
  151. }
  152. bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, int msaa)
  153. {
  154. bool preferGLES = false;
  155. #ifdef LOVE_GRAPHICS_USE_OPENGLES
  156. preferGLES = true;
  157. #endif
  158. const char *curdriver = SDL_GetCurrentVideoDriver();
  159. const char *glesdrivers[] = {"RPI", "Android", "uikit", "winrt", "emscripten"};
  160. // We always want to try OpenGL ES first on certain video backends.
  161. for (const char *glesdriver : glesdrivers)
  162. {
  163. if (curdriver && strstr(curdriver, glesdriver) == curdriver)
  164. {
  165. preferGLES = true;
  166. // Prior to SDL 2.0.4, backends that use OpenGL ES didn't properly
  167. // ask for a sRGB framebuffer when requested by SDL_GL_SetAttribute.
  168. // FIXME: This doesn't account for windowing backends that sometimes
  169. // use EGL, e.g. the X11 and windows SDL backends.
  170. if (hasSDL203orEarlier)
  171. graphics::setGammaCorrect(false);
  172. break;
  173. }
  174. }
  175. if (!preferGLES)
  176. {
  177. const char *gleshint = SDL_GetHint("LOVE_GRAPHICS_USE_OPENGLES");
  178. preferGLES = (gleshint != nullptr && gleshint[0] != '0');
  179. }
  180. // Do we want a debug context?
  181. const char *debughint = SDL_GetHint("LOVE_GRAPHICS_DEBUG");
  182. bool debug = (debughint != nullptr && debughint[0] != '0');
  183. // Different context attribute profiles to try.
  184. std::vector<ContextAttribs> attribslist = {
  185. {2, 1, false, debug}, // OpenGL 2.1.
  186. {3, 0, true, debug}, // OpenGL ES 3.
  187. {2, 0, true, debug}, // OpenGL ES 2.
  188. };
  189. // OpenGL ES 3+ contexts are only properly supported in SDL 2.0.4+.
  190. if (hasSDL203orEarlier)
  191. attribslist.erase(attribslist.begin() + 1);
  192. // Move OpenGL ES to the front of the list if we should prefer GLES.
  193. if (preferGLES)
  194. std::rotate(attribslist.begin(), attribslist.begin() + 1, attribslist.end());
  195. std::string windowerror;
  196. std::string contexterror;
  197. std::string glversion;
  198. // Unfortunately some OpenGL context settings are part of the internal
  199. // window state in the Windows and Linux SDL backends, so we have to
  200. // recreate the window when we want to change those settings...
  201. // Also, apparently some Intel drivers on Windows give back a Microsoft
  202. // OpenGL 1.1 software renderer context when high MSAA values are requested!
  203. const auto create = [&](ContextAttribs attribs) -> bool
  204. {
  205. if (context)
  206. {
  207. SDL_GL_DeleteContext(context);
  208. context = nullptr;
  209. }
  210. if (window)
  211. {
  212. SDL_DestroyWindow(window);
  213. SDL_FlushEvent(SDL_WINDOWEVENT);
  214. window = nullptr;
  215. }
  216. window = SDL_CreateWindow(title.c_str(), x, y, w, h, windowflags);
  217. if (!window)
  218. {
  219. windowerror = std::string(SDL_GetError());
  220. return false;
  221. }
  222. context = SDL_GL_CreateContext(window);
  223. if (!context)
  224. contexterror = std::string(SDL_GetError());
  225. // Make sure the context's version is at least what we requested.
  226. if (context && !checkGLVersion(attribs, glversion))
  227. {
  228. SDL_GL_DeleteContext(context);
  229. context = nullptr;
  230. }
  231. if (!context)
  232. {
  233. SDL_DestroyWindow(window);
  234. window = nullptr;
  235. return false;
  236. }
  237. return true;
  238. };
  239. // Try each context profile in order.
  240. for (ContextAttribs attribs : attribslist)
  241. {
  242. int curMSAA = msaa;
  243. bool curSRGB = love::graphics::isGammaCorrect();
  244. setGLFramebufferAttributes(curMSAA, curSRGB);
  245. setGLContextAttributes(attribs);
  246. windowerror.clear();
  247. contexterror.clear();
  248. create(attribs);
  249. if (!window && curMSAA > 0)
  250. {
  251. // The MSAA setting could have caused the failure.
  252. setGLFramebufferAttributes(0, curSRGB);
  253. if (create(attribs))
  254. curMSAA = 0;
  255. }
  256. if (!window && curSRGB)
  257. {
  258. // same with sRGB.
  259. setGLFramebufferAttributes(curMSAA, false);
  260. if (create(attribs))
  261. curSRGB = false;
  262. }
  263. if (!window && curMSAA > 0 && curSRGB)
  264. {
  265. // Or both!
  266. setGLFramebufferAttributes(0, false);
  267. if (create(attribs))
  268. {
  269. curMSAA = 0;
  270. curSRGB = false;
  271. }
  272. }
  273. if (window && context)
  274. {
  275. love::graphics::setGammaCorrect(curSRGB);
  276. break;
  277. }
  278. }
  279. if (!context || !window)
  280. {
  281. if (!windowerror.empty())
  282. {
  283. std::string title = "Unable to create window";
  284. std::string message = "SDL error: " + windowerror;
  285. std::cerr << title << std::endl << message << std::endl;
  286. // Display a message box with the error, but only once.
  287. if (!displayedWindowError)
  288. {
  289. showMessageBox(title, message, MESSAGEBOX_ERROR, false);
  290. displayedWindowError = true;
  291. }
  292. }
  293. else if (!context)
  294. {
  295. std::string title = "Unable to initialize OpenGL";
  296. std::string message = "This program requires a graphics card and video drivers which support OpenGL 2.1 or OpenGL ES 2.";
  297. if (!contexterror.empty())
  298. message += "\n\nOpenGL context creation error: " + contexterror;
  299. if (!glversion.empty())
  300. message += " \n\nDetected OpenGL version: " + glversion;
  301. std::cerr << title << std::endl << message << std::endl;
  302. // Display a message box with the error, but only once.
  303. if (!displayedContextError)
  304. {
  305. showMessageBox(title, message, MESSAGEBOX_ERROR, true);
  306. displayedContextError = true;
  307. }
  308. }
  309. close();
  310. return false;
  311. }
  312. open = true;
  313. return true;
  314. }
  315. bool Window::setWindow(int width, int height, WindowSettings *settings)
  316. {
  317. WindowSettings f;
  318. if (settings)
  319. f = *settings;
  320. f.minwidth = std::max(f.minwidth, 1);
  321. f.minheight = std::max(f.minheight, 1);
  322. f.display = std::min(std::max(f.display, 0), getDisplayCount() - 1);
  323. // Use the desktop resolution if a width or height of 0 is specified.
  324. if (width == 0 || height == 0)
  325. {
  326. SDL_DisplayMode mode = {};
  327. SDL_GetDesktopDisplayMode(f.display, &mode);
  328. width = mode.w;
  329. height = mode.h;
  330. }
  331. Uint32 sdlflags = SDL_WINDOW_OPENGL;
  332. // On Android we always must have fullscreen type FULLSCREEN_TYPE_DESKTOP
  333. #ifdef LOVE_ANDROID
  334. f.fstype = FULLSCREEN_DESKTOP;
  335. #endif
  336. if (f.fullscreen)
  337. {
  338. if (f.fstype == FULLSCREEN_DESKTOP)
  339. sdlflags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
  340. else
  341. {
  342. sdlflags |= SDL_WINDOW_FULLSCREEN;
  343. SDL_DisplayMode mode = {0, width, height, 0, 0};
  344. // Fullscreen window creation will bug out if no mode can be used.
  345. if (SDL_GetClosestDisplayMode(f.display, &mode, &mode) == nullptr)
  346. {
  347. // GetClosestDisplayMode will fail if we request a size larger
  348. // than the largest available display mode, so we'll try to use
  349. // the largest (first) mode in that case.
  350. if (SDL_GetDisplayMode(f.display, 0, &mode) < 0)
  351. return false;
  352. }
  353. width = mode.w;
  354. height = mode.h;
  355. }
  356. }
  357. if (f.resizable)
  358. sdlflags |= SDL_WINDOW_RESIZABLE;
  359. if (f.borderless)
  360. sdlflags |= SDL_WINDOW_BORDERLESS;
  361. if (f.highdpi)
  362. sdlflags |= SDL_WINDOW_ALLOW_HIGHDPI;
  363. int x = f.x;
  364. int y = f.y;
  365. if (f.useposition && !f.fullscreen)
  366. {
  367. // The position needs to be in the global coordinate space.
  368. SDL_Rect displaybounds = {};
  369. SDL_GetDisplayBounds(f.display, &displaybounds);
  370. x += displaybounds.x;
  371. y += displaybounds.y;
  372. }
  373. else
  374. {
  375. if (f.centered)
  376. x = y = SDL_WINDOWPOS_CENTERED_DISPLAY(f.display);
  377. else
  378. x = y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(f.display);
  379. }
  380. close();
  381. if (!createWindowAndContext(x, y, width, height, sdlflags, f.msaa))
  382. return false;
  383. // Make sure the window keeps any previously set icon.
  384. setIcon(icon.get());
  385. // Make sure the mouse keeps its previous grab setting.
  386. setMouseGrab(mouseGrabbed);
  387. // Enforce minimum window dimensions.
  388. SDL_SetWindowMinimumSize(window, f.minwidth, f.minheight);
  389. if ((f.useposition || f.centered) && !f.fullscreen)
  390. SDL_SetWindowPosition(window, x, y);
  391. SDL_RaiseWindow(window);
  392. SDL_GL_SetSwapInterval(f.vsync ? 1 : 0);
  393. updateSettings(f);
  394. auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
  395. if (gfx != nullptr)
  396. gfx->setMode(pixelWidth, pixelHeight);
  397. #ifdef LOVE_ANDROID
  398. love::android::setImmersive(f.fullscreen);
  399. #endif
  400. return true;
  401. }
  402. bool Window::onSizeChanged(int width, int height)
  403. {
  404. if (!window)
  405. return false;
  406. windowWidth = width;
  407. windowHeight = height;
  408. SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight);
  409. auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
  410. if (gfx != nullptr)
  411. gfx->setViewportSize(pixelWidth, pixelHeight);
  412. return true;
  413. }
  414. void Window::updateSettings(const WindowSettings &newsettings)
  415. {
  416. Uint32 wflags = SDL_GetWindowFlags(window);
  417. // Set the new display mode as the current display mode.
  418. SDL_GetWindowSize(window, &windowWidth, &windowHeight);
  419. SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight);
  420. if ((wflags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)
  421. {
  422. settings.fullscreen = true;
  423. settings.fstype = FULLSCREEN_DESKTOP;
  424. }
  425. else if ((wflags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)
  426. {
  427. settings.fullscreen = true;
  428. settings.fstype = FULLSCREEN_EXCLUSIVE;
  429. }
  430. else
  431. {
  432. settings.fullscreen = false;
  433. settings.fstype = newsettings.fstype;
  434. }
  435. #ifdef LOVE_ANDROID
  436. settings.fullscreen = love::android::getImmersive();
  437. #endif
  438. // SDL_GetWindowMinimumSize gives back 0,0 sometimes...
  439. settings.minwidth = newsettings.minwidth;
  440. settings.minheight = newsettings.minheight;
  441. settings.resizable = (wflags & SDL_WINDOW_RESIZABLE) != 0;
  442. settings.borderless = (wflags & SDL_WINDOW_BORDERLESS) != 0;
  443. settings.centered = newsettings.centered;
  444. getPosition(settings.x, settings.y, settings.display);
  445. settings.highdpi = (wflags & SDL_WINDOW_ALLOW_HIGHDPI) != 0;
  446. // Only minimize on focus loss if the window is in exclusive-fullscreen mode
  447. if (settings.fullscreen && settings.fstype == FULLSCREEN_EXCLUSIVE)
  448. SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "1");
  449. else
  450. SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
  451. // Verify MSAA setting.
  452. int buffers = 0;
  453. int samples = 0;
  454. SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &buffers);
  455. SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &samples);
  456. settings.msaa = (buffers > 0 ? samples : 0);
  457. settings.vsync = SDL_GL_GetSwapInterval() != 0;
  458. SDL_DisplayMode dmode = {};
  459. SDL_GetCurrentDisplayMode(settings.display, &dmode);
  460. // May be 0 if the refresh rate can't be determined.
  461. settings.refreshrate = (double) dmode.refresh_rate;
  462. }
  463. void Window::getWindow(int &width, int &height, WindowSettings &newsettings)
  464. {
  465. // The window might have been modified (moved, resized, etc.) by the user.
  466. if (window)
  467. updateSettings(settings);
  468. width = windowWidth;
  469. height = windowHeight;
  470. newsettings = settings;
  471. }
  472. void Window::close()
  473. {
  474. auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
  475. if (gfx != nullptr)
  476. gfx->unSetMode();
  477. if (context)
  478. {
  479. SDL_GL_DeleteContext(context);
  480. context = nullptr;
  481. }
  482. if (window)
  483. {
  484. SDL_DestroyWindow(window);
  485. window = nullptr;
  486. // The old window may have generated pending events which are no longer
  487. // relevant. Destroy them all!
  488. SDL_FlushEvent(SDL_WINDOWEVENT);
  489. }
  490. open = false;
  491. }
  492. bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype)
  493. {
  494. if (!window)
  495. return false;
  496. WindowSettings newsettings = settings;
  497. newsettings.fullscreen = fullscreen;
  498. newsettings.fstype = fstype;
  499. Uint32 sdlflags = 0;
  500. if (fullscreen)
  501. {
  502. if (fstype == FULLSCREEN_DESKTOP)
  503. sdlflags = SDL_WINDOW_FULLSCREEN_DESKTOP;
  504. else
  505. {
  506. sdlflags = SDL_WINDOW_FULLSCREEN;
  507. SDL_DisplayMode mode = {};
  508. mode.w = windowWidth;
  509. mode.h = windowHeight;
  510. SDL_GetClosestDisplayMode(SDL_GetWindowDisplayIndex(window), &mode, &mode);
  511. SDL_SetWindowDisplayMode(window, &mode);
  512. }
  513. }
  514. #ifdef LOVE_ANDROID
  515. love::android::setImmersive(fullscreen);
  516. #endif
  517. if (SDL_SetWindowFullscreen(window, sdlflags) == 0)
  518. {
  519. SDL_GL_MakeCurrent(window, context);
  520. updateSettings(newsettings);
  521. // Apparently this gets un-set when we exit fullscreen (at least in OS X).
  522. if (!fullscreen)
  523. SDL_SetWindowMinimumSize(window, settings.minwidth, settings.minheight);
  524. // Update the viewport size now instead of waiting for event polling.
  525. auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
  526. if (gfx != nullptr)
  527. gfx->setViewportSize(pixelWidth, pixelHeight);
  528. return true;
  529. }
  530. return false;
  531. }
  532. bool Window::setFullscreen(bool fullscreen)
  533. {
  534. return setFullscreen(fullscreen, settings.fstype);
  535. }
  536. int Window::getDisplayCount() const
  537. {
  538. return SDL_GetNumVideoDisplays();
  539. }
  540. const char *Window::getDisplayName(int displayindex) const
  541. {
  542. const char *name = SDL_GetDisplayName(displayindex);
  543. if (name == nullptr)
  544. throw love::Exception("Invalid display index: %d", displayindex + 1);
  545. return name;
  546. }
  547. typedef Window::WindowSize WindowSize;
  548. std::vector<WindowSize> Window::getFullscreenSizes(int displayindex) const
  549. {
  550. std::vector<WindowSize> sizes;
  551. for (int i = 0; i < SDL_GetNumDisplayModes(displayindex); i++)
  552. {
  553. SDL_DisplayMode mode = {};
  554. SDL_GetDisplayMode(displayindex, i, &mode);
  555. WindowSize w = {mode.w, mode.h};
  556. // SDL2's display mode list has multiple entries for modes of the same
  557. // size with different bits per pixel, so we need to filter those out.
  558. if (std::find(sizes.begin(), sizes.end(), w) == sizes.end())
  559. sizes.push_back(w);
  560. }
  561. return sizes;
  562. }
  563. void Window::getDesktopDimensions(int displayindex, int &width, int &height) const
  564. {
  565. if (displayindex >= 0 && displayindex < getDisplayCount())
  566. {
  567. SDL_DisplayMode mode = {};
  568. SDL_GetDesktopDisplayMode(displayindex, &mode);
  569. width = mode.w;
  570. height = mode.h;
  571. }
  572. else
  573. {
  574. width = 0;
  575. height = 0;
  576. }
  577. }
  578. void Window::setPosition(int x, int y, int displayindex)
  579. {
  580. if (!window)
  581. return;
  582. displayindex = std::min(std::max(displayindex, 0), getDisplayCount() - 1);
  583. SDL_Rect displaybounds = {};
  584. SDL_GetDisplayBounds(displayindex, &displaybounds);
  585. // The position needs to be in the global coordinate space.
  586. x += displaybounds.x;
  587. y += displaybounds.y;
  588. SDL_SetWindowPosition(window, x, y);
  589. settings.useposition = true;
  590. }
  591. void Window::getPosition(int &x, int &y, int &displayindex)
  592. {
  593. if (!window)
  594. {
  595. x = y = 0;
  596. displayindex = 0;
  597. return;
  598. }
  599. displayindex = std::max(SDL_GetWindowDisplayIndex(window), 0);
  600. SDL_GetWindowPosition(window, &x, &y);
  601. // In SDL <= 2.0.3, fullscreen windows are always reported as 0,0. In every
  602. // other case we need to convert the position from global coordinates to the
  603. // monitor's coordinate space.
  604. if (x != 0 || y != 0)
  605. {
  606. SDL_Rect displaybounds = {};
  607. SDL_GetDisplayBounds(displayindex, &displaybounds);
  608. x -= displaybounds.x;
  609. y -= displaybounds.y;
  610. }
  611. }
  612. bool Window::isOpen() const
  613. {
  614. return open;
  615. }
  616. void Window::setWindowTitle(const std::string &title)
  617. {
  618. this->title = title;
  619. if (window)
  620. SDL_SetWindowTitle(window, title.c_str());
  621. }
  622. const std::string &Window::getWindowTitle() const
  623. {
  624. return title;
  625. }
  626. bool Window::setIcon(love::image::ImageData *imgd)
  627. {
  628. if (!imgd)
  629. return false;
  630. icon.set(imgd);
  631. if (!window)
  632. return false;
  633. Uint32 rmask, gmask, bmask, amask;
  634. #ifdef LOVE_BIG_ENDIAN
  635. rmask = 0xFF000000;
  636. gmask = 0x00FF0000;
  637. bmask = 0x0000FF00;
  638. amask = 0x000000FF;
  639. #else
  640. rmask = 0x000000FF;
  641. gmask = 0x0000FF00;
  642. bmask = 0x00FF0000;
  643. amask = 0xFF000000;
  644. #endif
  645. int w = imgd->getWidth();
  646. int h = imgd->getHeight();
  647. int pitch = imgd->getWidth() * 4;
  648. SDL_Surface *sdlicon = 0;
  649. {
  650. // We don't want another thread modifying the ImageData mid-copy.
  651. love::thread::Lock lock(imgd->getMutex());
  652. sdlicon = SDL_CreateRGBSurfaceFrom(imgd->getData(), w, h, 32, pitch, rmask, gmask, bmask, amask);
  653. }
  654. if (!sdlicon)
  655. return false;
  656. SDL_SetWindowIcon(window, sdlicon);
  657. SDL_FreeSurface(sdlicon);
  658. return true;
  659. }
  660. love::image::ImageData *Window::getIcon()
  661. {
  662. return icon.get();
  663. }
  664. void Window::setDisplaySleepEnabled(bool enable)
  665. {
  666. if (enable)
  667. SDL_EnableScreenSaver();
  668. else
  669. SDL_DisableScreenSaver();
  670. }
  671. bool Window::isDisplaySleepEnabled() const
  672. {
  673. return SDL_IsScreenSaverEnabled() != SDL_FALSE;
  674. }
  675. void Window::minimize()
  676. {
  677. if (window != nullptr)
  678. SDL_MinimizeWindow(window);
  679. }
  680. void Window::maximize()
  681. {
  682. if (window != nullptr)
  683. SDL_MaximizeWindow(window);
  684. }
  685. void Window::swapBuffers()
  686. {
  687. SDL_GL_SwapWindow(window);
  688. }
  689. bool Window::hasFocus() const
  690. {
  691. return (window && SDL_GetKeyboardFocus() == window);
  692. }
  693. bool Window::hasMouseFocus() const
  694. {
  695. return (window && SDL_GetMouseFocus() == window);
  696. }
  697. bool Window::isVisible() const
  698. {
  699. return window && (SDL_GetWindowFlags(window) & SDL_WINDOW_SHOWN) != 0;
  700. }
  701. void Window::setMouseVisible(bool visible)
  702. {
  703. SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE);
  704. }
  705. bool Window::getMouseVisible() const
  706. {
  707. return (SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE);
  708. }
  709. void Window::setMouseGrab(bool grab)
  710. {
  711. mouseGrabbed = grab;
  712. if (window)
  713. SDL_SetWindowGrab(window, (SDL_bool) grab);
  714. }
  715. bool Window::isMouseGrabbed() const
  716. {
  717. if (window)
  718. return SDL_GetWindowGrab(window) != SDL_FALSE;
  719. else
  720. return mouseGrabbed;
  721. }
  722. void Window::getPixelDimensions(int &w, int &h) const
  723. {
  724. w = pixelWidth;
  725. h = pixelHeight;
  726. }
  727. void Window::windowToPixelCoords(double *x, double *y) const
  728. {
  729. if (x != nullptr)
  730. *x = (*x) * ((double) pixelWidth / (double) windowWidth);
  731. if (y != nullptr)
  732. *y = (*y) * ((double) pixelHeight / (double) windowHeight);
  733. }
  734. void Window::pixelToWindowCoords(double *x, double *y) const
  735. {
  736. if (x != nullptr)
  737. *x = (*x) * ((double) windowWidth / (double) pixelWidth);
  738. if (y != nullptr)
  739. *y = (*y) * ((double) windowHeight / (double) pixelHeight);
  740. }
  741. double Window::getPixelScale() const
  742. {
  743. #ifdef LOVE_ANDROID
  744. return love::android::getScreenScale();
  745. #else
  746. return (double) pixelHeight / (double) windowHeight;
  747. #endif
  748. }
  749. double Window::toPixels(double x) const
  750. {
  751. return x * getPixelScale();
  752. }
  753. void Window::toPixels(double wx, double wy, double &px, double &py) const
  754. {
  755. double scale = getPixelScale();
  756. px = wx * scale;
  757. py = wy * scale;
  758. }
  759. double Window::fromPixels(double x) const
  760. {
  761. return x / getPixelScale();
  762. }
  763. void Window::fromPixels(double px, double py, double &wx, double &wy) const
  764. {
  765. double scale = getPixelScale();
  766. wx = px / scale;
  767. wy = py / scale;
  768. }
  769. const void *Window::getHandle() const
  770. {
  771. return window;
  772. }
  773. SDL_MessageBoxFlags Window::convertMessageBoxType(MessageBoxType type) const
  774. {
  775. switch (type)
  776. {
  777. case MESSAGEBOX_ERROR:
  778. return SDL_MESSAGEBOX_ERROR;
  779. case MESSAGEBOX_WARNING:
  780. return SDL_MESSAGEBOX_WARNING;
  781. case MESSAGEBOX_INFO:
  782. default:
  783. return SDL_MESSAGEBOX_INFORMATION;
  784. }
  785. }
  786. bool Window::showMessageBox(const std::string &title, const std::string &message, MessageBoxType type, bool attachtowindow)
  787. {
  788. SDL_MessageBoxFlags flags = convertMessageBoxType(type);
  789. SDL_Window *sdlwindow = attachtowindow ? window : nullptr;
  790. return SDL_ShowSimpleMessageBox(flags, title.c_str(), message.c_str(), sdlwindow) >= 0;
  791. }
  792. int Window::showMessageBox(const MessageBoxData &data)
  793. {
  794. SDL_MessageBoxData sdldata = {};
  795. sdldata.flags = convertMessageBoxType(data.type);
  796. sdldata.title = data.title.c_str();
  797. sdldata.message = data.message.c_str();
  798. sdldata.window = data.attachToWindow ? window : nullptr;
  799. sdldata.numbuttons = (int) data.buttons.size();
  800. std::vector<SDL_MessageBoxButtonData> sdlbuttons;
  801. for (int i = 0; i < (int) data.buttons.size(); i++)
  802. {
  803. SDL_MessageBoxButtonData sdlbutton = {};
  804. sdlbutton.buttonid = i;
  805. sdlbutton.text = data.buttons[i].c_str();
  806. if (i == data.enterButtonIndex)
  807. sdlbutton.flags |= SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
  808. if (i == data.escapeButtonIndex)
  809. sdlbutton.flags |= SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
  810. sdlbuttons.push_back(sdlbutton);
  811. }
  812. sdldata.buttons = &sdlbuttons[0];
  813. int pressedbutton = -2;
  814. SDL_ShowMessageBox(&sdldata, &pressedbutton);
  815. return pressedbutton;
  816. }
  817. void Window::requestAttention(bool continuous)
  818. {
  819. #if defined(LOVE_WINDOWS)
  820. if (hasFocus())
  821. return;
  822. SDL_SysWMinfo wminfo = {};
  823. SDL_VERSION(&wminfo.version);
  824. if (SDL_GetWindowWMInfo(window, &wminfo))
  825. {
  826. FLASHWINFO flashinfo = {};
  827. flashinfo.cbSize = sizeof(FLASHWINFO);
  828. flashinfo.hwnd = wminfo.info.win.window;
  829. flashinfo.uCount = 1;
  830. flashinfo.dwFlags = FLASHW_ALL;
  831. if (continuous)
  832. {
  833. flashinfo.uCount = 0;
  834. flashinfo.dwFlags |= FLASHW_TIMERNOFG;
  835. }
  836. FlashWindowEx(&flashinfo);
  837. }
  838. #elif defined(LOVE_MACOSX)
  839. love::macosx::requestAttention(continuous);
  840. #else
  841. LOVE_UNUSED(continuous);
  842. #endif
  843. // TODO: Linux?
  844. }
  845. const char *Window::getName() const
  846. {
  847. return "love.window.sdl";
  848. }
  849. } // sdl
  850. } // window
  851. } // love