You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

715 lines
18KB

  1. #include "window.hpp"
  2. #include "app.hpp"
  3. #include "asset.hpp"
  4. #include "gamepad.hpp"
  5. #include "keyboard.hpp"
  6. #include "util/color.hpp"
  7. #include <map>
  8. #include <queue>
  9. #include <thread>
  10. #include "osdialog.h"
  11. #define NANOVG_GL2_IMPLEMENTATION 1
  12. // #define NANOVG_GL3_IMPLEMENTATION 1
  13. // #define NANOVG_GLES2_IMPLEMENTATION 1
  14. // #define NANOVG_GLES3_IMPLEMENTATION 1
  15. #include "nanovg_gl.h"
  16. // Hack to get framebuffer objects working on OpenGL 2 (we blindly assume the extension is supported)
  17. #define NANOVG_FBO_VALID 1
  18. #include "nanovg_gl_utils.h"
  19. #define BLENDISH_IMPLEMENTATION
  20. #include "blendish.h"
  21. #define NANOSVG_IMPLEMENTATION
  22. #define NANOSVG_ALL_COLOR_KEYWORDS
  23. #include "nanosvg.h"
  24. #ifdef ARCH_MAC
  25. // For CGAssociateMouseAndMouseCursorPosition
  26. #include <ApplicationServices/ApplicationServices.h>
  27. #endif
  28. namespace rack {
  29. GLFWwindow *gWindow = NULL;
  30. NVGcontext *gVg = NULL;
  31. NVGcontext *gFramebufferVg = NULL;
  32. std::shared_ptr<Font> gGuiFont;
  33. float gPixelRatio = 1.0;
  34. float gWindowRatio = 1.0;
  35. bool gAllowCursorLock = true;
  36. int gGuiFrame;
  37. Vec gMousePos;
  38. std::string lastWindowTitle;
  39. void windowSizeCallback(GLFWwindow* window, int width, int height) {
  40. }
  41. void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) {
  42. #ifdef ARCH_MAC
  43. // Ctrl-left click --> right click
  44. if (button == GLFW_MOUSE_BUTTON_LEFT) {
  45. if (glfwGetKey(gWindow, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(gWindow, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) {
  46. button = GLFW_MOUSE_BUTTON_RIGHT;
  47. }
  48. }
  49. #endif
  50. if (action == GLFW_PRESS) {
  51. gTempWidget = NULL;
  52. // onMouseDown
  53. {
  54. EventMouseDown e;
  55. e.pos = gMousePos;
  56. e.button = button;
  57. gScene->onMouseDown(e);
  58. gTempWidget = e.target;
  59. }
  60. if (button == GLFW_MOUSE_BUTTON_LEFT) {
  61. if (gTempWidget) {
  62. // onDragStart
  63. EventDragStart e;
  64. gTempWidget->onDragStart(e);
  65. }
  66. gDraggedWidget = gTempWidget;
  67. if (gTempWidget != gFocusedWidget) {
  68. if (gFocusedWidget) {
  69. // onDefocus
  70. EventDefocus e;
  71. gFocusedWidget->onDefocus(e);
  72. }
  73. gFocusedWidget = NULL;
  74. if (gTempWidget) {
  75. // onFocus
  76. EventFocus e;
  77. gTempWidget->onFocus(e);
  78. if (e.consumed) {
  79. gFocusedWidget = gTempWidget;
  80. }
  81. }
  82. }
  83. }
  84. gTempWidget = NULL;
  85. }
  86. else if (action == GLFW_RELEASE) {
  87. // onMouseUp
  88. gTempWidget = NULL;
  89. {
  90. EventMouseUp e;
  91. e.pos = gMousePos;
  92. e.button = button;
  93. gScene->onMouseUp(e);
  94. gTempWidget = e.target;
  95. }
  96. if (button == GLFW_MOUSE_BUTTON_LEFT) {
  97. if (gDraggedWidget) {
  98. // onDragDrop
  99. EventDragDrop e;
  100. e.origin = gDraggedWidget;
  101. gTempWidget->onDragDrop(e);
  102. }
  103. // gDraggedWidget might have been set to null in the last event, recheck here
  104. if (gDraggedWidget) {
  105. // onDragEnd
  106. EventDragEnd e;
  107. gDraggedWidget->onDragEnd(e);
  108. }
  109. gDraggedWidget = NULL;
  110. gDragHoveredWidget = NULL;
  111. }
  112. gTempWidget = NULL;
  113. }
  114. }
  115. struct MouseButtonArguments {
  116. GLFWwindow *window;
  117. int button;
  118. int action;
  119. int mods;
  120. };
  121. static std::queue<MouseButtonArguments> mouseButtonQueue;
  122. void mouseButtonStickyPop() {
  123. if (!mouseButtonQueue.empty()) {
  124. MouseButtonArguments args = mouseButtonQueue.front();
  125. mouseButtonQueue.pop();
  126. mouseButtonCallback(args.window, args.button, args.action, args.mods);
  127. }
  128. }
  129. void mouseButtonStickyCallback(GLFWwindow *window, int button, int action, int mods) {
  130. // Defer multiple clicks per frame to future frames
  131. MouseButtonArguments args = {window, button, action, mods};
  132. mouseButtonQueue.push(args);
  133. }
  134. void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) {
  135. Vec mousePos = Vec(xpos, ypos).div(gPixelRatio / gWindowRatio).round();
  136. Vec mouseRel = mousePos.minus(gMousePos);
  137. int cursorMode = glfwGetInputMode(gWindow, GLFW_CURSOR);
  138. (void) cursorMode;
  139. #ifdef ARCH_MAC
  140. // Workaround for Mac. We can't use GLFW_CURSOR_DISABLED because it's buggy, so implement it on our own.
  141. // This is not an ideal implementation. For example, if the user drags off the screen, the new mouse position will be clamped.
  142. if (cursorMode == GLFW_CURSOR_HIDDEN) {
  143. // CGSetLocalEventsSuppressionInterval(0.0);
  144. glfwSetCursorPos(gWindow, gMousePos.x, gMousePos.y);
  145. CGAssociateMouseAndMouseCursorPosition(true);
  146. mousePos = gMousePos;
  147. }
  148. // Because sometimes the cursor turns into an arrow when its position is on the boundary of the window
  149. glfwSetCursor(gWindow, NULL);
  150. #endif
  151. gMousePos = mousePos;
  152. gTempWidget = NULL;
  153. // onMouseMove
  154. {
  155. EventMouseMove e;
  156. e.pos = mousePos;
  157. e.mouseRel = mouseRel;
  158. gScene->onMouseMove(e);
  159. gTempWidget = e.target;
  160. }
  161. if (gDraggedWidget) {
  162. // onDragMove
  163. EventDragMove e;
  164. e.mouseRel = mouseRel;
  165. gDraggedWidget->onDragMove(e);
  166. if (gTempWidget != gDragHoveredWidget) {
  167. if (gDragHoveredWidget) {
  168. EventDragEnter e;
  169. e.origin = gDraggedWidget;
  170. gDragHoveredWidget->onDragLeave(e);
  171. }
  172. gDragHoveredWidget = gTempWidget;
  173. if (gDragHoveredWidget) {
  174. EventDragEnter e;
  175. e.origin = gDraggedWidget;
  176. gDragHoveredWidget->onDragEnter(e);
  177. }
  178. }
  179. }
  180. else {
  181. if (gTempWidget != gHoveredWidget) {
  182. if (gHoveredWidget) {
  183. // onMouseLeave
  184. EventMouseLeave e;
  185. gHoveredWidget->onMouseLeave(e);
  186. }
  187. gHoveredWidget = gTempWidget;
  188. if (gHoveredWidget) {
  189. // onMouseEnter
  190. EventMouseEnter e;
  191. gHoveredWidget->onMouseEnter(e);
  192. }
  193. }
  194. }
  195. gTempWidget = NULL;
  196. if (glfwGetMouseButton(gWindow, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS) {
  197. // TODO
  198. // Define a new global called gScrollWidget, which remembers the widget where middle-click was first pressed
  199. EventScroll e;
  200. e.pos = mousePos;
  201. e.scrollRel = mouseRel;
  202. gScene->onScroll(e);
  203. }
  204. }
  205. void cursorEnterCallback(GLFWwindow* window, int entered) {
  206. if (!entered) {
  207. if (gHoveredWidget) {
  208. // onMouseLeave
  209. EventMouseLeave e;
  210. gHoveredWidget->onMouseLeave(e);
  211. }
  212. gHoveredWidget = NULL;
  213. }
  214. }
  215. void scrollCallback(GLFWwindow *window, double x, double y) {
  216. Vec scrollRel = Vec(x, y);
  217. #if ARCH_LIN || ARCH_WIN
  218. if (windowIsShiftPressed())
  219. scrollRel = Vec(y, x);
  220. #endif
  221. // onScroll
  222. EventScroll e;
  223. e.pos = gMousePos;
  224. e.scrollRel = scrollRel.mult(50.0);
  225. gScene->onScroll(e);
  226. }
  227. void charCallback(GLFWwindow *window, unsigned int codepoint) {
  228. if (gFocusedWidget) {
  229. // onText
  230. EventText e;
  231. e.codepoint = codepoint;
  232. gFocusedWidget->onText(e);
  233. }
  234. }
  235. void keyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) {
  236. if (action == GLFW_PRESS || action == GLFW_REPEAT) {
  237. if (gFocusedWidget) {
  238. // onKey
  239. EventKey e;
  240. e.key = key;
  241. gFocusedWidget->onKey(e);
  242. if (e.consumed)
  243. return;
  244. }
  245. // onHoverKey
  246. EventHoverKey e;
  247. e.pos = gMousePos;
  248. e.key = key;
  249. gScene->onHoverKey(e);
  250. }
  251. // Keyboard MIDI driver
  252. if (!(mods & (GLFW_MOD_SHIFT | GLFW_MOD_CONTROL | GLFW_MOD_ALT | GLFW_MOD_SUPER))) {
  253. if (action == GLFW_PRESS) {
  254. keyboardPress(key);
  255. }
  256. else if (action == GLFW_RELEASE) {
  257. keyboardRelease(key);
  258. }
  259. }
  260. }
  261. void dropCallback(GLFWwindow *window, int count, const char **paths) {
  262. // onPathDrop
  263. EventPathDrop e;
  264. e.pos = gMousePos;
  265. for (int i = 0; i < count; i++) {
  266. e.paths.push_back(paths[i]);
  267. }
  268. gScene->onPathDrop(e);
  269. }
  270. void errorCallback(int error, const char *description) {
  271. warn("GLFW error %d: %s", error, description);
  272. }
  273. void renderGui() {
  274. int width, height;
  275. glfwGetFramebufferSize(gWindow, &width, &height);
  276. // Update and render
  277. nvgBeginFrame(gVg, width, height, gPixelRatio);
  278. nvgReset(gVg);
  279. nvgScale(gVg, gPixelRatio, gPixelRatio);
  280. gScene->draw(gVg);
  281. glViewport(0, 0, width, height);
  282. glClearColor(0.0, 0.0, 0.0, 1.0);
  283. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  284. nvgEndFrame(gVg);
  285. glfwSwapBuffers(gWindow);
  286. }
  287. void windowInit() {
  288. int err;
  289. // Set up GLFW
  290. glfwSetErrorCallback(errorCallback);
  291. err = glfwInit();
  292. if (err != GLFW_TRUE) {
  293. osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Could not initialize GLFW.");
  294. exit(1);
  295. }
  296. #if defined NANOVG_GL2
  297. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
  298. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
  299. #elif defined NANOVG_GL3
  300. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  301. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
  302. glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
  303. glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  304. #endif
  305. glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE);
  306. glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE);
  307. lastWindowTitle = "";
  308. gWindow = glfwCreateWindow(640, 480, lastWindowTitle.c_str(), NULL, NULL);
  309. if (!gWindow) {
  310. osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Cannot open window with OpenGL 2.0 renderer. Does your graphics card support OpenGL 2.0 or greater? If so, make sure you have the latest graphics drivers installed.");
  311. exit(1);
  312. }
  313. glfwMakeContextCurrent(gWindow);
  314. glfwSwapInterval(1);
  315. glfwSetInputMode(gWindow, GLFW_LOCK_KEY_MODS, 1);
  316. glfwSetWindowSizeCallback(gWindow, windowSizeCallback);
  317. glfwSetMouseButtonCallback(gWindow, mouseButtonStickyCallback);
  318. // Call this ourselves, but on every frame instead of only when the mouse moves
  319. // glfwSetCursorPosCallback(gWindow, cursorPosCallback);
  320. glfwSetCursorEnterCallback(gWindow, cursorEnterCallback);
  321. glfwSetScrollCallback(gWindow, scrollCallback);
  322. glfwSetCharCallback(gWindow, charCallback);
  323. glfwSetKeyCallback(gWindow, keyCallback);
  324. glfwSetDropCallback(gWindow, dropCallback);
  325. // Set up GLEW
  326. glewExperimental = GL_TRUE;
  327. err = glewInit();
  328. if (err != GLEW_OK) {
  329. osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Could not initialize GLEW. Does your graphics card support OpenGL 2.0 or greater? If so, make sure you have the latest graphics drivers installed.");
  330. exit(1);
  331. }
  332. // GLEW generates GL error because it calls glGetString(GL_EXTENSIONS), we'll consume it here.
  333. glGetError();
  334. glfwSetWindowSizeLimits(gWindow, 640, 480, GLFW_DONT_CARE, GLFW_DONT_CARE);
  335. // Set up NanoVG
  336. int nvgFlags = NVG_ANTIALIAS;
  337. #if defined NANOVG_GL2
  338. gVg = nvgCreateGL2(nvgFlags);
  339. #elif defined NANOVG_GL3
  340. gVg = nvgCreateGL3(nvgFlags);
  341. #elif defined NANOVG_GLES2
  342. gVg = nvgCreateGLES2(nvgFlags);
  343. #endif
  344. assert(gVg);
  345. #if defined NANOVG_GL2
  346. gFramebufferVg = nvgCreateGL2(nvgFlags);
  347. #elif defined NANOVG_GL3
  348. gFramebufferVg = nvgCreateGL3(nvgFlags);
  349. #elif defined NANOVG_GLES2
  350. gFramebufferVg = nvgCreateGLES2(nvgFlags);
  351. #endif
  352. assert(gFramebufferVg);
  353. // Set up Blendish
  354. gGuiFont = Font::load(assetGlobal("res/fonts/DejaVuSans.ttf"));
  355. bndSetFont(gGuiFont->handle);
  356. windowSetTheme(nvgRGB(0x33, 0x33, 0x33), nvgRGB(0xf0, 0xf0, 0xf0));
  357. }
  358. void windowDestroy() {
  359. gGuiFont.reset();
  360. #if defined NANOVG_GL2
  361. nvgDeleteGL2(gVg);
  362. #elif defined NANOVG_GL3
  363. nvgDeleteGL3(gVg);
  364. #elif defined NANOVG_GLES2
  365. nvgDeleteGLES2(gVg);
  366. #endif
  367. #if defined NANOVG_GL2
  368. nvgDeleteGL2(gFramebufferVg);
  369. #elif defined NANOVG_GL3
  370. nvgDeleteGL3(gFramebufferVg);
  371. #elif defined NANOVG_GLES2
  372. nvgDeleteGLES2(gFramebufferVg);
  373. #endif
  374. glfwDestroyWindow(gWindow);
  375. glfwTerminate();
  376. }
  377. void windowRun() {
  378. assert(gWindow);
  379. gGuiFrame = 0;
  380. while(!glfwWindowShouldClose(gWindow)) {
  381. double startTime = glfwGetTime();
  382. gGuiFrame++;
  383. // Poll events
  384. glfwPollEvents();
  385. {
  386. double xpos, ypos;
  387. glfwGetCursorPos(gWindow, &xpos, &ypos);
  388. cursorPosCallback(gWindow, xpos, ypos);
  389. }
  390. mouseButtonStickyPop();
  391. gamepadStep();
  392. // Set window title
  393. std::string windowTitle;
  394. windowTitle = gApplicationName;
  395. windowTitle += " ";
  396. windowTitle += gApplicationVersion;
  397. if (!gRackWidget->lastPath.empty()) {
  398. windowTitle += " - ";
  399. windowTitle += stringFilename(gRackWidget->lastPath);
  400. }
  401. if (windowTitle != lastWindowTitle) {
  402. glfwSetWindowTitle(gWindow, windowTitle.c_str());
  403. lastWindowTitle = windowTitle;
  404. }
  405. // Get desired scaling
  406. float pixelRatio;
  407. glfwGetWindowContentScale(gWindow, &pixelRatio, NULL);
  408. pixelRatio = roundf(pixelRatio);
  409. if (pixelRatio != gPixelRatio) {
  410. EventZoom eZoom;
  411. gScene->onZoom(eZoom);
  412. gPixelRatio = pixelRatio;
  413. }
  414. // Get framebuffer/window ratio
  415. int width, height;
  416. glfwGetFramebufferSize(gWindow, &width, &height);
  417. int windowWidth, windowHeight;
  418. glfwGetWindowSize(gWindow, &windowWidth, &windowHeight);
  419. gWindowRatio = (float)width / windowWidth;
  420. gScene->box.size = Vec(width, height).div(gPixelRatio);
  421. // Step scene
  422. gScene->step();
  423. // Render
  424. bool visible = glfwGetWindowAttrib(gWindow, GLFW_VISIBLE) && !glfwGetWindowAttrib(gWindow, GLFW_ICONIFIED);
  425. if (visible) {
  426. renderGui();
  427. }
  428. // Limit framerate manually if vsync isn't working
  429. double endTime = glfwGetTime();
  430. double frameTime = endTime - startTime;
  431. double minTime = 1.0 / 90.0;
  432. if (frameTime < minTime) {
  433. std::this_thread::sleep_for(std::chrono::duration<double>(minTime - frameTime));
  434. }
  435. endTime = glfwGetTime();
  436. // info("%lf fps", 1.0 / (endTime - startTime));
  437. }
  438. }
  439. void windowClose() {
  440. glfwSetWindowShouldClose(gWindow, GLFW_TRUE);
  441. }
  442. void windowCursorLock() {
  443. if (gAllowCursorLock) {
  444. #ifdef ARCH_MAC
  445. glfwSetInputMode(gWindow, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
  446. #else
  447. glfwSetInputMode(gWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
  448. #endif
  449. }
  450. }
  451. void windowCursorUnlock() {
  452. if (gAllowCursorLock) {
  453. glfwSetInputMode(gWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
  454. }
  455. }
  456. bool windowIsModPressed() {
  457. #ifdef ARCH_MAC
  458. return glfwGetKey(gWindow, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS || glfwGetKey(gWindow, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS;
  459. #else
  460. return glfwGetKey(gWindow, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(gWindow, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS;
  461. #endif
  462. }
  463. bool windowIsShiftPressed() {
  464. return glfwGetKey(gWindow, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || glfwGetKey(gWindow, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS;
  465. }
  466. Vec windowGetWindowSize() {
  467. int width, height;
  468. glfwGetWindowSize(gWindow, &width, &height);
  469. return Vec(width, height);
  470. }
  471. void windowSetWindowSize(Vec size) {
  472. int width = size.x;
  473. int height = size.y;
  474. glfwSetWindowSize(gWindow, width, height);
  475. }
  476. Vec windowGetWindowPos() {
  477. int x, y;
  478. glfwGetWindowPos(gWindow, &x, &y);
  479. return Vec(x, y);
  480. }
  481. void windowSetWindowPos(Vec pos) {
  482. int x = pos.x;
  483. int y = pos.y;
  484. glfwSetWindowPos(gWindow, x, y);
  485. }
  486. bool windowIsMaximized() {
  487. return glfwGetWindowAttrib(gWindow, GLFW_MAXIMIZED);
  488. }
  489. void windowSetTheme(NVGcolor bg, NVGcolor fg) {
  490. // Assume dark background and light foreground
  491. BNDwidgetTheme w;
  492. w.outlineColor = bg;
  493. w.itemColor = fg;
  494. w.innerColor = bg;
  495. w.innerSelectedColor = colorPlus(bg, nvgRGB(0x30, 0x30, 0x30));
  496. w.textColor = fg;
  497. w.textSelectedColor = fg;
  498. w.shadeTop = 0;
  499. w.shadeDown = 0;
  500. BNDtheme t;
  501. t.backgroundColor = colorPlus(bg, nvgRGB(0x30, 0x30, 0x30));
  502. t.regularTheme = w;
  503. t.toolTheme = w;
  504. t.radioTheme = w;
  505. t.textFieldTheme = w;
  506. t.optionTheme = w;
  507. t.choiceTheme = w;
  508. t.numberFieldTheme = w;
  509. t.sliderTheme = w;
  510. t.scrollBarTheme = w;
  511. t.tooltipTheme = w;
  512. t.menuTheme = w;
  513. t.menuItemTheme = w;
  514. t.sliderTheme.itemColor = bg;
  515. t.sliderTheme.innerColor = colorPlus(bg, nvgRGB(0x50, 0x50, 0x50));
  516. t.sliderTheme.innerSelectedColor = colorPlus(bg, nvgRGB(0x60, 0x60, 0x60));
  517. t.textFieldTheme = t.sliderTheme;
  518. t.textFieldTheme.textColor = colorMinus(bg, nvgRGB(0x20, 0x20, 0x20));
  519. t.textFieldTheme.textSelectedColor = t.textFieldTheme.textColor;
  520. t.scrollBarTheme.itemColor = colorPlus(bg, nvgRGB(0x50, 0x50, 0x50));
  521. t.scrollBarTheme.innerColor = bg;
  522. t.menuTheme.innerColor = colorMinus(bg, nvgRGB(0x10, 0x10, 0x10));
  523. t.menuTheme.textColor = colorMinus(fg, nvgRGB(0x50, 0x50, 0x50));
  524. t.menuTheme.textSelectedColor = t.menuTheme.textColor;
  525. bndSetTheme(t);
  526. }
  527. static int windowX = 0;
  528. static int windowY = 0;
  529. static int windowWidth = 0;
  530. static int windowHeight = 0;
  531. void windowSetFullScreen(bool fullScreen) {
  532. if (windowGetFullScreen()) {
  533. glfwSetWindowMonitor(gWindow, NULL, windowX, windowY, windowWidth, windowHeight, GLFW_DONT_CARE);
  534. }
  535. else {
  536. glfwGetWindowPos(gWindow, &windowX, &windowY);
  537. glfwGetWindowSize(gWindow, &windowWidth, &windowHeight);
  538. GLFWmonitor *monitor = glfwGetPrimaryMonitor();
  539. const GLFWvidmode* mode = glfwGetVideoMode(monitor);
  540. glfwSetWindowMonitor(gWindow, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
  541. }
  542. }
  543. bool windowGetFullScreen() {
  544. GLFWmonitor *monitor = glfwGetWindowMonitor(gWindow);
  545. return monitor != NULL;
  546. }
  547. ////////////////////
  548. // resources
  549. ////////////////////
  550. Font::Font(const std::string &filename) {
  551. handle = nvgCreateFont(gVg, filename.c_str(), filename.c_str());
  552. if (handle >= 0) {
  553. info("Loaded font %s", filename.c_str());
  554. }
  555. else {
  556. warn("Failed to load font %s", filename.c_str());
  557. }
  558. }
  559. Font::~Font() {
  560. // There is no NanoVG deleteFont() function yet, so do nothing
  561. }
  562. std::shared_ptr<Font> Font::load(const std::string &filename) {
  563. static std::map<std::string, std::weak_ptr<Font>> cache;
  564. auto sp = cache[filename].lock();
  565. if (!sp)
  566. cache[filename] = sp = std::make_shared<Font>(filename);
  567. return sp;
  568. }
  569. ////////////////////
  570. // Image
  571. ////////////////////
  572. Image::Image(const std::string &filename) {
  573. handle = nvgCreateImage(gVg, filename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY);
  574. if (handle > 0) {
  575. info("Loaded image %s", filename.c_str());
  576. }
  577. else {
  578. warn("Failed to load image %s", filename.c_str());
  579. }
  580. }
  581. Image::~Image() {
  582. // TODO What if handle is invalid?
  583. nvgDeleteImage(gVg, handle);
  584. }
  585. std::shared_ptr<Image> Image::load(const std::string &filename) {
  586. static std::map<std::string, std::weak_ptr<Image>> cache;
  587. auto sp = cache[filename].lock();
  588. if (!sp)
  589. cache[filename] = sp = std::make_shared<Image>(filename);
  590. return sp;
  591. }
  592. ////////////////////
  593. // SVG
  594. ////////////////////
  595. SVG::SVG(const std::string &filename) {
  596. handle = nsvgParseFromFile(filename.c_str(), "px", SVG_DPI);
  597. if (handle) {
  598. info("Loaded SVG %s", filename.c_str());
  599. }
  600. else {
  601. warn("Failed to load SVG %s", filename.c_str());
  602. }
  603. }
  604. SVG::~SVG() {
  605. nsvgDelete(handle);
  606. }
  607. std::shared_ptr<SVG> SVG::load(const std::string &filename) {
  608. static std::map<std::string, std::weak_ptr<SVG>> cache;
  609. auto sp = cache[filename].lock();
  610. if (!sp)
  611. cache[filename] = sp = std::make_shared<SVG>(filename);
  612. return sp;
  613. }
  614. } // namespace rack