2
0

App.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. #include "App.h"
  2. #include "AppGLFW.h"
  3. #include "Config.h"
  4. #include "FileSystem.h"
  5. #include "Logging.h"
  6. #include "Monitor.h"
  7. #include "Path.h"
  8. #include "Renderer.h"
  9. #include "StringUtils.h"
  10. #include "UI.h"
  11. #include "Window.h"
  12. #include <algorithm>
  13. #include <cstdio>
  14. #include <cstdlib>
  15. #include <cstring>
  16. #include <unordered_set>
  17. #include <vector>
  18. #include <imgui.h>
  19. namespace gameplay
  20. {
  21. struct App::Impl
  22. {
  23. bool running = false;
  24. Window* window{nullptr};
  25. std::unordered_set<Window*> windows;
  26. std::vector<Monitor*> monitors;
  27. std::unordered_set<std::shared_ptr<Cursor>> cursors;
  28. std::shared_ptr<FileSystem> fs{nullptr};
  29. std::shared_ptr<Config> config{nullptr};
  30. std::shared_ptr<Logging> logging{nullptr};
  31. std::shared_ptr<Renderer> renderer{nullptr};
  32. std::shared_ptr<UI> ui{nullptr};
  33. std::unordered_map<std::string, std::string> resourceAliases;
  34. Window* create_window(const WindowDesc& desc);
  35. void destroy_window(Window* window);
  36. Monitor* get_primary_monitor();
  37. Monitor* get_monitor(Window* window);
  38. Monitor** get_monitors(size_t* monitorCount);
  39. std::shared_ptr<Cursor> create_cursor(CursorStandardShape shape);
  40. std::shared_ptr<Cursor> create_cursor(const Pixmap* image, Int2 hotspotPos);
  41. void destroy_cursor(std::shared_ptr<Cursor> cursor);
  42. };
  43. App::App()
  44. {
  45. _impl = std::make_unique<App::Impl>();
  46. }
  47. App::~App()
  48. {
  49. }
  50. App* App::get_app()
  51. {
  52. static App app;
  53. return &app;
  54. }
  55. int App::exec(int argc, char** argv)
  56. {
  57. if (_impl->running)
  58. return 1;
  59. _impl->running = true;
  60. // create the file system
  61. _impl->fs = std::make_shared<FileSystem>();
  62. // create and load settings
  63. _impl->config = std::make_shared<Config>();
  64. _impl->config->load(argc, argv);
  65. // register application executable directory as a file system alias
  66. _impl->resourceAliases["app.dir"] = _impl->fs->get_app_directory_path();
  67. // register file system aliases from config
  68. _impl->config->for_each_table("resource.alias",
  69. [](Config* config, void* userPtr) -> bool
  70. {
  71. App::Impl* impl = (App::Impl*)userPtr;
  72. std::string name = config->get_string("name", "");
  73. std::string pathStr = config->get_string("path", "");
  74. if (App::get_app()->resolve_resource_path(pathStr))
  75. {
  76. impl->resourceAliases[name] = pathStr;
  77. }
  78. return true;
  79. }, (void*)_impl.get());
  80. // startup the logging system
  81. _impl->logging = std::make_shared<Logging>();
  82. _impl->logging->startup();
  83. // startup the glfw windowing
  84. glfwSetErrorCallback(GLFWUtils::on_error_callback);
  85. if (!glfwInit())
  86. {
  87. GP_LOG_ERROR("GLFW initialization failed.");
  88. return 1;
  89. }
  90. // load the main window from config
  91. std::string windowTitle = _impl->config->get_string("window.title", "");
  92. int windowWidth = _impl->config->get_int("window.width", 1280);
  93. int windowHeight = _impl->config->get_int("window.height", 720);
  94. bool windowFullscreen = _impl->config->get_bool("window.fullscreen", false);
  95. std::string windowHintsStr = _impl->config->get_string("window.hints", "NONE");
  96. WindowHints windowHints = GLFWUtils::parse_window_hints(windowHintsStr.c_str());
  97. // create the main window
  98. WindowDesc windowDesc = { windowTitle.c_str(), windowWidth, windowHeight, windowFullscreen, windowHints };
  99. _impl->window = _impl->create_window(windowDesc);
  100. // adjust the window position where the frame is at 0, 0
  101. int top;
  102. _impl->window->get_frame_size(nullptr, &top, nullptr, nullptr);
  103. _impl->window->set_pos({0, top});
  104. // startup the ui system
  105. _impl->ui = std::make_shared<UI>();
  106. _impl->ui->startup();
  107. // startup the renderer
  108. _impl->renderer = std::make_shared<Renderer>();
  109. _impl->renderer->startup();
  110. // run the main window event loop until we should close
  111. while (!_impl->window->should_close())
  112. {
  113. glfwPollEvents();
  114. _impl->renderer->next_frame();
  115. _impl->renderer->update();
  116. _impl->ui->update();
  117. _impl->renderer->render_frame();
  118. _impl->renderer->present_frame();
  119. }
  120. // destroy main window and shutdown
  121. _impl->destroy_window(_impl->window);
  122. // Sync the monitors first and cleanup the monitor objects
  123. size_t monitorCount;
  124. get_monitors(&monitorCount);
  125. std::for_each(_impl->monitors.begin(), _impl->monitors.end(), [](auto& monitor){delete monitor;});
  126. _impl->monitors.clear();
  127. // Cleanup the cursors and windows managed objects
  128. _impl->cursors.clear();
  129. std::for_each(_impl->windows.begin(), _impl->windows.end(), [](auto& window){delete window;});
  130. _impl->windows.clear();
  131. glfwTerminate();
  132. return 0;
  133. }
  134. void App::exit()
  135. {
  136. _impl->window->close();
  137. }
  138. void App::set_time(double timeSecs)
  139. {
  140. glfwSetTime(timeSecs);
  141. }
  142. double App::get_time() const
  143. {
  144. return glfwGetTime();
  145. }
  146. Monitor* App::get_primary_monitor()
  147. {
  148. return _impl->get_primary_monitor();
  149. }
  150. Monitor* App::get_monitor(Window* window)
  151. {
  152. return _impl->get_monitor(window);
  153. }
  154. Monitor** App::get_monitors(size_t* monitorCount)
  155. {
  156. return _impl->get_monitors(monitorCount);
  157. }
  158. std::shared_ptr<Cursor> App::create_cursor(CursorStandardShape shape)
  159. {
  160. return _impl->create_cursor(shape);
  161. }
  162. std::shared_ptr<Cursor> App::create_cursor(const Pixmap* image, Int2 hotspotPos)
  163. {
  164. return _impl->create_cursor(image, hotspotPos);
  165. }
  166. void App::destroy_cursor(std::shared_ptr<Cursor> cursor)
  167. {
  168. _impl->destroy_cursor(cursor);
  169. }
  170. void App::set_clip_board_string(const char* str)
  171. {
  172. glfwSetClipboardString(nullptr, str);
  173. }
  174. const char* App::get_clipboard_string() const
  175. {
  176. return glfwGetClipboardString(nullptr);
  177. }
  178. std::shared_ptr<FileSystem> App::get_file_system() const
  179. {
  180. return _impl->fs;
  181. }
  182. std::shared_ptr<Config> App::get_config() const
  183. {
  184. return _impl->config;
  185. }
  186. std::shared_ptr<Logging> App::get_logging() const
  187. {
  188. return _impl->logging;
  189. }
  190. Window* App::get_window() const
  191. {
  192. return _impl->window;
  193. }
  194. std::shared_ptr<Renderer> App::get_renderer() const
  195. {
  196. return _impl->renderer;
  197. }
  198. std::shared_ptr<UI> App::get_ui() const
  199. {
  200. return _impl->ui;
  201. }
  202. void App::set_resource_path(const char* alias, const char* path)
  203. {
  204. _impl->resourceAliases[alias] = path;
  205. }
  206. void App::clear_resource_path(const char* alias)
  207. {
  208. _impl->resourceAliases.erase(alias);
  209. }
  210. bool App::resolve_resource_path(std::string& resourcePath)
  211. {
  212. std::string path = resourcePath;
  213. size_t startPos = 0;
  214. const std::string startDelim = "@";
  215. while((startPos = path.find(startDelim, startPos)) != std::string::npos)
  216. {
  217. const std::string stopDelim = "/";
  218. size_t endPos = path.find(stopDelim, startPos);
  219. if (endPos != std::string::npos)
  220. {
  221. size_t aliasLen = endPos - startPos;
  222. std::string alias = path.substr(startPos + 1, aliasLen - 1);
  223. auto pair = _impl->resourceAliases.find(alias);
  224. if ( pair == _impl->resourceAliases.end())
  225. return false;
  226. std::string aliasPath = _impl->resourceAliases[alias];
  227. path.replace(startPos, aliasLen, aliasPath);
  228. }
  229. }
  230. resourcePath = path;
  231. return true;
  232. }
  233. Window* App::Impl::create_window(const WindowDesc& desc)
  234. {
  235. Window* window = nullptr;
  236. // default window hints
  237. glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
  238. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
  239. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
  240. glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE);
  241. glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
  242. // apply user supplied window hints
  243. glfwWindowHint(GLFW_RESIZABLE, (desc.hints & WINDOW_HINT_NO_RESIZE) ? GLFW_FALSE: GLFW_TRUE);
  244. glfwWindowHint(GLFW_DECORATED, (desc.hints & WINDOW_HINT_NO_DECORATION) ? GLFW_FALSE : GLFW_TRUE);
  245. glfwWindowHint(GLFW_AUTO_ICONIFY, (desc.hints & WINDOW_HINT_NO_AUTO_ICONIFY) ? GLFW_FALSE : GLFW_TRUE);
  246. glfwWindowHint(GLFW_FOCUS_ON_SHOW, (desc.hints & WINDOW_HINT_NO_FOCUS_ON_SHOW) ? GLFW_FALSE : GLFW_TRUE);
  247. glfwWindowHint(GLFW_SCALE_TO_MONITOR, (desc.hints & WINDOW_HINT_SCALE_TO_MONITOR) ? GLFW_TRUE : GLFW_FALSE);
  248. glfwWindowHint(GLFW_FLOATING, (desc.hints & WINDOW_HINT_FLOATING) ? GLFW_TRUE : GLFW_FALSE);
  249. glfwWindowHint(GLFW_MAXIMIZED, (desc.hints & WINDOW_HINT_MAXIMIZED) ? GLFW_TRUE : GLFW_FALSE);
  250. // apply fullscreen window hints
  251. if (desc.fullscreen)
  252. {
  253. auto mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
  254. glfwWindowHint(GLFW_RED_BITS, mode->redBits);
  255. glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits);
  256. glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits);
  257. glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate);
  258. }
  259. // create the window
  260. window = new Window();
  261. window->handle = std::make_unique<WindowHandle>();
  262. window->handle->glfwWindow = glfwCreateWindow(desc.width, desc.height, desc.title.c_str(), nullptr, nullptr);
  263. if (!window->handle->glfwWindow)
  264. {
  265. return nullptr;
  266. }
  267. // store this window in the glfw user pointer
  268. glfwSetWindowUserPointer(window->handle->glfwWindow, window);
  269. // setup glfw window callbacks
  270. glfwSetWindowPosCallback(window->handle->glfwWindow, GLFWUtils::on_window_pos_callback);
  271. glfwSetWindowSizeCallback(window->handle->glfwWindow, GLFWUtils::on_window_size_callback);
  272. glfwSetWindowCloseCallback(window->handle->glfwWindow, GLFWUtils::on_window_close_callback);
  273. glfwSetWindowFocusCallback(window->handle->glfwWindow, GLFWUtils::on_window_focus_callback);
  274. glfwSetWindowIconifyCallback(window->handle->glfwWindow, GLFWUtils::on_window_iconify_callback);
  275. glfwSetWindowMaximizeCallback(window->handle->glfwWindow, GLFWUtils::on_window_maximize_callback);
  276. glfwSetWindowContentScaleCallback(window->handle->glfwWindow, GLFWUtils::on_window_content_scale_callback);
  277. glfwSetCursorPosCallback(window->handle->glfwWindow, GLFWUtils::on_cursor_move_callback);
  278. glfwSetCursorEnterCallback(window->handle->glfwWindow, GLFWUtils::on_cursor_enter_callback);
  279. glfwSetDropCallback(window->handle->glfwWindow, GLFWUtils::on_drop_callback);
  280. glfwSetKeyCallback(window->handle->glfwWindow, GLFWUtils::on_key_callback);
  281. glfwSetCharCallback(window->handle->glfwWindow, GLFWUtils::on_char_callback);
  282. glfwSetMouseButtonCallback(window->handle->glfwWindow, GLFWUtils::on_mouse_button_callback);
  283. glfwSetScrollCallback(window->handle->glfwWindow, GLFWUtils::on_scroll_callback);
  284. glfwSetJoystickCallback(GLFWUtils::on_joystick_callback);
  285. // store the last size, pos and mouse scale for toggline fullscreen
  286. glfwGetWindowSize(window->handle->glfwWindow, &window->handle->size.x, &window->handle->size.y);
  287. glfwGetWindowPos(window->handle->glfwWindow, &window->handle->pos.x, &window->handle->pos.y);
  288. window->handle->mouseScale.x = 1 / float(window->handle->size.x);
  289. window->handle->mouseScale.y = 1 / float(window->handle->size.y);
  290. // get the platform window/display
  291. #if GP_PLATFORM_WINDOWS
  292. window->handle->platformWindow = glfwGetWin32Window(window->handle->glfwWindow);
  293. #elif GP_PLATFORM_LINUX
  294. window->handle->platformWindow = (void*)glfwGetX11Window(window->handle->glfwWindow);
  295. window->handle->platformDisplay = glfwGetX11Display();
  296. #endif
  297. // set fullscreen if enabled
  298. if (desc.fullscreen)
  299. {
  300. window->set_fullscreen(true);
  301. }
  302. windows.insert(window);
  303. return window;
  304. }
  305. void App::Impl::destroy_window(Window* window)
  306. {
  307. windows.erase(window);
  308. GP_SAFE_DELETE(window);
  309. }
  310. Monitor* App::Impl::get_primary_monitor()
  311. {
  312. GLFWmonitor* primaryMonitor = glfwGetPrimaryMonitor();
  313. return (Monitor*)glfwGetMonitorUserPointer(primaryMonitor);
  314. }
  315. Monitor* App::Impl::get_monitor(Window* window)
  316. {
  317. Monitor* retMonitor = nullptr;
  318. Int2 windowPos = window->get_pos();
  319. Int2 windowSize = window->get_size();
  320. int mostOverlap = 0;
  321. for (size_t i = 0; i < monitors.size(); i++)
  322. {
  323. Monitor* monitor = monitors[i];
  324. Int2 monitorPos = monitor->get_pos();
  325. Int2 monitorSize = { monitor->get_video_mode().width, monitor->get_video_mode().height};
  326. Int2 windowPosSize;
  327. windowPosSize.x = windowPos.x + windowSize.x;
  328. windowPosSize.y = windowPos.y + windowSize.y;
  329. int overlap = GLFWUtils::get_rect_overlap(windowPosSize, windowSize, monitorPos, monitorSize);
  330. if (mostOverlap < overlap)
  331. {
  332. mostOverlap = overlap;
  333. retMonitor = monitor;
  334. }
  335. }
  336. return retMonitor;
  337. }
  338. Monitor** App::Impl::get_monitors(size_t* monitorCount)
  339. {
  340. monitors.clear();
  341. int count;
  342. GLFWmonitor** glfwMonitors = glfwGetMonitors(&count);
  343. for (int i = 0; i < count; i++)
  344. {
  345. monitors.push_back((Monitor*)glfwGetMonitorUserPointer(glfwMonitors[i]));
  346. }
  347. *monitorCount = count;
  348. return monitors.data();
  349. }
  350. std::shared_ptr<Cursor> App::Impl::create_cursor(CursorStandardShape shape)
  351. {
  352. std::shared_ptr<Cursor> cursor = nullptr;
  353. GLFWcursor* glfwCursor = nullptr;
  354. switch (shape)
  355. {
  356. case CursorStandardShape::ARROW:
  357. glfwCursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
  358. break;
  359. case CursorStandardShape::IBEAM:
  360. glfwCursor = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
  361. break;
  362. case CursorStandardShape::CROSSHAIR:
  363. glfwCursor = glfwCreateStandardCursor(GLFW_CROSSHAIR_CURSOR);
  364. break;
  365. case CursorStandardShape::HAND:
  366. glfwCursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
  367. break;
  368. case CursorStandardShape::HRESIZE:
  369. glfwCursor = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR);
  370. break;
  371. case CursorStandardShape::VRESIZE:
  372. glfwCursor = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR);
  373. break;
  374. default:
  375. GP_ASSERT(false);
  376. glfwCursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
  377. break;
  378. }
  379. if (glfwCursor)
  380. {
  381. cursor = std::make_shared<Cursor>();
  382. cursor->glfwCursor = glfwCursor;
  383. cursors.insert(cursor);
  384. }
  385. return cursor;
  386. }
  387. std::shared_ptr<Cursor> App::Impl::create_cursor(const Pixmap* image, Int2 hotspotPos)
  388. {
  389. std::shared_ptr<Cursor> cursor{nullptr};
  390. GLFWcursor* glfwCursor = glfwCreateCursor((const GLFWimage*)image, hotspotPos.x, hotspotPos.y);
  391. if (glfwCursor)
  392. {
  393. cursor = std::make_shared<Cursor>();
  394. cursor->glfwCursor = glfwCursor;
  395. cursors.insert(cursor);
  396. }
  397. return cursor;
  398. }
  399. void App::Impl::destroy_cursor(std::shared_ptr<Cursor> cursor)
  400. {
  401. if (cursor && cursor->glfwCursor)
  402. {
  403. glfwDestroyCursor(cursor->glfwCursor);
  404. cursors.erase(cursor);
  405. }
  406. }
  407. }
  408. GP_ASSERT_STRUCTS_MATCH(GLFWimage, gameplay::Pixmap);