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.

382 lines
8.5KB

  1. #include <map>
  2. #include <queue>
  3. #include <thread>
  4. #include <stb_image_write.h>
  5. #include <osdialog.h>
  6. #include <window/Window.hpp>
  7. #include <asset.hpp>
  8. #include <widget/Widget.hpp>
  9. #include <app/Scene.hpp>
  10. #include <keyboard.hpp>
  11. #include <context.hpp>
  12. #include <patch.hpp>
  13. #include <settings.hpp>
  14. #include <plugin.hpp> // used in Window::screenshot
  15. #include <system.hpp> // used in Window::screenshot
  16. #include "DistrhoUI.hpp"
  17. namespace rack {
  18. namespace window {
  19. extern DISTRHO_NAMESPACE::UI* lastUI;
  20. static const math::Vec minWindowSize = math::Vec(640, 480);
  21. void Font::loadFile(const std::string& filename, NVGcontext* vg) {
  22. this->vg = vg;
  23. handle = nvgCreateFont(vg, filename.c_str(), filename.c_str());
  24. if (handle < 0)
  25. throw Exception("Failed to load font %s", filename.c_str());
  26. INFO("Loaded font %s", filename.c_str());
  27. }
  28. Font::~Font() {
  29. // There is no NanoVG deleteFont() function yet, so do nothing
  30. }
  31. std::shared_ptr<Font> Font::load(const std::string& filename) {
  32. return APP->window->loadFont(filename);
  33. }
  34. void Image::loadFile(const std::string& filename, NVGcontext* vg) {
  35. this->vg = vg;
  36. handle = nvgCreateImage(vg, filename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY);
  37. if (handle <= 0)
  38. throw Exception("Failed to load image %s", filename.c_str());
  39. INFO("Loaded image %s", filename.c_str());
  40. }
  41. Image::~Image() {
  42. // TODO What if handle is invalid?
  43. if (handle >= 0)
  44. nvgDeleteImage(vg, handle);
  45. }
  46. std::shared_ptr<Image> Image::load(const std::string& filename) {
  47. return APP->window->loadImage(filename);
  48. }
  49. struct Window::Internal {
  50. DISTRHO_NAMESPACE::UI* ui;
  51. std::string lastWindowTitle;
  52. int lastWindowX = 0;
  53. int lastWindowY = 0;
  54. int lastWindowWidth = 0;
  55. int lastWindowHeight = 0;
  56. int frame = 0;
  57. bool ignoreNextMouseDelta = false;
  58. int frameSwapInterval = -1;
  59. double monitorRefreshRate = 0.0;
  60. double frameTime = 0.0;
  61. double lastFrameDuration = 0.0;
  62. math::Vec lastMousePos;
  63. std::map<std::string, std::shared_ptr<Font>> fontCache;
  64. std::map<std::string, std::shared_ptr<Image>> imageCache;
  65. bool fbDirtyOnSubpixelChange = true;
  66. };
  67. Window::Window() {
  68. internal = new Internal;
  69. internal->ui = lastUI;
  70. vg = lastUI->getContext();
  71. int err;
  72. const GLubyte* vendor = glGetString(GL_VENDOR);
  73. const GLubyte* renderer = glGetString(GL_RENDERER);
  74. const GLubyte* version = glGetString(GL_VERSION);
  75. INFO("Renderer: %s %s", vendor, renderer);
  76. INFO("OpenGL: %s", version);
  77. INFO("UI pointer: %p", lastUI);
  78. // GLEW generates GL error because it calls glGetString(GL_EXTENSIONS), we'll consume it here.
  79. glGetError();
  80. // Load default Blendish font
  81. uiFont = loadFont(asset::system("res/fonts/DejaVuSans.ttf"));
  82. bndSetFont(uiFont->handle);
  83. if (APP->scene) {
  84. widget::Widget::ContextCreateEvent e;
  85. APP->scene->onContextCreate(e);
  86. }
  87. }
  88. Window::~Window() {
  89. if (APP->scene) {
  90. widget::Widget::ContextDestroyEvent e;
  91. APP->scene->onContextDestroy(e);
  92. }
  93. // Fonts and Images in the cache must be deleted before the NanoVG context is deleted
  94. internal->fontCache.clear();
  95. internal->imageCache.clear();
  96. // nvgDeleteClone(fbVg);
  97. delete internal;
  98. }
  99. math::Vec Window::getSize() {
  100. return math::Vec(1280, 720);
  101. }
  102. void Window::setSize(math::Vec size) {
  103. size = size.max(minWindowSize);
  104. }
  105. void Window::run() {
  106. internal->frame = 0;
  107. }
  108. void Window::step() {
  109. double frameTime = system::getTime();
  110. double lastFrameTime = internal->frameTime;
  111. internal->frameTime = frameTime;
  112. internal->lastFrameDuration = frameTime - lastFrameTime;
  113. // DEBUG("%.2lf Hz", 1.0 / internal->lastFrameDuration);
  114. double t1 = 0.0, t2 = 0.0, t3 = 0.0, t4 = 0.0, t5 = 0.0;
  115. // Make event handlers and step() have a clean NanoVG context
  116. nvgReset(vg);
  117. bndSetFont(uiFont->handle);
  118. nvgFillColor(vg, nvgRGBf(1, 1, 1));
  119. nvgStrokeColor(vg, nvgRGBf(1, 1, 1));
  120. // Poll events
  121. // Save and restore context because event handler set their own context based on which window they originate from.
  122. Context* context = contextGet();
  123. // glfwPollEvents();
  124. contextSet(context);
  125. // Set window title
  126. std::string windowTitle = APP_NAME + " " + APP_EDITION_NAME + " " + APP_VERSION;
  127. if (APP->patch->path != "") {
  128. windowTitle += " - ";
  129. if (!APP->history->isSaved())
  130. windowTitle += "*";
  131. windowTitle += system::getFilename(APP->patch->path);
  132. }
  133. if (windowTitle != internal->lastWindowTitle) {
  134. internal->ui->getWindow().setTitle(windowTitle.c_str());
  135. internal->lastWindowTitle = windowTitle;
  136. }
  137. // Get desired pixel ratio
  138. float newPixelRatio = internal->ui->getScaleFactor();
  139. if (newPixelRatio != pixelRatio) {
  140. pixelRatio = newPixelRatio;
  141. APP->event->handleDirty();
  142. }
  143. // Get framebuffer/window ratio
  144. int winWidth = internal->ui->getWidth();
  145. int winHeight = internal->ui->getHeight();
  146. int fbWidth = winWidth;// * newPixelRatio;
  147. int fbHeight = winHeight;// * newPixelRatio;
  148. windowRatio = (float)fbWidth / winWidth;
  149. t1 = system::getTime();
  150. if (APP->scene) {
  151. // DEBUG("%f %f %d %d", pixelRatio, windowRatio, fbWidth, winWidth);
  152. // Resize scene
  153. APP->scene->box.size = math::Vec(fbWidth, fbHeight).div(pixelRatio);
  154. // Step scene
  155. APP->scene->step();
  156. t2 = system::getTime();
  157. // Render scene
  158. bool visible = true;
  159. if (visible) {
  160. // Update and render
  161. nvgBeginFrame(vg, fbWidth, fbHeight, pixelRatio);
  162. nvgScale(vg, pixelRatio, pixelRatio);
  163. // Draw scene
  164. widget::Widget::DrawArgs args;
  165. args.vg = vg;
  166. args.clipBox = APP->scene->box.zeroPos();
  167. APP->scene->draw(args);
  168. t3 = system::getTime();
  169. // glViewport(0, -winHeight, fbWidth, fbHeight);
  170. // glClearColor(0.0, 0.0, 0.0, 1.0);
  171. // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  172. nvgEndFrame(vg);
  173. t4 = system::getTime();
  174. }
  175. }
  176. t5 = system::getTime();
  177. // DEBUG("pre-step %6.1f step %6.1f draw %6.1f nvgEndFrame %6.1f glfwSwapBuffers %6.1f total %6.1f",
  178. // (t1 - frameTime) * 1e3f,
  179. // (t2 - t1) * 1e3f,
  180. // (t3 - t2) * 1e3f,
  181. // (t4 - t2) * 1e3f,
  182. // (t5 - t4) * 1e3f,
  183. // (t5 - frameTime) * 1e3f
  184. // );
  185. internal->frame++;
  186. }
  187. void Window::screenshot(const std::string&) {
  188. }
  189. void Window::screenshotModules(const std::string&, float) {
  190. }
  191. void Window::close() {
  192. internal->ui->getWindow().close();
  193. }
  194. void Window::cursorLock() {
  195. if (!settings::allowCursorLock)
  196. return;
  197. internal->ignoreNextMouseDelta = true;
  198. }
  199. void Window::cursorUnlock() {
  200. if (!settings::allowCursorLock)
  201. return;
  202. internal->ignoreNextMouseDelta = true;
  203. }
  204. bool Window::isCursorLocked() {
  205. return false;
  206. }
  207. int Window::getMods() {
  208. int mods = 0;
  209. /*
  210. if (glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)
  211. mods |= GLFW_MOD_SHIFT;
  212. if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS)
  213. mods |= GLFW_MOD_CONTROL;
  214. if (glfwGetKey(win, GLFW_KEY_LEFT_ALT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)
  215. mods |= GLFW_MOD_ALT;
  216. if (glfwGetKey(win, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)
  217. mods |= GLFW_MOD_SUPER;
  218. */
  219. return mods;
  220. }
  221. void Window::setFullScreen(bool) {
  222. }
  223. bool Window::isFullScreen() {
  224. return false;
  225. }
  226. double Window::getMonitorRefreshRate() {
  227. return internal->monitorRefreshRate;
  228. }
  229. double Window::getFrameTime() {
  230. return internal->frameTime;
  231. }
  232. double Window::getLastFrameDuration() {
  233. return internal->lastFrameDuration;
  234. }
  235. double Window::getFrameDurationRemaining() {
  236. double frameDurationDesired = internal->frameSwapInterval / internal->monitorRefreshRate;
  237. return frameDurationDesired - (system::getTime() - internal->frameTime);
  238. }
  239. std::shared_ptr<Font> Window::loadFont(const std::string& filename) {
  240. const auto& pair = internal->fontCache.find(filename);
  241. if (pair != internal->fontCache.end())
  242. return pair->second;
  243. // Load font
  244. std::shared_ptr<Font> font;
  245. try {
  246. font = std::make_shared<Font>();
  247. font->loadFile(filename, vg);
  248. }
  249. catch (Exception& e) {
  250. WARN("%s", e.what());
  251. font = NULL;
  252. }
  253. internal->fontCache[filename] = font;
  254. return font;
  255. }
  256. std::shared_ptr<Image> Window::loadImage(const std::string& filename) {
  257. const auto& pair = internal->imageCache.find(filename);
  258. if (pair != internal->imageCache.end())
  259. return pair->second;
  260. // Load image
  261. std::shared_ptr<Image> image;
  262. try {
  263. image = std::make_shared<Image>();
  264. image->loadFile(filename, vg);
  265. }
  266. catch (Exception& e) {
  267. WARN("%s", e.what());
  268. image = NULL;
  269. }
  270. internal->imageCache[filename] = image;
  271. return image;
  272. }
  273. bool& Window::fbDirtyOnSubpixelChange() {
  274. return internal->fbDirtyOnSubpixelChange;
  275. }
  276. void init() {
  277. }
  278. void destroy() {
  279. }
  280. } // namespace window
  281. } // namespace rack