Window.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. /**
  2. * Copyright (c) 2006-2013 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. // C++
  25. #include <iostream>
  26. #include <vector>
  27. #include <algorithm>
  28. namespace love
  29. {
  30. namespace window
  31. {
  32. namespace sdl
  33. {
  34. Window::Window()
  35. : windowTitle("")
  36. , created(false)
  37. , mouseGrabbed(false)
  38. , window(0)
  39. , context(0)
  40. {
  41. if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
  42. throw love::Exception("%s", SDL_GetError());
  43. }
  44. Window::~Window()
  45. {
  46. if (curMode.icon)
  47. curMode.icon->release();
  48. if (window)
  49. SDL_DestroyWindow(window);
  50. if (context)
  51. SDL_GL_DeleteContext(context);
  52. SDL_QuitSubSystem(SDL_INIT_VIDEO);
  53. }
  54. Window::_currentMode::_currentMode()
  55. : width(800)
  56. , height(600)
  57. , attribs()
  58. , icon(0)
  59. {
  60. }
  61. bool Window::setWindow(int width, int height, WindowAttributes *attribs)
  62. {
  63. graphics::Graphics *gfx = (graphics::Graphics *) Module::findInstance("love.graphics.");
  64. if (gfx)
  65. gfx->unSetMode();
  66. WindowAttributes f;
  67. if (attribs)
  68. f = *attribs;
  69. f.minwidth = std::max(f.minwidth, 1);
  70. f.minheight = std::max(f.minheight, 1);
  71. f.display = std::min(std::max(f.display, 0), getDisplayCount());
  72. // Use the desktop resolution if a width or height of 0 is specified.
  73. if (width == 0 || height == 0)
  74. {
  75. SDL_DisplayMode mode = {};
  76. SDL_GetDesktopDisplayMode(f.display, &mode);
  77. width = mode.w;
  78. height = mode.h;
  79. }
  80. Uint32 sdlflags = SDL_WINDOW_OPENGL;
  81. if (f.fullscreen)
  82. {
  83. if (f.fstype == FULLSCREEN_TYPE_DESKTOP)
  84. sdlflags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
  85. else
  86. {
  87. sdlflags |= SDL_WINDOW_FULLSCREEN;
  88. // Fullscreen window creation will bug out if no mode can be used.
  89. SDL_DisplayMode mode = {0, width, height, 0, 0};
  90. if (SDL_GetClosestDisplayMode(f.display, &mode, &mode) == 0)
  91. return false;
  92. }
  93. }
  94. if (f.resizable)
  95. sdlflags |= SDL_WINDOW_RESIZABLE;
  96. if (f.borderless)
  97. sdlflags |= SDL_WINDOW_BORDERLESS;
  98. // Destroy and recreate the window if the dimensions or flags have changed.
  99. if (window)
  100. {
  101. int curdisplay = SDL_GetWindowDisplayIndex(window);
  102. Uint32 wflags = SDL_GetWindowFlags(window);
  103. wflags &= (SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_RESIZABLE | SDL_WINDOW_BORDERLESS);
  104. if (sdlflags != wflags || width != curMode.width || height != curMode.height
  105. || f.display != curdisplay || f.fsaa != curMode.attribs.fsaa)
  106. {
  107. SDL_DestroyWindow(window);
  108. window = 0;
  109. // The old window may have generated pending events which are no
  110. // longer relevant. Destroy them all!
  111. SDL_FlushEvent(SDL_WINDOWEVENT);
  112. }
  113. }
  114. int centeredpos = SDL_WINDOWPOS_CENTERED_DISPLAY(f.display);
  115. int uncenteredpos = SDL_WINDOWPOS_UNDEFINED_DISPLAY(f.display);
  116. if (!window)
  117. {
  118. // In Windows and Linux, some GL attributes are set on window creation.
  119. setWindowGLAttributes(f.fsaa);
  120. const char *title = windowTitle.c_str();
  121. int pos = f.centered ? centeredpos : uncenteredpos;
  122. window = SDL_CreateWindow(title, pos, pos, width, height, sdlflags);
  123. if (!window && f.fsaa > 0)
  124. {
  125. // FSAA might have caused the failure, disable it and try again.
  126. SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
  127. SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
  128. window = SDL_CreateWindow(title, pos, pos, width, height, sdlflags);
  129. f.fsaa = 0;
  130. }
  131. // Make sure the window keeps any previously set icon.
  132. if (window && curMode.icon)
  133. setIcon(curMode.icon);
  134. }
  135. if (!window)
  136. {
  137. std::cerr << "Could not set video mode: " << SDL_GetError() << std::endl;
  138. return false;
  139. }
  140. // Enforce minimum window dimensions.
  141. SDL_SetWindowMinimumSize(window, f.minwidth, f.minheight);
  142. if (f.centered && !f.fullscreen)
  143. SDL_SetWindowPosition(window, centeredpos, centeredpos);
  144. SDL_RaiseWindow(window);
  145. if (!setContext(f.fsaa, f.vsync))
  146. return false;
  147. created = true;
  148. updateAttributes(f);
  149. if (gfx)
  150. gfx->setMode(curMode.width, curMode.height);
  151. // Make sure the mouse keeps its previous grab setting.
  152. setMouseGrab(mouseGrabbed);
  153. return true;
  154. }
  155. bool Window::onWindowResize(int width, int height)
  156. {
  157. if (!window)
  158. return false;
  159. curMode.width = width;
  160. curMode.height = height;
  161. return true;
  162. }
  163. bool Window::setContext(int fsaa, bool vsync)
  164. {
  165. // We would normally only need to recreate the context if FSAA changes or
  166. // SDL_GL_MakeCurrent is unsuccessful, but in Windows MakeCurrent can
  167. // sometimes claim success but the context will actually be trashed.
  168. if (context)
  169. {
  170. SDL_GL_DeleteContext(context);
  171. context = 0;
  172. }
  173. // Make sure the proper attributes are set.
  174. setWindowGLAttributes(fsaa);
  175. context = SDL_GL_CreateContext(window);
  176. if (!context && fsaa > 0)
  177. {
  178. // FSAA might have caused the failure, disable it and try again.
  179. SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
  180. SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
  181. context = SDL_GL_CreateContext(window);
  182. }
  183. if (!context)
  184. {
  185. std::cerr << "Could not set video mode: " << SDL_GetError() << std::endl;
  186. return false;
  187. }
  188. // Set vertical synchronization.
  189. SDL_GL_SetSwapInterval(vsync ? 1 : 0);
  190. // Verify FSAA setting.
  191. int buffers;
  192. int samples;
  193. SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &buffers);
  194. SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &samples);
  195. // Don't fail because of this, but issue a warning.
  196. if ((!buffers && fsaa) || (samples != fsaa))
  197. {
  198. std::cerr << "Warning, FSAA setting failed! (Result: buffers: " << buffers << ", samples: " << samples << ")" << std::endl;
  199. fsaa = (buffers > 0) ? samples : 0;
  200. }
  201. curMode.attribs.fsaa = fsaa;
  202. curMode.attribs.vsync = SDL_GL_GetSwapInterval() != 0;
  203. return true;
  204. }
  205. void Window::setWindowGLAttributes(int fsaa) const
  206. {
  207. // Set GL window attributes.
  208. SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
  209. SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
  210. SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
  211. SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
  212. SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  213. SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
  214. // FSAA.
  215. SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, (fsaa > 0) ? 1 : 0);
  216. SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, (fsaa > 0) ? fsaa : 0);
  217. }
  218. void Window::updateAttributes(const WindowAttributes &newattribs)
  219. {
  220. Uint32 wflags = SDL_GetWindowFlags(window);
  221. // Set the new display mode as the current display mode.
  222. SDL_GetWindowSize(window, &curMode.width, &curMode.height);
  223. if ((wflags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)
  224. {
  225. curMode.attribs.fullscreen = true;
  226. curMode.attribs.fstype = FULLSCREEN_TYPE_DESKTOP;
  227. }
  228. else if ((wflags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)
  229. {
  230. curMode.attribs.fullscreen = true;
  231. curMode.attribs.fstype = FULLSCREEN_TYPE_NORMAL;
  232. }
  233. else
  234. {
  235. curMode.attribs.fullscreen = false;
  236. curMode.attribs.fstype = newattribs.fstype;
  237. }
  238. // The min width/height is set to 0 internally in SDL when in fullscreen.
  239. if (curMode.attribs.fullscreen)
  240. {
  241. curMode.attribs.minwidth = newattribs.minwidth;
  242. curMode.attribs.minheight = newattribs.minheight;
  243. }
  244. else
  245. SDL_GetWindowMinimumSize(window, &curMode.attribs.minwidth, &curMode.attribs.minheight);
  246. curMode.attribs.resizable = (wflags & SDL_WINDOW_RESIZABLE) != 0;
  247. curMode.attribs.borderless = (wflags & SDL_WINDOW_BORDERLESS) != 0;
  248. curMode.attribs.centered = newattribs.centered;
  249. curMode.attribs.display = std::max(SDL_GetWindowDisplayIndex(window), 0);
  250. // Only minimize on focus loss if the window is in exclusive-fullscreen
  251. // mode (mimics behaviour of SDL 2.0.2+).
  252. // In OS X we always disable this to prevent dock minimization weirdness.
  253. #ifndef LOVE_MACOSX
  254. if (curMode.attribs.fullscreen && curMode.attribs.fstype == FULLSCREEN_TYPE_NORMAL)
  255. SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "1");
  256. else
  257. #endif
  258. SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
  259. }
  260. void Window::getWindow(int &width, int &height, WindowAttributes &attribs)
  261. {
  262. // Window position may be different from creation - update display index.
  263. if (window)
  264. curMode.attribs.display = std::max(SDL_GetWindowDisplayIndex(window), 0);
  265. width = curMode.width;
  266. height = curMode.height;
  267. attribs = curMode.attribs;
  268. }
  269. bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype)
  270. {
  271. if (!window)
  272. return false;
  273. WindowAttributes newattribs = curMode.attribs;
  274. newattribs.fullscreen = fullscreen;
  275. newattribs.fstype = fstype;
  276. Uint32 sdlflags = 0;
  277. if (fullscreen)
  278. {
  279. if (fstype == FULLSCREEN_TYPE_DESKTOP)
  280. sdlflags = SDL_WINDOW_FULLSCREEN_DESKTOP;
  281. else
  282. {
  283. sdlflags = SDL_WINDOW_FULLSCREEN;
  284. SDL_DisplayMode mode = {};
  285. mode.w = curMode.width;
  286. mode.h = curMode.height;
  287. SDL_GetClosestDisplayMode(SDL_GetWindowDisplayIndex(window), &mode, &mode);
  288. SDL_SetWindowDisplayMode(window, &mode);
  289. }
  290. }
  291. if (SDL_SetWindowFullscreen(window, sdlflags) == 0)
  292. {
  293. SDL_GL_MakeCurrent(window, context);
  294. updateAttributes(newattribs);
  295. // Update the viewport size now instead of waiting for event polling.
  296. graphics::Graphics *gfx = (graphics::Graphics *) Module::findInstance("love.graphics.");
  297. if (gfx)
  298. gfx->setViewportSize(curMode.width, curMode.height);
  299. return true;
  300. }
  301. return false;
  302. }
  303. bool Window::setFullscreen(bool fullscreen)
  304. {
  305. return setFullscreen(fullscreen, curMode.attribs.fstype);
  306. }
  307. int Window::getDisplayCount() const
  308. {
  309. return SDL_GetNumVideoDisplays();
  310. }
  311. typedef Window::WindowSize WindowSize;
  312. std::vector<WindowSize> Window::getFullscreenSizes(int displayindex) const
  313. {
  314. std::vector<WindowSize> sizes;
  315. SDL_DisplayMode mode = {};
  316. std::vector<WindowSize>::const_iterator it;
  317. for (int i = 0; i < SDL_GetNumDisplayModes(displayindex); i++)
  318. {
  319. SDL_GetDisplayMode(displayindex, i, &mode);
  320. // SDL2's display mode list has multiple entries for modes of the same
  321. // size with different bits per pixel, so we need to filter those out.
  322. bool alreadyhassize = false;
  323. for (it = sizes.begin(); it != sizes.end(); ++it)
  324. {
  325. if (it->width == mode.w && it->height == mode.h)
  326. {
  327. alreadyhassize = true;
  328. break;
  329. }
  330. }
  331. if (!alreadyhassize)
  332. {
  333. WindowSize w = {mode.w, mode.h};
  334. sizes.push_back(w);
  335. }
  336. }
  337. return sizes;
  338. }
  339. int Window::getWidth() const
  340. {
  341. return curMode.width;
  342. }
  343. int Window::getHeight() const
  344. {
  345. return curMode.height;
  346. }
  347. void Window::getDesktopDimensions(int displayindex, int &width, int &height) const
  348. {
  349. if (displayindex >= 0 && displayindex < getDisplayCount())
  350. {
  351. SDL_DisplayMode mode = {};
  352. SDL_GetDesktopDisplayMode(displayindex, &mode);
  353. width = mode.w;
  354. height = mode.h;
  355. }
  356. else
  357. {
  358. width = 0;
  359. height = 0;
  360. }
  361. }
  362. bool Window::isCreated() const
  363. {
  364. return created;
  365. }
  366. void Window::setWindowTitle(const std::string &title)
  367. {
  368. windowTitle = title;
  369. if (window)
  370. SDL_SetWindowTitle(window, title.c_str());
  371. }
  372. const std::string &Window::getWindowTitle() const
  373. {
  374. return windowTitle;
  375. }
  376. bool Window::setIcon(love::image::ImageData *imgd)
  377. {
  378. if (!imgd)
  379. return false;
  380. imgd->retain();
  381. if (curMode.icon)
  382. curMode.icon->release();
  383. curMode.icon = imgd;
  384. if (!window)
  385. return false;
  386. Uint32 rmask, gmask, bmask, amask;
  387. #ifdef LOVE_BIG_ENDIAN
  388. rmask = 0xFF000000;
  389. gmask = 0x00FF0000;
  390. bmask = 0x0000FF00;
  391. amask = 0x000000FF;
  392. #else
  393. rmask = 0x000000FF;
  394. gmask = 0x0000FF00;
  395. bmask = 0x00FF0000;
  396. amask = 0xFF000000;
  397. #endif
  398. int w = imgd->getWidth();
  399. int h = imgd->getHeight();
  400. int pitch = imgd->getWidth() * 4;
  401. SDL_Surface *sdlicon = 0;
  402. {
  403. // We don't want another thread modifying the ImageData mid-copy.
  404. love::thread::Lock lock(imgd->getMutex());
  405. sdlicon = SDL_CreateRGBSurfaceFrom(imgd->getData(), w, h, 32, pitch, rmask, gmask, bmask, amask);
  406. }
  407. if (!sdlicon)
  408. return false;
  409. SDL_SetWindowIcon(window, sdlicon);
  410. SDL_FreeSurface(sdlicon);
  411. return true;
  412. }
  413. love::image::ImageData *Window::getIcon()
  414. {
  415. return curMode.icon;
  416. }
  417. void Window::swapBuffers()
  418. {
  419. SDL_GL_SwapWindow(window);
  420. }
  421. bool Window::hasFocus() const
  422. {
  423. return (window && SDL_GetKeyboardFocus() == window);
  424. }
  425. bool Window::hasMouseFocus() const
  426. {
  427. return (window && SDL_GetMouseFocus() == window);
  428. }
  429. bool Window::isVisible() const
  430. {
  431. return window && (SDL_GetWindowFlags(window) & SDL_WINDOW_SHOWN) != 0;
  432. }
  433. void Window::setMouseVisible(bool visible)
  434. {
  435. SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE);
  436. }
  437. bool Window::getMouseVisible() const
  438. {
  439. return (SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE);
  440. }
  441. void Window::setMouseGrab(bool grab)
  442. {
  443. mouseGrabbed = grab;
  444. if (window)
  445. SDL_SetWindowGrab(window, (SDL_bool) grab);
  446. }
  447. bool Window::isMouseGrabbed() const
  448. {
  449. if (window)
  450. return (bool) SDL_GetWindowGrab(window);
  451. else
  452. return mouseGrabbed;
  453. }
  454. const void *Window::getHandle() const
  455. {
  456. return window;
  457. }
  458. love::window::Window *Window::createSingleton()
  459. {
  460. if (!singleton)
  461. singleton = new Window();
  462. else
  463. singleton->retain();
  464. return singleton;
  465. }
  466. love::window::Window *Window::getSingleton()
  467. {
  468. return singleton;
  469. }
  470. const char *Window::getName() const
  471. {
  472. return "love.window.sdl";
  473. }
  474. } // sdl
  475. } // window
  476. } // love