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.

412 lines
10KB

  1. #include <map>
  2. #include <GL/glew.h>
  3. #include <GLFW/glfw3.h>
  4. #include "gui.hpp"
  5. #include "app.hpp"
  6. #define NANOVG_GL2_IMPLEMENTATION
  7. // #define NANOVG_GL3_IMPLEMENTATION
  8. #include "../ext/nanovg/src/nanovg_gl.h"
  9. // Hack to get framebuffer objects working on OpenGL 2 (we blindly assume the extension is supported)
  10. #define NANOVG_FBO_VALID 1
  11. #include "../ext/nanovg/src/nanovg_gl_utils.h"
  12. #define BLENDISH_IMPLEMENTATION
  13. #include "../ext/oui-blendish/blendish.h"
  14. #define NANOSVG_IMPLEMENTATION
  15. #include "../ext/nanosvg/src/nanosvg.h"
  16. #ifdef ARCH_MAC
  17. // For CGAssociateMouseAndMouseCursorPosition
  18. #include <ApplicationServices/ApplicationServices.h>
  19. #endif
  20. namespace rack {
  21. static GLFWwindow *window = NULL;
  22. std::shared_ptr<Font> gGuiFont;
  23. NVGcontext *gVg = NULL;
  24. float gPixelRatio = 0.0;
  25. void windowSizeCallback(GLFWwindow* window, int width, int height) {
  26. gScene->box.size = Vec(width, height);
  27. }
  28. void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) {
  29. #ifdef ARCH_MAC
  30. // Ctrl-left click --> right click
  31. if (button == GLFW_MOUSE_BUTTON_LEFT) {
  32. if (guiIsKeyPressed(GLFW_KEY_LEFT_CONTROL) || guiIsKeyPressed(GLFW_KEY_RIGHT_CONTROL))
  33. button = GLFW_MOUSE_BUTTON_RIGHT;
  34. }
  35. #endif
  36. if (action == GLFW_PRESS) {
  37. // onMouseDown
  38. Widget *w = gScene->onMouseDown(gMousePos, button);
  39. if (button == GLFW_MOUSE_BUTTON_LEFT) {
  40. if (w) {
  41. // onDragStart
  42. w->onDragStart();
  43. }
  44. gDraggedWidget = w;
  45. if (w != gSelectedWidget) {
  46. if (gSelectedWidget) {
  47. w->onDeselect();
  48. }
  49. if (w) {
  50. w->onSelect();
  51. }
  52. gSelectedWidget = w;
  53. }
  54. }
  55. }
  56. else if (action == GLFW_RELEASE) {
  57. // onMouseUp
  58. Widget *w = gScene->onMouseUp(gMousePos, button);
  59. if (button == GLFW_MOUSE_BUTTON_LEFT) {
  60. if (gDraggedWidget) {
  61. // onDragDrop
  62. w->onDragDrop(gDraggedWidget);
  63. }
  64. // gDraggedWidget might have been set to null in the last event, recheck here
  65. if (gDraggedWidget) {
  66. // onDragEnd
  67. gDraggedWidget->onDragEnd();
  68. }
  69. gDraggedWidget = NULL;
  70. gDragHoveredWidget = NULL;
  71. }
  72. }
  73. }
  74. void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) {
  75. Vec mousePos = Vec(xpos, ypos).round();
  76. Vec mouseRel = mousePos.minus(gMousePos);
  77. #ifdef ARCH_MAC
  78. // Workaround for Mac. We can't use GLFW_CURSOR_DISABLED because it's buggy, so implement it on our own.
  79. // This is not an ideal implementation. For example, if the user drags off the screen, the new mouse position will be clamped.
  80. int mouseMode = glfwGetInputMode(window, GLFW_CURSOR);
  81. if (mouseMode == GLFW_CURSOR_HIDDEN) {
  82. // CGSetLocalEventsSuppressionInterval(0.0);
  83. glfwSetCursorPos(window, gMousePos.x, gMousePos.y);
  84. CGAssociateMouseAndMouseCursorPosition(true);
  85. mousePos = gMousePos;
  86. }
  87. // Because sometimes the cursor turns into an arrow when its position is on the boundary of the window
  88. glfwSetCursor(window, NULL);
  89. #endif
  90. gMousePos = mousePos;
  91. // onMouseMove
  92. Widget *hovered = gScene->onMouseMove(gMousePos, mouseRel);
  93. if (gDraggedWidget) {
  94. // onDragMove
  95. gDraggedWidget->onDragMove(mouseRel);
  96. if (hovered != gDragHoveredWidget) {
  97. if (gDragHoveredWidget) {
  98. gDragHoveredWidget->onDragLeave(gDraggedWidget);
  99. }
  100. if (hovered) {
  101. hovered->onDragEnter(gDraggedWidget);
  102. }
  103. gDragHoveredWidget = hovered;
  104. }
  105. }
  106. else {
  107. if (hovered != gHoveredWidget) {
  108. if (gHoveredWidget) {
  109. // onMouseLeave
  110. gHoveredWidget->onMouseLeave();
  111. }
  112. if (hovered) {
  113. // onMouseEnter
  114. hovered->onMouseEnter();
  115. }
  116. gHoveredWidget = hovered;
  117. }
  118. }
  119. }
  120. void cursorEnterCallback(GLFWwindow* window, int entered) {
  121. if (!entered) {
  122. if (gHoveredWidget) {
  123. gHoveredWidget->onMouseLeave();
  124. }
  125. gHoveredWidget = NULL;
  126. }
  127. }
  128. void scrollCallback(GLFWwindow *window, double x, double y) {
  129. Vec scrollRel = Vec(x, y);
  130. // onScroll
  131. gScene->onScroll(gMousePos, scrollRel.mult(-95));
  132. }
  133. void charCallback(GLFWwindow *window, unsigned int codepoint) {
  134. if (gSelectedWidget) {
  135. gSelectedWidget->onText(codepoint);
  136. }
  137. }
  138. static int lastWindowX, lastWindowY, lastWindowWidth, lastWindowHeight;
  139. void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
  140. if (action == GLFW_PRESS || action == GLFW_REPEAT) {
  141. if (key == GLFW_KEY_F11 || key == GLFW_KEY_ESCAPE) {
  142. /*
  143. // Toggle fullscreen
  144. GLFWmonitor *monitor = glfwGetWindowMonitor(window);
  145. if (monitor) {
  146. // Window mode
  147. glfwSetWindowMonitor(window, NULL, lastWindowX, lastWindowY, lastWindowWidth, lastWindowHeight, 0);
  148. }
  149. else {
  150. // Fullscreen
  151. glfwGetWindowPos(window, &lastWindowX, &lastWindowY);
  152. glfwGetWindowSize(window, &lastWindowWidth, &lastWindowHeight);
  153. monitor = glfwGetPrimaryMonitor();
  154. assert(monitor);
  155. const GLFWvidmode *mode = glfwGetVideoMode(monitor);
  156. glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
  157. }
  158. */
  159. }
  160. else {
  161. if (gSelectedWidget) {
  162. gSelectedWidget->onKey(key);
  163. }
  164. }
  165. }
  166. }
  167. void errorCallback(int error, const char *description) {
  168. fprintf(stderr, "GLFW error %d: %s\n", error, description);
  169. }
  170. void renderGui() {
  171. int width, height;
  172. glfwGetFramebufferSize(window, &width, &height);
  173. int windowWidth, windowHeight;
  174. glfwGetWindowSize(window, &windowWidth, &windowHeight);
  175. gPixelRatio = (float)width / windowWidth;
  176. bool visible = glfwGetWindowAttrib(window, GLFW_VISIBLE) && !glfwGetWindowAttrib(window, GLFW_ICONIFIED);
  177. if (visible) {
  178. // Update and render
  179. glViewport(0, 0, width, height);
  180. glClearColor(0.0, 0.0, 0.0, 1.0);
  181. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  182. nvgBeginFrame(gVg, width, height, gPixelRatio);
  183. nvgSave(gVg);
  184. nvgReset(gVg);
  185. nvgScale(gVg, gPixelRatio, gPixelRatio);
  186. gScene->draw(gVg);
  187. nvgRestore(gVg);
  188. nvgEndFrame(gVg);
  189. }
  190. glfwSwapBuffers(window);
  191. }
  192. void guiInit() {
  193. int err;
  194. // Set up GLFW
  195. glfwSetErrorCallback(errorCallback);
  196. err = glfwInit();
  197. assert(err);
  198. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
  199. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
  200. // glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  201. // glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
  202. // glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
  203. // glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  204. glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE);
  205. std::string title = gApplicationName + " " + gApplicationVersion;
  206. window = glfwCreateWindow(1000, 750, title.c_str(), NULL, NULL);
  207. assert(window);
  208. glfwMakeContextCurrent(window);
  209. glfwSwapInterval(1);
  210. glfwSetWindowSizeCallback(window, windowSizeCallback);
  211. glfwSetMouseButtonCallback(window, mouseButtonCallback);
  212. // glfwSetCursorPosCallback(window, cursorPosCallback);
  213. glfwSetCursorEnterCallback(window, cursorEnterCallback);
  214. glfwSetScrollCallback(window, scrollCallback);
  215. glfwSetCharCallback(window, charCallback);
  216. glfwSetKeyCallback(window, keyCallback);
  217. // Set up GLEW
  218. glewExperimental = GL_TRUE;
  219. err = glewInit();
  220. assert(err == GLEW_OK);
  221. // Check framebuffer support
  222. assert(GLEW_EXT_framebuffer_object);
  223. // GLEW generates GL error because it calls glGetString(GL_EXTENSIONS), we'll consume it here.
  224. glGetError();
  225. glfwSetWindowSizeLimits(window, 640, 480, GLFW_DONT_CARE, GLFW_DONT_CARE);
  226. // Set up NanoVG
  227. gVg = nvgCreateGL2(NVG_ANTIALIAS);
  228. // gVg = nvgCreateGL3(NVG_ANTIALIAS);
  229. assert(gVg);
  230. // Set up Blendish
  231. gGuiFont = Font::load("res/DejaVuSans.ttf");
  232. bndSetFont(gGuiFont->handle);
  233. // bndSetIconImage(loadImage("res/icons.png"));
  234. }
  235. void guiDestroy() {
  236. gGuiFont.reset();
  237. nvgDeleteGL2(gVg);
  238. // nvgDeleteGL3(gVg);
  239. glfwDestroyWindow(window);
  240. glfwTerminate();
  241. }
  242. void guiRun() {
  243. assert(window);
  244. {
  245. int width, height;
  246. glfwGetWindowSize(window, &width, &height);
  247. windowSizeCallback(window, width, height);
  248. }
  249. gGuiFrame = 0;
  250. double lastTime = 0.0;
  251. while(!glfwWindowShouldClose(window)) {
  252. gGuiFrame++;
  253. glfwPollEvents();
  254. {
  255. double xpos, ypos;
  256. glfwGetCursorPos(window, &xpos, &ypos);
  257. cursorPosCallback(window, xpos, ypos);
  258. }
  259. gScene->step();
  260. renderGui();
  261. double currTime = glfwGetTime();
  262. // printf("%lf fps\n", 1.0/(currTime - lastTime));
  263. lastTime = currTime;
  264. (void) lastTime;
  265. }
  266. }
  267. bool guiIsKeyPressed(int key) {
  268. return glfwGetKey(window, key) == GLFW_PRESS;
  269. }
  270. void guiCursorLock() {
  271. #ifdef ARCH_MAC
  272. glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
  273. #else
  274. glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
  275. #endif
  276. }
  277. void guiCursorUnlock() {
  278. glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
  279. }
  280. ////////////////////
  281. // resources
  282. ////////////////////
  283. Font::Font(const std::string &filename) {
  284. handle = nvgCreateFont(gVg, filename.c_str(), filename.c_str());
  285. if (handle >= 0) {
  286. fprintf(stderr, "Loaded font %s\n", filename.c_str());
  287. }
  288. else {
  289. fprintf(stderr, "Failed to load font %s\n", filename.c_str());
  290. }
  291. }
  292. Font::~Font() {
  293. // There is no NanoVG deleteFont() function yet, so do nothing
  294. }
  295. std::shared_ptr<Font> Font::load(const std::string &filename) {
  296. static std::map<std::string, std::weak_ptr<Font>> cache;
  297. auto sp = cache[filename].lock();
  298. if (!sp)
  299. cache[filename] = sp = std::make_shared<Font>(filename);
  300. return sp;
  301. }
  302. ////////////////////
  303. // Image
  304. ////////////////////
  305. Image::Image(const std::string &filename) {
  306. handle = nvgCreateImage(gVg, filename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY);
  307. if (handle > 0) {
  308. fprintf(stderr, "Loaded image %s\n", filename.c_str());
  309. }
  310. else {
  311. fprintf(stderr, "Failed to load image %s\n", filename.c_str());
  312. }
  313. }
  314. Image::~Image() {
  315. // TODO What if handle is invalid?
  316. nvgDeleteImage(gVg, handle);
  317. }
  318. std::shared_ptr<Image> Image::load(const std::string &filename) {
  319. static std::map<std::string, std::weak_ptr<Image>> cache;
  320. auto sp = cache[filename].lock();
  321. if (!sp)
  322. cache[filename] = sp = std::make_shared<Image>(filename);
  323. return sp;
  324. }
  325. ////////////////////
  326. // SVG
  327. ////////////////////
  328. SVG::SVG(const std::string &filename) {
  329. handle = nsvgParseFromFile(filename.c_str(), "px", 96.0);
  330. if (handle) {
  331. fprintf(stderr, "Loaded SVG %s\n", filename.c_str());
  332. }
  333. else {
  334. fprintf(stderr, "Failed to load SVG %s\n", filename.c_str());
  335. }
  336. }
  337. SVG::~SVG() {
  338. nsvgDelete(handle);
  339. }
  340. std::shared_ptr<SVG> SVG::load(const std::string &filename) {
  341. static std::map<std::string, std::weak_ptr<SVG>> cache;
  342. auto sp = cache[filename].lock();
  343. if (!sp)
  344. cache[filename] = sp = std::make_shared<SVG>(filename);
  345. return sp;
  346. }
  347. } // namespace rack