Window.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. #include "ErrorHandlerLocator.h"
  2. #include "GUIHandler.h"
  3. #include "Window.h"
  4. Window::Window()
  5. {
  6. m_mouseInfo.clear();
  7. m_mouseCapturedBeforeLostFocus = false;
  8. m_enableGUI = false;
  9. m_changesQueued = false;
  10. m_inFullscreen = false;
  11. m_numDisplays = 0;
  12. m_mousePositionBeforeSetRelativeX = 0;
  13. m_mousePositionBeforeSetRelativeY = 0;
  14. m_SDLWindow = nullptr;
  15. m_GLContext = nullptr;
  16. m_guiHandler = nullptr;
  17. // Reserve the space, since we know the number of supported scancodes
  18. m_scancodeNames.reserve(Scancode::NumberOfScancodes);
  19. // Iterate over all scancodes and match them with their name, in unordered map
  20. for(int i = 0; i < Scancode::NumberOfScancodes; i++)
  21. {
  22. Scancode scanCode = static_cast<Scancode>(i);
  23. m_binds[i].m_scancode = scanCode;
  24. m_scancodeNames[GetString(scanCode)] = scanCode;
  25. }
  26. }
  27. Window::~Window()
  28. {
  29. SDL_Quit();
  30. }
  31. ErrorCode Window::init()
  32. {
  33. ErrorCode returnError = ErrorCode::Success;
  34. if(SDL_Init(SDL_INIT_VIDEO) < 0)
  35. returnError = ErrorCode::SDL_video_init_failed;
  36. else
  37. {
  38. // Get number of displays, check if the number is valid, and check if default display number is valid
  39. auto numDisplays = SDL_GetNumVideoDisplays();
  40. if(numDisplays < 0)
  41. {
  42. returnError = ErrorCode::Invalid_num_vid_displays;
  43. }
  44. else
  45. {
  46. if(numDisplays < Config::windowVar().default_display - 1)
  47. {
  48. Config::setWindowVar().default_display = 0;
  49. }
  50. // Get display mode with information about current screen resolution (among other things)
  51. SDL_DisplayMode displayMode;
  52. SDL_GetCurrentDisplayMode(Config::windowVar().default_display, &displayMode);
  53. ErrHandlerLoc::get().log(ErrorType::Info, ErrorSource::Source_Window, "Number of screens: " + Utilities::toString(numDisplays));
  54. ErrHandlerLoc::get().log(ErrorType::Info, ErrorSource::Source_Window, "Current screen resolution: " + Utilities::toString(displayMode.w) + "x" + Utilities::toString(displayMode.h));
  55. // Check if the resolution in config doesn't extend beyond current display resolution
  56. if(Config::windowVar().window_size_fullscreen_x > displayMode.w)
  57. Config::setWindowVar().window_size_fullscreen_x = displayMode.w;
  58. if(Config::windowVar().window_size_fullscreen_y > displayMode.h)
  59. Config::setWindowVar().window_size_fullscreen_y = displayMode.h;
  60. if(Config::windowVar().window_size_windowed_x > displayMode.w)
  61. Config::setWindowVar().window_size_windowed_x = displayMode.w;
  62. if(Config::windowVar().window_size_windowed_y > displayMode.h)
  63. Config::setWindowVar().window_size_windowed_y = displayMode.h;
  64. m_inFullscreen = Config::windowVar().fullscreen;
  65. // Set current resolution depending on whether fullscreen is on by default
  66. if(m_inFullscreen)
  67. {
  68. Config::setGraphicsVar().current_resolution_x = Config::windowVar().window_size_fullscreen_x;
  69. Config::setGraphicsVar().current_resolution_y = Config::windowVar().window_size_fullscreen_y;
  70. }
  71. else
  72. {
  73. Config::setGraphicsVar().current_resolution_x = Config::windowVar().window_size_windowed_x;
  74. Config::setGraphicsVar().current_resolution_y = Config::windowVar().window_size_windowed_y;
  75. }
  76. }
  77. }
  78. return returnError;
  79. }
  80. ErrorCode Window::createWindow()
  81. {
  82. ErrorCode returnError = ErrorCode::Success;
  83. // Set OpenGL context versions (if the version is not supported, no context will be created)
  84. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, Config::engineVar().gl_context_major_version);
  85. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, Config::engineVar().gl_context_minor_version);
  86. SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, Config::graphicsVar().double_buffering);
  87. SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, Config::graphicsVar().alpha_size);
  88. // Turn on multi-sampling anti-aliasing (MSAA)
  89. SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, Config::graphicsVar().multisample_buffers);
  90. SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, Config::graphicsVar().multisample_samples);
  91. // Set the re-sizable window flag if it is set on
  92. unsigned int flags = Config::windowVar().resizable ? flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE : SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
  93. // Spawn a window
  94. m_SDLWindow = SDL_CreateWindow(Config::windowVar().name.c_str(),
  95. Config::windowVar().window_position_x, Config::windowVar().window_position_y,
  96. Config::graphicsVar().current_resolution_x, Config::graphicsVar().current_resolution_y, flags);
  97. // Check if the creation of the window was successful
  98. if(!m_SDLWindow)
  99. returnError = ErrorCode::Window_creation_failed;
  100. else
  101. {
  102. ErrHandlerLoc::get().log(ErrorType::Info, ErrorSource::Source_Window, "SDL window has been created");
  103. // Create OpenGL context and attach it to the window
  104. m_GLContext = SDL_GL_CreateContext(m_SDLWindow);
  105. setVerticalSync(Config::windowVar().vertical_sync);
  106. // If mouse cursor is clipped, we need to hide it
  107. setMouseRelativeMode(Config::windowVar().mouse_captured);
  108. // Set if the mouse should warm to screen center in relative capture mode
  109. if(Config::inputVar().mouse_warp_mode)
  110. SDL_SetHintWithPriority(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1", SDL_HINT_OVERRIDE);
  111. else
  112. SDL_SetHintWithPriority(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "0", SDL_HINT_OVERRIDE);
  113. }
  114. return returnError;
  115. }
  116. void Window::handleEvents()
  117. {
  118. // Clear mouse value from the previous frame
  119. m_mouseInfo.clear();
  120. // Process queued up changes
  121. processChanges();
  122. SDL_Event SDLEvent;
  123. while(SDL_PollEvent(&SDLEvent))
  124. {
  125. // If QUIT event is received, shutdown the engine
  126. if(SDLEvent.type == SDL_QUIT)
  127. {
  128. Config::setEngineVar().running = false;
  129. break;
  130. }
  131. else
  132. {
  133. // If GUI is enabled, send the event to it
  134. if(m_enableGUI && !Config::m_windowVar.mouse_captured)
  135. m_guiHandler->processSDLEvent(SDLEvent);
  136. handleSDLEvent(SDLEvent);
  137. }
  138. }
  139. // If filtering is enabled, interpolate mouse location over current and previous frame; otherwise use current frame data
  140. if(Config::inputVar().mouse_filter)
  141. {
  142. m_mouseInfo.m_movementX = (m_mouseInfo.m_movementCurrentFrameX + m_mouseInfo.m_movementPrevFrameX) * 0.5f;
  143. m_mouseInfo.m_movementY = (m_mouseInfo.m_movementCurrentFrameY + m_mouseInfo.m_movementPrevFrameY) * 0.5f;
  144. // Save the current frame's mouse location to be used for next frame
  145. m_mouseInfo.m_movementPrevFrameX = m_mouseInfo.m_movementCurrentFrameX;
  146. m_mouseInfo.m_movementPrevFrameY = m_mouseInfo.m_movementCurrentFrameY;
  147. }
  148. else
  149. {
  150. m_mouseInfo.m_movementX = (float)m_mouseInfo.m_movementCurrentFrameX;
  151. m_mouseInfo.m_movementY = (float)m_mouseInfo.m_movementCurrentFrameY;
  152. }
  153. }
  154. void Window::handleSDLEvent(const SDL_Event &p_SDLEvent)
  155. {
  156. switch(p_SDLEvent.type)
  157. {
  158. case SDL_WINDOWEVENT:
  159. {
  160. switch(p_SDLEvent.window.event)
  161. {
  162. case SDL_WINDOWEVENT_RESIZED:
  163. if(!Config::windowVar().fullscreen || Config::windowVar().fullscreen_borderless)
  164. {
  165. Config::setGraphicsVar().current_resolution_x = p_SDLEvent.window.data1;
  166. Config::setGraphicsVar().current_resolution_y = p_SDLEvent.window.data2;
  167. if(!Config::windowVar().fullscreen)
  168. {
  169. Config::setWindowVar().window_size_windowed_x = p_SDLEvent.window.data1;
  170. Config::setWindowVar().window_size_windowed_y = p_SDLEvent.window.data2;
  171. }
  172. ErrHandlerLoc::get().log(ErrorType::Info, ErrorSource::Source_Window, "The window has been resized to " + Utilities::toString(p_SDLEvent.window.data1) + "x" + Utilities::toString(p_SDLEvent.window.data2));
  173. }
  174. break;
  175. case SDL_WINDOWEVENT_MOVED:
  176. Config::setWindowVar().window_position_x = p_SDLEvent.window.data1;
  177. Config::setWindowVar().window_position_y = p_SDLEvent.window.data2;
  178. break;
  179. case SDL_WINDOWEVENT_FOCUS_GAINED:
  180. Config::m_windowVar.window_in_focus = true;
  181. // If mouse was captured before focus, re-capture it
  182. if(m_mouseCapturedBeforeLostFocus)
  183. {
  184. // Recapture the mouse
  185. Config::m_windowVar.mouse_captured = m_mouseCapturedBeforeLostFocus;
  186. setMouseRelativeMode(m_mouseCapturedBeforeLostFocus);
  187. // Set flag to false, so that it will only be set to true if focus is lost
  188. m_mouseCapturedBeforeLostFocus = false;
  189. }
  190. break;
  191. case SDL_WINDOWEVENT_FOCUS_LOST:
  192. Config::m_windowVar.window_in_focus = false;
  193. // If mouse should be un-captured on lost focus and mouse is currently captured
  194. if(Config::windowVar().mouse_release_on_lost_focus && Config::windowVar().mouse_captured)
  195. {
  196. // Set the flag to true, so that it will be recaptured on gained focus
  197. m_mouseCapturedBeforeLostFocus = true;
  198. // Un-capture the mouse
  199. Config::m_windowVar.mouse_captured = false;
  200. setMouseRelativeMode(false);
  201. }
  202. break;
  203. case SDL_WINDOWEVENT_CLOSE:
  204. // This is not an ideal way to shutdown the engine, but if it happens,
  205. // mark engine as not running anymore, and it will be shutdown shortly
  206. Config::m_engineVar.running = false;
  207. break;
  208. }
  209. break;
  210. }
  211. case SDL_MOUSEMOTION:
  212. {
  213. if(!m_enableGUI || !m_guiHandler->isMouseCaptured())
  214. {
  215. // Get the relative mouse location
  216. m_mouseInfo.m_movementCurrentFrameX += p_SDLEvent.motion.xrel;
  217. m_mouseInfo.m_movementCurrentFrameY += p_SDLEvent.motion.yrel;
  218. }
  219. break;
  220. }
  221. case SDL_MOUSEWHEEL:
  222. {
  223. if(!m_enableGUI || !m_guiHandler->isMouseCaptured())
  224. {
  225. if(p_SDLEvent.wheel.x != 0)
  226. {
  227. m_mouseInfo.m_wheelX = p_SDLEvent.wheel.x;
  228. if(p_SDLEvent.wheel.x > 0)
  229. m_binds[Scancode::Mouse_wheelup].activate();
  230. else
  231. m_binds[Scancode::Mouse_wheeldown].activate();
  232. }
  233. else
  234. {
  235. m_mouseInfo.m_wheelY = p_SDLEvent.wheel.y;
  236. if(p_SDLEvent.wheel.y > 0)
  237. m_binds[Scancode::Mouse_wheelright].activate();
  238. else
  239. m_binds[Scancode::Mouse_wheelleft].activate();
  240. }
  241. }
  242. break;
  243. }
  244. case SDL_MOUSEBUTTONDOWN:
  245. {
  246. if(!m_enableGUI || !m_guiHandler->isMouseCaptured())
  247. {
  248. switch(p_SDLEvent.button.button)
  249. {
  250. case(SDL_BUTTON_LEFT):
  251. m_binds[Scancode::Mouse_left].activate();
  252. break;
  253. case(SDL_BUTTON_RIGHT):
  254. m_binds[Scancode::Mouse_right].activate();
  255. break;
  256. case(SDL_BUTTON_MIDDLE):
  257. m_binds[Scancode::Mouse_middle].activate();
  258. break;
  259. case(SDL_BUTTON_X1):
  260. m_binds[Scancode::Mouse_x1].activate();
  261. break;
  262. case(SDL_BUTTON_X2):
  263. m_binds[Scancode::Mouse_x2].activate();
  264. break;
  265. }
  266. break;
  267. }
  268. }
  269. case SDL_MOUSEBUTTONUP:
  270. {
  271. switch(p_SDLEvent.button.button)
  272. {
  273. case(SDL_BUTTON_LEFT) :
  274. m_binds[Scancode::Mouse_left].deactivate();
  275. break;
  276. case(SDL_BUTTON_RIGHT) :
  277. m_binds[Scancode::Mouse_right].deactivate();
  278. break;
  279. case(SDL_BUTTON_MIDDLE) :
  280. m_binds[Scancode::Mouse_middle].deactivate();
  281. break;
  282. case(SDL_BUTTON_X1) :
  283. m_binds[Scancode::Mouse_x1].deactivate();
  284. break;
  285. case(SDL_BUTTON_X2) :
  286. m_binds[Scancode::Mouse_x2].deactivate();
  287. break;
  288. }
  289. break;
  290. }
  291. case SDL_KEYDOWN:
  292. {
  293. if(!m_enableGUI || !m_guiHandler->isKeyboardCaptured())
  294. {
  295. // If the key's scancode is in the range of scancode enum, activate it
  296. if(p_SDLEvent.key.keysym.scancode < Scancode::NumberOfScancodes)
  297. m_binds[p_SDLEvent.key.keysym.scancode].activate();
  298. }
  299. break;
  300. }
  301. case SDL_KEYUP:
  302. {
  303. // If the key's scancode is in the range of scancode enum, deactivate it
  304. if(p_SDLEvent.key.keysym.scancode < Scancode::NumberOfScancodes)
  305. m_binds[p_SDLEvent.key.keysym.scancode].deactivate();
  306. break;
  307. }
  308. }
  309. }
  310. void Window::processChanges()
  311. {
  312. // If any changes are queued up (none most of the time, so have an early check here)
  313. if(m_changesQueued)
  314. {
  315. // Iterate over all changes and process them
  316. for(decltype(m_changeQueue.size()) i = 0, size = m_changeQueue.size(); i < size; i++)
  317. {
  318. switch(m_changeQueue[i].getPropertyID())
  319. {
  320. case Properties::Fullscreen:
  321. // Get new value, if it differs from the current state, set the new mouse capture mode
  322. if(Config::windowVar().fullscreen != m_changeQueue[i].getBool())
  323. {
  324. // Set the new fullscreen flag
  325. Config::m_windowVar.fullscreen = m_changeQueue[i].getBool();
  326. // If it is switching to border-less fullscreen, set the fullscreen resolution
  327. if (Config::windowVar().fullscreen && !Config::windowVar().fullscreen_borderless)
  328. {
  329. setWindowSize(Config::windowVar().window_size_fullscreen_x, Config::windowVar().window_size_fullscreen_y);
  330. Config::setGraphicsVar().current_resolution_x = Config::windowVar().window_size_fullscreen_x;
  331. Config::setGraphicsVar().current_resolution_y = Config::windowVar().window_size_fullscreen_y;
  332. }
  333. // If it is switching to windowed, set the windowed resolution
  334. if(!Config::windowVar().fullscreen)
  335. {
  336. setWindowSize(Config::windowVar().window_size_windowed_x, Config::windowVar().window_size_windowed_y);
  337. Config::setGraphicsVar().current_resolution_x = Config::windowVar().window_size_windowed_x;
  338. Config::setGraphicsVar().current_resolution_y = Config::windowVar().window_size_windowed_y;
  339. }
  340. // Set the fullscreen mode
  341. setFullscreen(Config::windowVar().fullscreen);
  342. }
  343. break;
  344. case Properties::MouseCapture:
  345. // Get new value, if it differs from the current state, set the new mouse capture mode
  346. if(Config::windowVar().mouse_captured != m_changeQueue[i].getBool())
  347. {
  348. Config::m_windowVar.mouse_captured = m_changeQueue[i].getBool();
  349. setMouseRelativeMode(Config::windowVar().mouse_captured);
  350. }
  351. break;
  352. case Properties::VerticalSync:
  353. // Get new value, if it differs from the current state, update the vertical sync mode
  354. if(Config::windowVar().vertical_sync != m_changeQueue[i].getBool())
  355. {
  356. Config::m_windowVar.vertical_sync = m_changeQueue[i].getBool();
  357. setVerticalSync(Config::windowVar().vertical_sync);
  358. }
  359. break;
  360. case Properties::WindowTitle:
  361. // Change window title
  362. setWindowTitle(m_changeQueue[i].getString());
  363. break;
  364. }
  365. }
  366. // Clear the queued up changes
  367. m_changeQueue.clear();
  368. // Mark that all changes have been processed
  369. m_changesQueued = false;
  370. }
  371. }