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.

365 lines
8.6KB

  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. namespace rack {
  17. static GLFWwindow *window = NULL;
  18. std::shared_ptr<Font> gGuiFont;
  19. NVGcontext *gVg = NULL;
  20. void windowSizeCallback(GLFWwindow* window, int width, int height) {
  21. gScene->box.size = Vec(width, height);
  22. }
  23. void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) {
  24. if (action == GLFW_PRESS) {
  25. // onMouseDown
  26. Widget *w = gScene->onMouseDown(gMousePos, button);
  27. if (button == GLFW_MOUSE_BUTTON_LEFT) {
  28. if (w) {
  29. // onDragStart
  30. w->onDragStart();
  31. }
  32. gDraggedWidget = w;
  33. if (w != gSelectedWidget) {
  34. if (gSelectedWidget) {
  35. w->onDeselect();
  36. }
  37. if (w) {
  38. w->onSelect();
  39. }
  40. gSelectedWidget = w;
  41. }
  42. }
  43. }
  44. else if (action == GLFW_RELEASE) {
  45. // onMouseUp
  46. Widget *w = gScene->onMouseUp(gMousePos, button);
  47. if (button == GLFW_MOUSE_BUTTON_LEFT) {
  48. if (gDraggedWidget) {
  49. // onDragDrop
  50. w->onDragDrop(gDraggedWidget);
  51. }
  52. // gDraggedWidget might have been set to null in the last event, recheck here
  53. if (gDraggedWidget) {
  54. // onDragEnd
  55. gDraggedWidget->onDragEnd();
  56. }
  57. gDraggedWidget = NULL;
  58. gDragHoveredWidget = NULL;
  59. }
  60. }
  61. }
  62. void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) {
  63. Vec mousePos = Vec(xpos, ypos).round();
  64. Vec mouseRel = mousePos.minus(gMousePos);
  65. gMousePos = mousePos;
  66. // onMouseMove
  67. Widget *hovered = gScene->onMouseMove(gMousePos, mouseRel);
  68. if (gDraggedWidget) {
  69. // onDragMove
  70. gDraggedWidget->onDragMove(mouseRel);
  71. if (hovered != gDragHoveredWidget) {
  72. if (gDragHoveredWidget) {
  73. gDragHoveredWidget->onDragLeave(gDraggedWidget);
  74. }
  75. if (hovered) {
  76. hovered->onDragEnter(gDraggedWidget);
  77. }
  78. gDragHoveredWidget = hovered;
  79. }
  80. }
  81. else {
  82. if (hovered != gHoveredWidget) {
  83. if (gHoveredWidget) {
  84. // onMouseLeave
  85. gHoveredWidget->onMouseLeave();
  86. }
  87. if (hovered) {
  88. // onMouseEnter
  89. hovered->onMouseEnter();
  90. }
  91. gHoveredWidget = hovered;
  92. }
  93. }
  94. }
  95. void cursorEnterCallback(GLFWwindow* window, int entered) {
  96. if (!entered) {
  97. if (gHoveredWidget) {
  98. gHoveredWidget->onMouseLeave();
  99. }
  100. gHoveredWidget = NULL;
  101. }
  102. }
  103. void scrollCallback(GLFWwindow *window, double x, double y) {
  104. Vec scrollRel = Vec(x, y);
  105. // onScroll
  106. gScene->onScroll(gMousePos, scrollRel.mult(-95));
  107. }
  108. void charCallback(GLFWwindow *window, unsigned int codepoint) {
  109. if (gSelectedWidget) {
  110. gSelectedWidget->onText(codepoint);
  111. }
  112. }
  113. static int lastWindowX, lastWindowY, lastWindowWidth, lastWindowHeight;
  114. void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
  115. if (action == GLFW_PRESS || action == GLFW_REPEAT) {
  116. if (key == GLFW_KEY_F11 || key == GLFW_KEY_ESCAPE) {
  117. // Toggle fullscreen
  118. GLFWmonitor *monitor = glfwGetWindowMonitor(window);
  119. if (monitor) {
  120. // Window mode
  121. glfwSetWindowMonitor(window, NULL, lastWindowX, lastWindowY, lastWindowWidth, lastWindowHeight, 0);
  122. }
  123. else {
  124. // Fullscreen
  125. glfwGetWindowPos(window, &lastWindowX, &lastWindowY);
  126. glfwGetWindowSize(window, &lastWindowWidth, &lastWindowHeight);
  127. monitor = glfwGetPrimaryMonitor();
  128. assert(monitor);
  129. const GLFWvidmode *mode = glfwGetVideoMode(monitor);
  130. glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
  131. }
  132. }
  133. else {
  134. if (gSelectedWidget) {
  135. gSelectedWidget->onKey(key);
  136. }
  137. }
  138. }
  139. }
  140. void renderGui() {
  141. int width, height;
  142. glfwGetFramebufferSize(window, &width, &height);
  143. // glfwGetWindowSize(window, &width, &height);
  144. // Update and render
  145. glViewport(0, 0, width, height);
  146. glClearColor(0.0, 0.0, 0.0, 1.0);
  147. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  148. nvgBeginFrame(gVg, width, height, 1.0);
  149. nvgSave(gVg);
  150. gScene->draw(gVg);
  151. nvgRestore(gVg);
  152. nvgEndFrame(gVg);
  153. glfwSwapBuffers(window);
  154. }
  155. void guiInit() {
  156. int err;
  157. // Set up GLFW
  158. err = glfwInit();
  159. assert(err);
  160. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
  161. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
  162. // glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  163. // glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
  164. // glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
  165. // glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  166. glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE);
  167. std::string title = gApplicationName + " " + gApplicationVersion;
  168. window = glfwCreateWindow(1000, 750, title.c_str(), NULL, NULL);
  169. assert(window);
  170. glfwMakeContextCurrent(window);
  171. glfwSwapInterval(1);
  172. glfwSetWindowSizeCallback(window, windowSizeCallback);
  173. glfwSetMouseButtonCallback(window, mouseButtonCallback);
  174. // glfwSetCursorPosCallback(window, cursorPosCallback);
  175. glfwSetCursorEnterCallback(window, cursorEnterCallback);
  176. glfwSetScrollCallback(window, scrollCallback);
  177. glfwSetCharCallback(window, charCallback);
  178. glfwSetKeyCallback(window, keyCallback);
  179. // Set up GLEW
  180. glewExperimental = GL_TRUE;
  181. err = glewInit();
  182. assert(err == GLEW_OK);
  183. // Check framebuffer support
  184. assert(GLEW_EXT_framebuffer_object);
  185. // GLEW generates GL error because it calls glGetString(GL_EXTENSIONS), we'll consume it here.
  186. glGetError();
  187. glfwSetWindowSizeLimits(window, 240, 160, GLFW_DONT_CARE, GLFW_DONT_CARE);
  188. // Set up NanoVG
  189. gVg = nvgCreateGL2(NVG_ANTIALIAS);
  190. // gVg = nvgCreateGL3(NVG_ANTIALIAS);
  191. assert(gVg);
  192. // Set up Blendish
  193. gGuiFont = Font::load("res/DejaVuSans.ttf");
  194. bndSetFont(gGuiFont->handle);
  195. // bndSetIconImage(loadImage("res/icons.png"));
  196. }
  197. void guiDestroy() {
  198. gGuiFont.reset();
  199. nvgDeleteGL2(gVg);
  200. // nvgDeleteGL3(gVg);
  201. glfwDestroyWindow(window);
  202. glfwTerminate();
  203. }
  204. void guiRun() {
  205. assert(window);
  206. {
  207. int width, height;
  208. glfwGetWindowSize(window, &width, &height);
  209. windowSizeCallback(window, width, height);
  210. }
  211. gGuiFrame = 0;
  212. double lastTime = 0.0;
  213. while(!glfwWindowShouldClose(window)) {
  214. gGuiFrame++;
  215. glfwPollEvents();
  216. {
  217. double xpos, ypos;
  218. glfwGetCursorPos(window, &xpos, &ypos);
  219. cursorPosCallback(window, xpos, ypos);
  220. }
  221. gScene->step();
  222. renderGui();
  223. double currTime = glfwGetTime();
  224. // printf("%lf fps\n", 1.0/(currTime - lastTime));
  225. lastTime = currTime;
  226. (void) lastTime;
  227. }
  228. }
  229. bool guiIsKeyPressed(int key) {
  230. return glfwGetKey(window, key) == GLFW_PRESS;
  231. }
  232. void guiCursorLock() {
  233. glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
  234. }
  235. void guiCursorUnlock() {
  236. glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
  237. }
  238. ////////////////////
  239. // resources
  240. ////////////////////
  241. Font::Font(const std::string &filename) {
  242. handle = nvgCreateFont(gVg, filename.c_str(), filename.c_str());
  243. if (handle >= 0) {
  244. fprintf(stderr, "Loaded font %s\n", filename.c_str());
  245. }
  246. else {
  247. fprintf(stderr, "Failed to load font %s\n", filename.c_str());
  248. }
  249. }
  250. Font::~Font() {
  251. // There is no NanoVG deleteFont() function yet, so do nothing
  252. }
  253. std::shared_ptr<Font> Font::load(const std::string &filename) {
  254. static std::map<std::string, std::weak_ptr<Font>> cache;
  255. auto sp = cache[filename].lock();
  256. if (!sp)
  257. cache[filename] = sp = std::make_shared<Font>(filename);
  258. return sp;
  259. }
  260. ////////////////////
  261. // Image
  262. ////////////////////
  263. Image::Image(const std::string &filename) {
  264. handle = nvgCreateImage(gVg, filename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY);
  265. if (handle > 0) {
  266. fprintf(stderr, "Loaded image %s\n", filename.c_str());
  267. }
  268. else {
  269. fprintf(stderr, "Failed to load image %s\n", filename.c_str());
  270. }
  271. }
  272. Image::~Image() {
  273. // TODO What if handle is invalid?
  274. nvgDeleteImage(gVg, handle);
  275. }
  276. std::shared_ptr<Image> Image::load(const std::string &filename) {
  277. static std::map<std::string, std::weak_ptr<Image>> cache;
  278. auto sp = cache[filename].lock();
  279. if (!sp)
  280. cache[filename] = sp = std::make_shared<Image>(filename);
  281. return sp;
  282. }
  283. ////////////////////
  284. // SVG
  285. ////////////////////
  286. SVG::SVG(const std::string &filename) {
  287. handle = nsvgParseFromFile(filename.c_str(), "px", 96.0);
  288. if (handle) {
  289. fprintf(stderr, "Loaded SVG %s\n", filename.c_str());
  290. }
  291. else {
  292. fprintf(stderr, "Failed to load SVG %s\n", filename.c_str());
  293. }
  294. }
  295. SVG::~SVG() {
  296. nsvgDelete(handle);
  297. }
  298. std::shared_ptr<SVG> SVG::load(const std::string &filename) {
  299. static std::map<std::string, std::weak_ptr<SVG>> cache;
  300. auto sp = cache[filename].lock();
  301. if (!sp)
  302. cache[filename] = sp = std::make_shared<SVG>(filename);
  303. return sp;
  304. }
  305. } // namespace rack