Window.cpp 25 KB

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