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.

389 lines
8.7KB

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