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.

1455 lines
46KB

  1. --- ../Rack/src/window/Window.cpp 2023-12-17 12:57:01.139429461 +0100
  2. +++ Window.cpp 2023-10-22 13:33:43.777041594 +0200
  3. @@ -1,33 +1,94 @@
  4. +/*
  5. + * DISTRHO Cardinal Plugin
  6. + * Copyright (C) 2021-2023 Filipe Coelho <falktx@falktx.com>
  7. + *
  8. + * This program is free software; you can redistribute it and/or
  9. + * modify it under the terms of the GNU General Public License as
  10. + * published by the Free Software Foundation; either version 3 of
  11. + * the License, or any later version.
  12. + *
  13. + * This program is distributed in the hope that it will be useful,
  14. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. + * GNU General Public License for more details.
  17. + *
  18. + * For a full copy of the GNU General Public License see the LICENSE file.
  19. + */
  20. +
  21. +/**
  22. + * This file is an edited version of VCVRack's window/Window.cpp
  23. + * Copyright (C) 2016-2023 VCV.
  24. + *
  25. + * This program is free software: you can redistribute it and/or
  26. + * modify it under the terms of the GNU General Public License as
  27. + * published by the Free Software Foundation; either version 3 of
  28. + * the License, or (at your option) any later version.
  29. + */
  30. +
  31. #include <map>
  32. #include <queue>
  33. #include <thread>
  34. -#if defined ARCH_MAC
  35. - // For CGAssociateMouseAndMouseCursorPosition
  36. - #include <ApplicationServices/ApplicationServices.h>
  37. -#endif
  38. -
  39. -#include <stb_image_write.h>
  40. -#include <osdialog.h>
  41. -
  42. #include <window/Window.hpp>
  43. #include <asset.hpp>
  44. #include <widget/Widget.hpp>
  45. #include <app/Scene.hpp>
  46. -#include <keyboard.hpp>
  47. -#include <gamepad.hpp>
  48. #include <context.hpp>
  49. #include <patch.hpp>
  50. #include <settings.hpp>
  51. -#include <plugin.hpp> // used in Window::screenshot
  52. -#include <system.hpp> // used in Window::screenshot
  53. +#include <system.hpp>
  54. +
  55. +#ifdef NDEBUG
  56. +# undef DEBUG
  57. +#endif
  58. +
  59. +#include "DistrhoUI.hpp"
  60. +#include "Application.hpp"
  61. +#include "extra/String.hpp"
  62. +#include "../CardinalCommon.hpp"
  63. +#include "../PluginContext.hpp"
  64. +#include "../WindowParameters.hpp"
  65. +
  66. +#ifndef DGL_NO_SHARED_RESOURCES
  67. +# include "src/Resources.hpp"
  68. +#endif
  69. +#if !(defined(DGL_USE_GLES) || CARDINAL_VARIANT_MINI)
  70. +
  71. +#define CARDINAL_WINDOW_CAN_GENERATE_SCREENSHOTS
  72. +
  73. +// comment out if wanting to generate a local screenshot.png
  74. +#define STBI_WRITE_NO_STDIO
  75. +
  76. +// uncomment to generate screenshots without the rack rail background (ie, transparent)
  77. +// #define CARDINAL_TRANSPARENT_SCREENSHOTS
  78. +
  79. +// used in Window::screenshot
  80. +#define STB_IMAGE_WRITE_IMPLEMENTATION
  81. +#include "stb_image_write.h"
  82. +
  83. +#endif
  84. +
  85. +#ifdef DISTRHO_OS_WASM
  86. +# include <emscripten/html5.h>
  87. +#endif
  88. namespace rack {
  89. namespace window {
  90. -static const math::Vec WINDOW_SIZE_MIN = math::Vec(640, 480);
  91. +static const math::Vec WINDOW_SIZE_MIN = math::Vec(648, 538);
  92. +
  93. +
  94. +struct FontWithOriginalContext : Font {
  95. + int ohandle = -1;
  96. + std::string ofilename;
  97. +};
  98. +
  99. +struct ImageWithOriginalContext : Image {
  100. + int ohandle = -1;
  101. + std::string ofilename;
  102. +};
  103. Font::~Font() {
  104. @@ -42,9 +103,8 @@
  105. // Transfer ownership of font data to font object
  106. uint8_t* data = system::readFile(filename, &size);
  107. // Don't use nvgCreateFont because it doesn't properly handle UTF-8 filenames on Windows.
  108. - handle = nvgCreateFontMem(vg, name.c_str(), data, size, 0);
  109. + handle = nvgCreateFontMem(vg, name.c_str(), data, size, 1);
  110. if (handle < 0) {
  111. - std::free(data);
  112. throw Exception("Failed to load font %s", filename.c_str());
  113. }
  114. INFO("Loaded font %s", filename.c_str());
  115. @@ -79,340 +139,478 @@
  116. }
  117. +#ifdef CARDINAL_WINDOW_CAN_GENERATE_SCREENSHOTS
  118. +enum ScreenshotStep {
  119. + kScreenshotStepNone,
  120. + kScreenshotStepStarted,
  121. + kScreenshotStepFirstPass,
  122. + kScreenshotStepSecondPass,
  123. + kScreenshotStepSaving
  124. +};
  125. +#endif
  126. +
  127. +
  128. struct Window::Internal {
  129. std::string lastWindowTitle;
  130. - int lastWindowX = 0;
  131. - int lastWindowY = 0;
  132. - int lastWindowWidth = 0;
  133. - int lastWindowHeight = 0;
  134. + CardinalBaseUI* ui = nullptr;
  135. + DGL_NAMESPACE::NanoTopLevelWidget* tlw = nullptr;
  136. + DISTRHO_NAMESPACE::WindowParameters params;
  137. + DISTRHO_NAMESPACE::WindowParametersCallback* callback = nullptr;
  138. +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  139. + DGL_NAMESPACE::Application hiddenApp;
  140. + DGL_NAMESPACE::Window hiddenWindow;
  141. + NVGcontext* r_vg = nullptr;
  142. + NVGcontext* r_fbVg = nullptr;
  143. + NVGcontext* o_vg = nullptr;
  144. + NVGcontext* o_fbVg = nullptr;
  145. +#endif
  146. +
  147. + math::Vec size = WINDOW_SIZE_MIN;
  148. +
  149. + int mods = 0;
  150. + int currentRateLimit = 0;
  151. int frame = 0;
  152. - bool ignoreNextMouseDelta = false;
  153. - double monitorRefreshRate = 0.0;
  154. +#ifdef CARDINAL_WINDOW_CAN_GENERATE_SCREENSHOTS
  155. + int generateScreenshotStep = kScreenshotStepNone;
  156. +#endif
  157. + double monitorRefreshRate = 60.0;
  158. double frameTime = NAN;
  159. double lastFrameDuration = NAN;
  160. - math::Vec lastMousePos;
  161. -
  162. - std::map<std::string, std::shared_ptr<Font>> fontCache;
  163. - std::map<std::string, std::shared_ptr<Image>> imageCache;
  164. + std::map<std::string, std::shared_ptr<FontWithOriginalContext>> fontCache;
  165. + std::map<std::string, std::shared_ptr<ImageWithOriginalContext>> imageCache;
  166. bool fbDirtyOnSubpixelChange = true;
  167. int fbCount = 0;
  168. -};
  169. -
  170. -static void windowPosCallback(GLFWwindow* win, int x, int y) {
  171. - if (glfwGetWindowAttrib(win, GLFW_MAXIMIZED))
  172. - return;
  173. - if (glfwGetWindowAttrib(win, GLFW_ICONIFIED))
  174. - return;
  175. - if (glfwGetWindowMonitor(win))
  176. - return;
  177. - settings::windowPos = math::Vec(x, y);
  178. - // DEBUG("windowPosCallback %d %d", x, y);
  179. -}
  180. + Internal()
  181. +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  182. + : hiddenApp(false),
  183. + hiddenWindow(hiddenApp, 0, DISTRHO_UI_DEFAULT_WIDTH, DISTRHO_UI_DEFAULT_HEIGHT, 0.0, true)
  184. + {
  185. + hiddenWindow.setIgnoringKeyRepeat(true);
  186. + hiddenApp.idle();
  187. + }
  188. +#else
  189. + {}
  190. +#endif
  191. +};
  192. -static void windowSizeCallback(GLFWwindow* win, int width, int height) {
  193. - if (glfwGetWindowAttrib(win, GLFW_MAXIMIZED))
  194. - return;
  195. - if (glfwGetWindowAttrib(win, GLFW_ICONIFIED))
  196. - return;
  197. - if (glfwGetWindowMonitor(win))
  198. - return;
  199. - settings::windowSize = math::Vec(width, height);
  200. - // DEBUG("windowSizeCallback %d %d", width, height);
  201. -}
  202. +#ifndef DGL_NO_SHARED_RESOURCES
  203. +static int loadFallbackFont(NVGcontext* const vg)
  204. +{
  205. + const int font = nvgFindFont(vg, NANOVG_DEJAVU_SANS_TTF);
  206. + if (font >= 0)
  207. + return font;
  208. + using namespace dpf_resources;
  209. -static void windowMaximizeCallback(GLFWwindow* win, int maximized) {
  210. - settings::windowMaximized = maximized;
  211. - // DEBUG("windowMaximizeCallback %d", maximized);
  212. + return nvgCreateFontMem(vg, NANOVG_DEJAVU_SANS_TTF,
  213. + (uchar*)dejavusans_ttf, dejavusans_ttf_size, 0);
  214. }
  215. -
  216. -
  217. -static void mouseButtonCallback(GLFWwindow* win, int button, int action, int mods) {
  218. - contextSet((Context*) glfwGetWindowUserPointer(win));
  219. -#if defined ARCH_MAC
  220. - // Remap Ctrl-left click to right click on Mac
  221. - if (button == GLFW_MOUSE_BUTTON_LEFT && (mods & RACK_MOD_MASK) == GLFW_MOD_CONTROL) {
  222. - button = GLFW_MOUSE_BUTTON_RIGHT;
  223. - mods &= ~GLFW_MOD_CONTROL;
  224. - }
  225. - // Remap Ctrl-shift-left click to middle click on Mac
  226. - if (button == GLFW_MOUSE_BUTTON_LEFT && (mods & RACK_MOD_MASK) == (GLFW_MOD_CONTROL | GLFW_MOD_SHIFT)) {
  227. - button = GLFW_MOUSE_BUTTON_MIDDLE;
  228. - mods &= ~(GLFW_MOD_CONTROL | GLFW_MOD_SHIFT);
  229. - }
  230. #endif
  231. - APP->event->handleButton(APP->window->internal->lastMousePos, button, action, mods);
  232. -}
  233. -
  234. -static void cursorPosCallback(GLFWwindow* win, double xpos, double ypos) {
  235. - contextSet((Context*) glfwGetWindowUserPointer(win));
  236. - math::Vec mousePos = math::Vec(xpos, ypos).div(APP->window->pixelRatio / APP->window->windowRatio).round();
  237. - math::Vec mouseDelta = mousePos.minus(APP->window->internal->lastMousePos);
  238. +Window::Window() {
  239. + internal = new Internal;
  240. - // Workaround for GLFW warping mouse to a different position when the cursor is locked or unlocked.
  241. - if (APP->window->internal->ignoreNextMouseDelta) {
  242. - APP->window->internal->ignoreNextMouseDelta = false;
  243. - mouseDelta = math::Vec();
  244. - }
  245. + // Set up NanoVG
  246. + const int nvgFlags = NVG_ANTIALIAS;
  247. - int cursorMode = glfwGetInputMode(win, GLFW_CURSOR);
  248. - (void) cursorMode;
  249. +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  250. + const DGL_NAMESPACE::Window::ScopedGraphicsContext sgc(internal->hiddenWindow);
  251. + vg = nvgCreateGL(nvgFlags);
  252. +#else
  253. + vg = static_cast<CardinalPluginContext*>(APP)->tlw->getContext();
  254. +#endif
  255. + DISTRHO_SAFE_ASSERT_RETURN(vg != nullptr,);
  256. -#if defined ARCH_MAC
  257. - // Workaround for Mac. We can't use GLFW_CURSOR_DISABLED because it's buggy, so implement it on our own.
  258. - // This is not an ideal implementation. For example, if the user drags off the screen, the new mouse position will be clamped.
  259. - if (cursorMode == GLFW_CURSOR_HIDDEN) {
  260. - // CGSetLocalEventsSuppressionInterval(0.0);
  261. - glfwSetCursorPos(win, APP->window->internal->lastMousePos.x, APP->window->internal->lastMousePos.y);
  262. - CGAssociateMouseAndMouseCursorPosition(true);
  263. - mousePos = APP->window->internal->lastMousePos;
  264. - }
  265. - // Because sometimes the cursor turns into an arrow when its position is on the boundary of the window
  266. - glfwSetCursor(win, NULL);
  267. +#ifdef NANOVG_GLES2
  268. + fbVg = nvgCreateSharedGLES2(vg, nvgFlags);
  269. +#else
  270. + fbVg = nvgCreateSharedGL2(vg, nvgFlags);
  271. #endif
  272. - APP->window->internal->lastMousePos = mousePos;
  273. + // Load default Blendish font
  274. +#ifndef DGL_NO_SHARED_RESOURCES
  275. + uiFont = std::make_shared<Font>();
  276. + uiFont->vg = vg;
  277. + uiFont->handle = loadFallbackFont(vg);
  278. +
  279. + std::shared_ptr<FontWithOriginalContext> uiFont2;
  280. + uiFont2 = std::make_shared<FontWithOriginalContext>();
  281. + uiFont2->vg = vg;
  282. + uiFont2->handle = loadFallbackFont(vg);
  283. + uiFont2->ofilename = asset::system("res/fonts/DejaVuSans.ttf");
  284. + internal->fontCache[uiFont2->ofilename] = uiFont2;
  285. +#else
  286. + uiFont = loadFont(asset::system("res/fonts/DejaVuSans.ttf"));
  287. +#endif
  288. - APP->event->handleHover(mousePos, mouseDelta);
  289. + if (uiFont != nullptr)
  290. + bndSetFont(uiFont->handle);
  291. - // Keyboard/mouse MIDI driver
  292. - int width, height;
  293. - glfwGetWindowSize(win, &width, &height);
  294. - math::Vec scaledPos(xpos / width, ypos / height);
  295. - keyboard::mouseMove(scaledPos);
  296. +#ifdef DISTRHO_OS_WASM
  297. + emscripten_lock_orientation(EMSCRIPTEN_ORIENTATION_LANDSCAPE_PRIMARY);
  298. +#endif
  299. }
  300. -
  301. -static void cursorEnterCallback(GLFWwindow* win, int entered) {
  302. - contextSet((Context*) glfwGetWindowUserPointer(win));
  303. - if (!entered) {
  304. - APP->event->handleLeave();
  305. +void WindowSetPluginRemote(Window* const window, NanoTopLevelWidget* const tlw)
  306. +{
  307. + // if nanovg context failed, init only bare minimum
  308. + if (window->vg == nullptr)
  309. + {
  310. + if (tlw != nullptr)
  311. + {
  312. + window->internal->tlw = tlw;
  313. + window->internal->size = rack::math::Vec(tlw->getWidth(), tlw->getHeight());
  314. + }
  315. + else
  316. + {
  317. + window->internal->tlw = nullptr;
  318. + window->internal->callback = nullptr;
  319. + }
  320. + return;
  321. }
  322. -}
  323. -
  324. -static void scrollCallback(GLFWwindow* win, double x, double y) {
  325. - contextSet((Context*) glfwGetWindowUserPointer(win));
  326. - math::Vec scrollDelta = math::Vec(x, y);
  327. -#if defined ARCH_MAC
  328. - scrollDelta = scrollDelta.mult(10.0);
  329. + if (tlw != nullptr)
  330. + {
  331. + const GLubyte* vendor = glGetString(GL_VENDOR);
  332. + const GLubyte* renderer = glGetString(GL_RENDERER);
  333. + const GLubyte* version = glGetString(GL_VERSION);
  334. + INFO("Renderer: %s %s", vendor, renderer);
  335. + INFO("OpenGL: %s", version);
  336. +
  337. + window->internal->tlw = tlw;
  338. + window->internal->size = rack::math::Vec(tlw->getWidth(), tlw->getHeight());
  339. +
  340. +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  341. + // Set up NanoVG
  342. + window->internal->r_vg = tlw->getContext();
  343. +#ifdef NANOVG_GLES2
  344. + window->internal->r_fbVg = nvgCreateSharedGLES2(window->internal->r_vg, NVG_ANTIALIAS);
  345. #else
  346. - scrollDelta = scrollDelta.mult(50.0);
  347. + window->internal->r_fbVg = nvgCreateSharedGL2(window->internal->r_vg, NVG_ANTIALIAS);
  348. #endif
  349. - APP->event->handleScroll(APP->window->internal->lastMousePos, scrollDelta);
  350. -}
  351. + // swap contexts
  352. + window->internal->o_vg = window->vg;
  353. + window->internal->o_fbVg = window->fbVg;
  354. + window->vg = window->internal->r_vg;
  355. + window->fbVg = window->internal->r_fbVg;
  356. +
  357. + // also for fonts and images
  358. + window->uiFont->vg = window->vg;
  359. + window->uiFont->handle = loadFallbackFont(window->vg);
  360. + for (auto& font : window->internal->fontCache)
  361. + {
  362. + font.second->vg = window->vg;
  363. + font.second->ohandle = font.second->handle;
  364. + font.second->handle = nvgCreateFont(window->vg,
  365. + font.second->ofilename.c_str(), font.second->ofilename.c_str());
  366. + }
  367. + for (auto& image : window->internal->imageCache)
  368. + {
  369. + image.second->vg = window->vg;
  370. + image.second->ohandle = image.second->handle;
  371. + image.second->handle = nvgCreateImage(window->vg, image.second->ofilename.c_str(),
  372. + NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY);
  373. + }
  374. +#endif
  375. + // Init settings
  376. + WindowParametersRestore(window);
  377. -static void charCallback(GLFWwindow* win, unsigned int codepoint) {
  378. - contextSet((Context*) glfwGetWindowUserPointer(win));
  379. - if (APP->event->handleText(APP->window->internal->lastMousePos, codepoint))
  380. - return;
  381. -}
  382. + widget::Widget::ContextCreateEvent e = {};
  383. + e.vg = window->vg;
  384. + APP->scene->onContextCreate(e);
  385. + }
  386. + else
  387. + {
  388. + widget::Widget::ContextDestroyEvent e = {};
  389. + e.vg = window->vg;
  390. + APP->scene->onContextDestroy(e);
  391. +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  392. + // swap contexts
  393. + window->uiFont->vg = window->internal->o_vg;
  394. + window->vg = window->internal->o_vg;
  395. + window->fbVg = window->internal->o_fbVg;
  396. + window->internal->o_vg = nullptr;
  397. + window->internal->o_fbVg = nullptr;
  398. +
  399. + // also for fonts and images
  400. + window->uiFont->vg = window->vg;
  401. + window->uiFont->handle = loadFallbackFont(window->vg);
  402. + for (auto& font : window->internal->fontCache)
  403. + {
  404. + font.second->vg = window->vg;
  405. + font.second->handle = font.second->ohandle;
  406. + font.second->ohandle = -1;
  407. + }
  408. + for (auto& image : window->internal->imageCache)
  409. + {
  410. + image.second->vg = window->vg;
  411. + image.second->handle = image.second->ohandle;
  412. + image.second->ohandle = -1;
  413. + }
  414. -static void keyCallback(GLFWwindow* win, int key, int scancode, int action, int mods) {
  415. - contextSet((Context*) glfwGetWindowUserPointer(win));
  416. - if (APP->event->handleKey(APP->window->internal->lastMousePos, key, scancode, action, mods))
  417. - return;
  418. +#if defined NANOVG_GLES2
  419. + nvgDeleteGLES2(window->internal->r_fbVg);
  420. +#else
  421. + nvgDeleteGL2(window->internal->r_fbVg);
  422. +#endif
  423. +#endif
  424. - // Keyboard/mouse MIDI driver
  425. - if (action == GLFW_PRESS && (mods & RACK_MOD_MASK) == 0) {
  426. - keyboard::press(key);
  427. - }
  428. - if (action == GLFW_RELEASE) {
  429. - keyboard::release(key);
  430. + window->internal->tlw = nullptr;
  431. + window->internal->callback = nullptr;
  432. }
  433. }
  434. -
  435. -static void dropCallback(GLFWwindow* win, int count, const char** paths) {
  436. - contextSet((Context*) glfwGetWindowUserPointer(win));
  437. - std::vector<std::string> pathsVec;
  438. - for (int i = 0; i < count; i++) {
  439. - pathsVec.push_back(paths[i]);
  440. +void WindowSetPluginUI(Window* const window, CardinalBaseUI* const ui)
  441. +{
  442. + // if nanovg context failed, init only bare minimum
  443. + if (window->vg == nullptr)
  444. + {
  445. + if (ui != nullptr)
  446. + {
  447. + window->internal->ui = ui;
  448. + window->internal->size = rack::math::Vec(ui->getWidth(), ui->getHeight());
  449. + }
  450. + else
  451. + {
  452. + window->internal->ui = nullptr;
  453. + window->internal->callback = nullptr;
  454. + }
  455. + return;
  456. }
  457. - APP->event->handleDrop(APP->window->internal->lastMousePos, pathsVec);
  458. -}
  459. -
  460. -
  461. -static void errorCallback(int error, const char* description) {
  462. - WARN("GLFW error %d: %s", error, description);
  463. -}
  464. + if (ui != nullptr)
  465. + {
  466. + const GLubyte* vendor = glGetString(GL_VENDOR);
  467. + const GLubyte* renderer = glGetString(GL_RENDERER);
  468. + const GLubyte* version = glGetString(GL_VERSION);
  469. + INFO("Renderer: %s %s", vendor, renderer);
  470. + INFO("OpenGL: %s", version);
  471. +
  472. + window->internal->tlw = ui;
  473. + window->internal->ui = ui;
  474. + window->internal->size = rack::math::Vec(ui->getWidth(), ui->getHeight());
  475. +
  476. +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  477. + // Set up NanoVG
  478. + window->internal->r_vg = ui->getContext();
  479. +#ifdef NANOVG_GLES2
  480. + window->internal->r_fbVg = nvgCreateSharedGLES2(window->internal->r_vg, NVG_ANTIALIAS);
  481. +#else
  482. + window->internal->r_fbVg = nvgCreateSharedGL2(window->internal->r_vg, NVG_ANTIALIAS);
  483. +#endif
  484. -Window::Window() {
  485. - internal = new Internal;
  486. - int err;
  487. -
  488. - // Set window hints
  489. -#if defined NANOVG_GL2
  490. - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
  491. - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
  492. -#elif defined NANOVG_GL3
  493. - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  494. - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
  495. - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
  496. - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  497. -#endif
  498. - glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE);
  499. - glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
  500. -
  501. -#if defined ARCH_MAC
  502. - glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE);
  503. -#endif
  504. -
  505. - // Create window
  506. - win = glfwCreateWindow(1024, 720, "", NULL, NULL);
  507. - if (!win) {
  508. - osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Could not open GLFW window. Does your graphics card support OpenGL 2.0 or greater? If so, make sure you have the latest graphics drivers installed.");
  509. - throw Exception("Could not create Window");
  510. - }
  511. -
  512. - float contentScale;
  513. - glfwGetWindowContentScale(win, &contentScale, NULL);
  514. - INFO("Window content scale: %f", contentScale);
  515. -
  516. - glfwSetWindowSizeLimits(win, WINDOW_SIZE_MIN.x, WINDOW_SIZE_MIN.y, GLFW_DONT_CARE, GLFW_DONT_CARE);
  517. - if (settings::windowSize.x > 0 && settings::windowSize.y > 0) {
  518. - glfwSetWindowSize(win, settings::windowSize.x, settings::windowSize.y);
  519. - }
  520. - if (settings::windowPos.x > -32000 && settings::windowPos.y > -32000) {
  521. - glfwSetWindowPos(win, settings::windowPos.x, settings::windowPos.y);
  522. - }
  523. - if (settings::windowMaximized) {
  524. - glfwMaximizeWindow(win);
  525. - }
  526. - glfwShowWindow(win);
  527. -
  528. - glfwSetWindowUserPointer(win, contextGet());
  529. - glfwSetInputMode(win, GLFW_LOCK_KEY_MODS, 1);
  530. -
  531. - glfwMakeContextCurrent(win);
  532. - glfwSwapInterval(0);
  533. - const GLFWvidmode* monitorMode = glfwGetVideoMode(glfwGetPrimaryMonitor());
  534. - if (monitorMode->refreshRate > 0) {
  535. - internal->monitorRefreshRate = monitorMode->refreshRate;
  536. - }
  537. - else {
  538. - // Some monitors report 0Hz refresh rate for some reason, so as a workaround, assume 60Hz.
  539. - internal->monitorRefreshRate = 60;
  540. - }
  541. -
  542. - // Set window callbacks
  543. - glfwSetWindowPosCallback(win, windowPosCallback);
  544. - glfwSetWindowSizeCallback(win, windowSizeCallback);
  545. - glfwSetWindowMaximizeCallback(win, windowMaximizeCallback);
  546. - glfwSetMouseButtonCallback(win, mouseButtonCallback);
  547. - // Call this ourselves, but on every frame instead of only when the mouse moves
  548. - // glfwSetCursorPosCallback(win, cursorPosCallback);
  549. - glfwSetCursorEnterCallback(win, cursorEnterCallback);
  550. - glfwSetScrollCallback(win, scrollCallback);
  551. - glfwSetCharCallback(win, charCallback);
  552. - glfwSetKeyCallback(win, keyCallback);
  553. - glfwSetDropCallback(win, dropCallback);
  554. -
  555. - // Set up GLEW
  556. - glewExperimental = GL_TRUE;
  557. - err = glewInit();
  558. - if (err != GLEW_OK) {
  559. - 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.");
  560. - throw Exception("Could not initialize GLEW");
  561. - }
  562. -
  563. - const GLubyte* vendor = glGetString(GL_VENDOR);
  564. - const GLubyte* renderer = glGetString(GL_RENDERER);
  565. - const GLubyte* version = glGetString(GL_VERSION);
  566. - INFO("Renderer: %s %s", vendor, renderer);
  567. - INFO("OpenGL: %s", version);
  568. + // swap contexts
  569. + window->internal->o_vg = window->vg;
  570. + window->internal->o_fbVg = window->fbVg;
  571. + window->vg = window->internal->r_vg;
  572. + window->fbVg = window->internal->r_fbVg;
  573. +
  574. + // also for fonts and images
  575. + window->uiFont->vg = window->vg;
  576. + window->uiFont->handle = loadFallbackFont(window->vg);
  577. + for (auto& font : window->internal->fontCache)
  578. + {
  579. + font.second->vg = window->vg;
  580. + font.second->ohandle = font.second->handle;
  581. + font.second->handle = nvgCreateFont(window->vg,
  582. + font.second->ofilename.c_str(), font.second->ofilename.c_str());
  583. + }
  584. + for (auto& image : window->internal->imageCache)
  585. + {
  586. + image.second->vg = window->vg;
  587. + image.second->ohandle = image.second->handle;
  588. + image.second->handle = nvgCreateImage(window->vg, image.second->ofilename.c_str(),
  589. + NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY);
  590. + }
  591. +#endif
  592. - // GLEW generates GL error because it calls glGetString(GL_EXTENSIONS), we'll consume it here.
  593. - glGetError();
  594. + // Init settings
  595. + WindowParametersRestore(window);
  596. - // Set up NanoVG
  597. - int nvgFlags = NVG_ANTIALIAS;
  598. -#if defined NANOVG_GL2
  599. - vg = nvgCreateGL2(nvgFlags);
  600. - fbVg = nvgCreateSharedGL2(vg, nvgFlags);
  601. -#elif defined NANOVG_GL3
  602. - vg = nvgCreateGL3(nvgFlags);
  603. -#elif defined NANOVG_GLES2
  604. - vg = nvgCreateGLES2(nvgFlags);
  605. -#endif
  606. - if (!vg) {
  607. - osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Could not initialize NanoVG. Does your graphics card support OpenGL 2.0 or greater? If so, make sure you have the latest graphics drivers installed.");
  608. - throw Exception("Could not initialize NanoVG");
  609. + widget::Widget::ContextCreateEvent e = {};
  610. + e.vg = window->vg;
  611. + APP->scene->onContextCreate(e);
  612. }
  613. + else
  614. + {
  615. + widget::Widget::ContextDestroyEvent e = {};
  616. + e.vg = window->vg;
  617. + APP->scene->onContextDestroy(e);
  618. - // Load default Blendish font
  619. - uiFont = loadFont(asset::system("res/fonts/DejaVuSans.ttf"));
  620. - bndSetFont(uiFont->handle);
  621. +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  622. + // swap contexts
  623. + window->uiFont->vg = window->internal->o_vg;
  624. + window->vg = window->internal->o_vg;
  625. + window->fbVg = window->internal->o_fbVg;
  626. + window->internal->o_vg = nullptr;
  627. + window->internal->o_fbVg = nullptr;
  628. +
  629. + // also for fonts and images
  630. + window->uiFont->vg = window->vg;
  631. + window->uiFont->handle = loadFallbackFont(window->vg);
  632. + for (auto& font : window->internal->fontCache)
  633. + {
  634. + font.second->vg = window->vg;
  635. + font.second->handle = font.second->ohandle;
  636. + font.second->ohandle = -1;
  637. + }
  638. + for (auto& image : window->internal->imageCache)
  639. + {
  640. + image.second->vg = window->vg;
  641. + image.second->handle = image.second->ohandle;
  642. + image.second->ohandle = -1;
  643. + }
  644. - if (APP->scene) {
  645. - widget::Widget::ContextCreateEvent e;
  646. - e.vg = vg;
  647. - APP->scene->onContextCreate(e);
  648. +#if defined NANOVG_GLES2
  649. + nvgDeleteGLES2(window->internal->r_fbVg);
  650. +#else
  651. + nvgDeleteGL2(window->internal->r_fbVg);
  652. +#endif
  653. +#endif
  654. +
  655. + window->internal->tlw = nullptr;
  656. + window->internal->ui = nullptr;
  657. + window->internal->callback = nullptr;
  658. }
  659. }
  660. +void WindowSetMods(Window* const window, const int mods)
  661. +{
  662. + window->internal->mods = mods;
  663. +}
  664. Window::~Window() {
  665. - if (APP->scene) {
  666. - widget::Widget::ContextDestroyEvent e;
  667. - e.vg = vg;
  668. - APP->scene->onContextDestroy(e);
  669. - }
  670. + {
  671. +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  672. + DGL_NAMESPACE::Window::ScopedGraphicsContext sgc(internal->hiddenWindow);
  673. + internal->hiddenWindow.close();
  674. + internal->hiddenApp.idle();
  675. +#endif
  676. - // Fonts and Images in the cache must be deleted before the NanoVG context is deleted
  677. - internal->fontCache.clear();
  678. - internal->imageCache.clear();
  679. -
  680. - // nvgDeleteClone(fbVg);
  681. -
  682. -#if defined NANOVG_GL2
  683. - nvgDeleteGL2(vg);
  684. - nvgDeleteGL2(fbVg);
  685. -#elif defined NANOVG_GL3
  686. - nvgDeleteGL3(vg);
  687. -#elif defined NANOVG_GLES2
  688. - nvgDeleteGLES2(vg);
  689. + // Fonts and Images in the cache must be deleted before the NanoVG context is deleted
  690. + internal->fontCache.clear();
  691. + internal->imageCache.clear();
  692. +
  693. + if (vg != nullptr)
  694. + {
  695. +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  696. +#if defined NANOVG_GLES2
  697. + nvgDeleteGLES2(internal->o_fbVg != nullptr ? internal->o_fbVg : fbVg);
  698. + nvgDeleteGLES2(internal->o_vg != nullptr ? internal->o_vg : vg);
  699. +#else
  700. + nvgDeleteGL2(internal->o_fbVg != nullptr ? internal->o_fbVg : fbVg);
  701. + nvgDeleteGL2(internal->o_vg != nullptr ? internal->o_vg : vg);
  702. #endif
  703. +#else
  704. +#if defined NANOVG_GLES2
  705. + nvgDeleteGLES2(fbVg);
  706. +#else
  707. + nvgDeleteGL2(fbVg);
  708. +#endif
  709. +#endif
  710. + }
  711. + }
  712. - glfwDestroyWindow(win);
  713. delete internal;
  714. }
  715. math::Vec Window::getSize() {
  716. - int width, height;
  717. - glfwGetWindowSize(win, &width, &height);
  718. - return math::Vec(width, height);
  719. + return internal->size;
  720. }
  721. void Window::setSize(math::Vec size) {
  722. size = size.max(WINDOW_SIZE_MIN);
  723. - glfwSetWindowSize(win, size.x, size.y);
  724. + internal->size = size;
  725. +
  726. + if (DGL_NAMESPACE::NanoTopLevelWidget* const tlw = internal->ui)
  727. + tlw->setSize(internal->size.x, internal->size.y);
  728. +}
  729. +
  730. +void WindowSetInternalSize(rack::window::Window* const window, math::Vec size) {
  731. + size = size.max(WINDOW_SIZE_MIN);
  732. + window->internal->size = size;
  733. }
  734. void Window::run() {
  735. internal->frame = 0;
  736. - while (!glfwWindowShouldClose(win)) {
  737. - step();
  738. +}
  739. +
  740. +
  741. +#ifdef CARDINAL_WINDOW_CAN_GENERATE_SCREENSHOTS
  742. +static void Window__flipBitmap(uint8_t* pixels, const int width, const int height, const int depth) {
  743. + for (int y = 0; y < height / 2; y++) {
  744. + const int flipY = height - y - 1;
  745. + uint8_t tmp[width * depth];
  746. + std::memcpy(tmp, &pixels[y * width * depth], width * depth);
  747. + std::memmove(&pixels[y * width * depth], &pixels[flipY * width * depth], width * depth);
  748. + std::memcpy(&pixels[flipY * width * depth], tmp, width * depth);
  749. + }
  750. +}
  751. +
  752. +
  753. +#ifdef STBI_WRITE_NO_STDIO
  754. +static void Window__downscaleBitmap(uint8_t* pixels, int& width, int& height) {
  755. + int targetWidth = width;
  756. + int targetHeight = height;
  757. + double scale = 1.0;
  758. +
  759. + if (targetWidth > 340) {
  760. + scale = width / 340.0;
  761. + targetWidth = 340;
  762. + targetHeight = height / scale;
  763. + }
  764. + if (targetHeight > 210) {
  765. + scale = height / 210.0;
  766. + targetHeight = 210;
  767. + targetWidth = width / scale;
  768. + }
  769. + DISTRHO_SAFE_ASSERT_INT_RETURN(targetWidth <= 340, targetWidth,);
  770. + DISTRHO_SAFE_ASSERT_INT_RETURN(targetHeight <= 210, targetHeight,);
  771. +
  772. + // FIXME worst possible quality :/
  773. + for (int y = 0; y < targetHeight; ++y) {
  774. + const int ys = static_cast<int>(y * scale);
  775. + for (int x = 0; x < targetWidth; ++x) {
  776. + const int xs = static_cast<int>(x * scale);
  777. + std::memmove(pixels + (width * y + x) * 3, pixels + (width * ys + xs) * 3, 3);
  778. + }
  779. + }
  780. +
  781. + width = targetWidth;
  782. + height = targetHeight;
  783. +}
  784. +
  785. +static void Window__writeImagePNG(void* context, void* data, int size) {
  786. + USE_NAMESPACE_DISTRHO
  787. + CardinalBaseUI* const ui = static_cast<CardinalBaseUI*>(context);
  788. + if (char* const screenshot = String::asBase64(data, size).getAndReleaseBuffer()) {
  789. + ui->setState("screenshot", screenshot);
  790. + if (ui->remoteDetails != nullptr)
  791. + remoteUtils::sendScreenshotToRemote(ui->remoteDetails, screenshot);
  792. + std::free(screenshot);
  793. }
  794. }
  795. +#endif
  796. +#endif
  797. void Window::step() {
  798. + if (internal->tlw == nullptr || vg == nullptr)
  799. + return;
  800. +
  801. double frameTime = system::getTime();
  802. if (std::isfinite(internal->frameTime)) {
  803. internal->lastFrameDuration = frameTime - internal->frameTime;
  804. @@ -424,57 +622,52 @@
  805. // Make event handlers and step() have a clean NanoVG context
  806. nvgReset(vg);
  807. - bndSetFont(uiFont->handle);
  808. -
  809. - // Poll events
  810. - // Save and restore context because event handler set their own context based on which window they originate from.
  811. - Context* context = contextGet();
  812. - glfwPollEvents();
  813. - contextSet(context);
  814. -
  815. - // In case glfwPollEvents() sets another OpenGL context
  816. - glfwMakeContextCurrent(win);
  817. -
  818. - // Call cursorPosCallback every frame, not just when the mouse moves
  819. - {
  820. - double xpos, ypos;
  821. - glfwGetCursorPos(win, &xpos, &ypos);
  822. - cursorPosCallback(win, xpos, ypos);
  823. - }
  824. - gamepad::step();
  825. + if (uiFont != nullptr)
  826. + bndSetFont(uiFont->handle);
  827. // Set window title
  828. - std::string windowTitle = APP_NAME + " " + APP_EDITION_NAME + " " + APP_VERSION;
  829. - if (APP->patch->path != "") {
  830. - windowTitle += " - ";
  831. - if (!APP->history->isSaved())
  832. - windowTitle += "*";
  833. - windowTitle += system::getFilename(APP->patch->path);
  834. - }
  835. - if (windowTitle != internal->lastWindowTitle) {
  836. - glfwSetWindowTitle(win, windowTitle.c_str());
  837. - internal->lastWindowTitle = windowTitle;
  838. + if (isStandalone()) {
  839. +#if CARDINAL_VARIANT_MINI
  840. + std::string windowTitle = "Cardinal Mini";
  841. +#else
  842. + std::string windowTitle = "Cardinal";
  843. +#endif
  844. + if (APP->patch->path != "") {
  845. + windowTitle += " - ";
  846. + if (!APP->history->isSaved())
  847. + windowTitle += "*";
  848. + windowTitle += system::getFilename(APP->patch->path);
  849. + }
  850. + if (windowTitle != internal->lastWindowTitle) {
  851. + internal->tlw->getWindow().setTitle(windowTitle.c_str());
  852. + internal->lastWindowTitle = windowTitle;
  853. + }
  854. }
  855. // Get desired pixel ratio
  856. - float newPixelRatio;
  857. - if (settings::pixelRatio > 0.0) {
  858. - newPixelRatio = settings::pixelRatio;
  859. - }
  860. - else {
  861. - glfwGetWindowContentScale(win, &newPixelRatio, NULL);
  862. - newPixelRatio = std::floor(newPixelRatio + 0.5);
  863. - }
  864. + float newPixelRatio = internal->tlw->getScaleFactor();
  865. if (newPixelRatio != pixelRatio) {
  866. pixelRatio = newPixelRatio;
  867. APP->event->handleDirty();
  868. }
  869. +#ifdef CARDINAL_WINDOW_CAN_GENERATE_SCREENSHOTS
  870. + // Hide menu and background if generating screenshot
  871. + if (internal->generateScreenshotStep == kScreenshotStepStarted) {
  872. +#ifdef CARDINAL_TRANSPARENT_SCREENSHOTS
  873. + APP->scene->menuBar->hide();
  874. + APP->scene->rack->children.front()->hide();
  875. +#else
  876. + internal->generateScreenshotStep = kScreenshotStepSecondPass;
  877. +#endif
  878. + }
  879. +#endif
  880. +
  881. // Get framebuffer/window ratio
  882. - int fbWidth, fbHeight;
  883. - glfwGetFramebufferSize(win, &fbWidth, &fbHeight);
  884. - int winWidth, winHeight;
  885. - glfwGetWindowSize(win, &winWidth, &winHeight);
  886. + int winWidth = internal->tlw->getWidth();
  887. + int winHeight = internal->tlw->getHeight();
  888. + int fbWidth = winWidth;
  889. + int fbHeight = winHeight;
  890. windowRatio = (float)fbWidth / winWidth;
  891. // t1 = system::getTime();
  892. @@ -488,10 +681,8 @@
  893. // t2 = system::getTime();
  894. // Render scene
  895. - bool visible = glfwGetWindowAttrib(win, GLFW_VISIBLE) && !glfwGetWindowAttrib(win, GLFW_ICONIFIED);
  896. - if (visible) {
  897. + {
  898. // Update and render
  899. - nvgBeginFrame(vg, fbWidth, fbHeight, pixelRatio);
  900. nvgScale(vg, pixelRatio, pixelRatio);
  901. // Draw scene
  902. @@ -502,23 +693,16 @@
  903. // t3 = system::getTime();
  904. glViewport(0, 0, fbWidth, fbHeight);
  905. +#ifdef CARDINAL_TRANSPARENT_SCREENSHOTS
  906. + glClearColor(0.0, 0.0, 0.0, 0.0);
  907. +#else
  908. glClearColor(0.0, 0.0, 0.0, 1.0);
  909. +#endif
  910. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  911. - nvgEndFrame(vg);
  912. }
  913. // t4 = system::getTime();
  914. }
  915. - glfwSwapBuffers(win);
  916. -
  917. - // Limit frame rate
  918. - if (settings::frameRateLimit > 0) {
  919. - double remaining = getFrameDurationRemaining();
  920. - if (remaining > 0.0) {
  921. - system::sleep(remaining);
  922. - }
  923. - }
  924. -
  925. // t5 = system::getTime();
  926. // DEBUG("pre-step %6.1f step %6.1f draw %6.1f nvgEndFrame %6.1f glfwSwapBuffers %6.1f total %6.1f",
  927. // (t1 - frameTime) * 1e3f,
  928. @@ -528,168 +712,132 @@
  929. // (t5 - t4) * 1e3f,
  930. // (t5 - frameTime) * 1e3f
  931. // );
  932. - internal->frame++;
  933. -}
  934. + ++internal->frame;
  935. +
  936. +#ifdef CARDINAL_WINDOW_CAN_GENERATE_SCREENSHOTS
  937. + if (internal->generateScreenshotStep != kScreenshotStepNone) {
  938. + ++internal->generateScreenshotStep;
  939. +
  940. + int y = 0;
  941. +#ifdef CARDINAL_TRANSPARENT_SCREENSHOTS
  942. + constexpr const int depth = 4;
  943. +#else
  944. + y = APP->scene->menuBar->box.size.y * newPixelRatio;
  945. + constexpr const int depth = 3;
  946. +#endif
  947. + // Allocate pixel color buffer
  948. + uint8_t* const pixels = new uint8_t[winHeight * winWidth * 4];
  949. -static void flipBitmap(uint8_t* pixels, int width, int height, int depth) {
  950. - for (int y = 0; y < height / 2; y++) {
  951. - int flipY = height - y - 1;
  952. - uint8_t tmp[width * depth];
  953. - std::memcpy(tmp, &pixels[y * width * depth], width * depth);
  954. - std::memcpy(&pixels[y * width * depth], &pixels[flipY * width * depth], width * depth);
  955. - std::memcpy(&pixels[flipY * width * depth], tmp, width * depth);
  956. + // glReadPixels defaults to GL_BACK, but the back-buffer is unstable, so use the front buffer (what the user sees)
  957. + glReadBuffer(GL_FRONT);
  958. + glReadPixels(0, 0, winWidth, winHeight, depth == 3 ? GL_RGB : GL_RGBA, GL_UNSIGNED_BYTE, pixels);
  959. +
  960. + if (internal->generateScreenshotStep == kScreenshotStepSaving)
  961. + {
  962. + // Write pixels to PNG
  963. + Window__flipBitmap(pixels, winWidth, winHeight, depth);
  964. + winHeight -= y;
  965. + const int stride = winWidth * depth;
  966. + uint8_t* const pixelsWithOffset = pixels + (stride * y);
  967. +#ifdef STBI_WRITE_NO_STDIO
  968. + Window__downscaleBitmap(pixelsWithOffset, winWidth, winHeight);
  969. + stbi_write_png_to_func(Window__writeImagePNG, internal->ui,
  970. + winWidth, winHeight, depth, pixelsWithOffset, stride);
  971. +#else
  972. + stbi_write_png("screenshot.png", winWidth, winHeight, depth, pixelsWithOffset, stride);
  973. +#endif
  974. +
  975. + internal->generateScreenshotStep = kScreenshotStepNone;
  976. +#ifdef CARDINAL_TRANSPARENT_SCREENSHOTS
  977. + APP->scene->menuBar->show();
  978. + APP->scene->rack->children.front()->show();
  979. +#endif
  980. + }
  981. +
  982. + delete[] pixels;
  983. }
  984. +#endif
  985. }
  986. void Window::screenshot(const std::string& screenshotPath) {
  987. - // Get window framebuffer size
  988. - int width, height;
  989. - glfwGetFramebufferSize(APP->window->win, &width, &height);
  990. -
  991. - // Allocate pixel color buffer
  992. - uint8_t* pixels = new uint8_t[height * width * 4];
  993. -
  994. - // glReadPixels defaults to GL_BACK, but the back-buffer is unstable, so use the front buffer (what the user sees)
  995. - glReadBuffer(GL_FRONT);
  996. - glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
  997. -
  998. - // Write pixels to PNG
  999. - flipBitmap(pixels, width, height, 4);
  1000. - stbi_write_png(screenshotPath.c_str(), width, height, 4, pixels, width * 4);
  1001. -
  1002. - delete[] pixels;
  1003. }
  1004. void Window::screenshotModules(const std::string& screenshotsDir, float zoom) {
  1005. - // Disable preferDarkPanels
  1006. - bool preferDarkPanels = settings::preferDarkPanels;
  1007. - settings::preferDarkPanels = false;
  1008. - DEFER({settings::preferDarkPanels = preferDarkPanels;});
  1009. -
  1010. - // Iterate plugins and create directories
  1011. - system::createDirectories(screenshotsDir);
  1012. - for (plugin::Plugin* p : plugin::plugins) {
  1013. - std::string dir = system::join(screenshotsDir, p->slug);
  1014. - system::createDirectory(dir);
  1015. - for (plugin::Model* model : p->models) {
  1016. - std::string filename = system::join(dir, model->slug + ".png");
  1017. -
  1018. - // Skip model if screenshot already exists
  1019. - if (system::isFile(filename))
  1020. - continue;
  1021. -
  1022. - INFO("Screenshotting %s %s to %s", p->slug.c_str(), model->slug.c_str(), filename.c_str());
  1023. -
  1024. - // Create widgets
  1025. - widget::FramebufferWidget* fbw = new widget::FramebufferWidget;
  1026. - fbw->oversample = 2;
  1027. -
  1028. - struct ModuleWidgetContainer : widget::Widget {
  1029. - void draw(const DrawArgs& args) override {
  1030. - Widget::draw(args);
  1031. - Widget::drawLayer(args, 1);
  1032. - }
  1033. - };
  1034. - ModuleWidgetContainer* mwc = new ModuleWidgetContainer;
  1035. - fbw->addChild(mwc);
  1036. -
  1037. - app::ModuleWidget* mw = model->createModuleWidget(NULL);
  1038. - mwc->box.size = mw->box.size;
  1039. - fbw->box.size = mw->box.size;
  1040. - mwc->addChild(mw);
  1041. -
  1042. - // Step to allow the ModuleWidget state to set its default appearance.
  1043. - fbw->step();
  1044. -
  1045. - // Draw to framebuffer
  1046. - fbw->render(math::Vec(zoom, zoom));
  1047. -
  1048. - // Read pixels
  1049. - nvgluBindFramebuffer(fbw->getFramebuffer());
  1050. - int width, height;
  1051. - nvgImageSize(vg, fbw->getImageHandle(), &width, &height);
  1052. - uint8_t* pixels = new uint8_t[height * width * 4];
  1053. - glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
  1054. -
  1055. - // Write pixels to PNG
  1056. - flipBitmap(pixels, width, height, 4);
  1057. - stbi_write_png(filename.c_str(), width, height, 4, pixels, width * 4);
  1058. -
  1059. - // Cleanup
  1060. - delete[] pixels;
  1061. - nvgluBindFramebuffer(NULL);
  1062. - delete fbw;
  1063. - }
  1064. - }
  1065. }
  1066. void Window::close() {
  1067. - glfwSetWindowShouldClose(win, GLFW_TRUE);
  1068. + DISTRHO_SAFE_ASSERT_RETURN(internal->tlw != nullptr,);
  1069. +
  1070. + internal->tlw->getWindow().close();
  1071. }
  1072. void Window::cursorLock() {
  1073. +#ifdef DISTRHO_OS_WASM
  1074. if (!settings::allowCursorLock)
  1075. return;
  1076. -#if defined ARCH_MAC
  1077. - glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
  1078. -#else
  1079. - glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
  1080. + emscripten_request_pointerlock(internal->tlw->getWindow().getApp().getClassName(), false);
  1081. #endif
  1082. - internal->ignoreNextMouseDelta = true;
  1083. }
  1084. void Window::cursorUnlock() {
  1085. +#ifdef DISTRHO_OS_WASM
  1086. if (!settings::allowCursorLock)
  1087. return;
  1088. - glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
  1089. - internal->ignoreNextMouseDelta = true;
  1090. + emscripten_exit_pointerlock();
  1091. +#endif
  1092. }
  1093. bool Window::isCursorLocked() {
  1094. - return glfwGetInputMode(win, GLFW_CURSOR) != GLFW_CURSOR_NORMAL;
  1095. +#ifdef DISTRHO_OS_WASM
  1096. + EmscriptenPointerlockChangeEvent status;
  1097. + if (emscripten_get_pointerlock_status(&status) == EMSCRIPTEN_RESULT_SUCCESS)
  1098. + return status.isActive;
  1099. +#endif
  1100. + return false;
  1101. }
  1102. int Window::getMods() {
  1103. - int mods = 0;
  1104. - if (glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)
  1105. - mods |= GLFW_MOD_SHIFT;
  1106. - if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS)
  1107. - mods |= GLFW_MOD_CONTROL;
  1108. - if (glfwGetKey(win, GLFW_KEY_LEFT_ALT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)
  1109. - mods |= GLFW_MOD_ALT;
  1110. - if (glfwGetKey(win, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)
  1111. - mods |= GLFW_MOD_SUPER;
  1112. - return mods;
  1113. + return internal->mods;
  1114. }
  1115. void Window::setFullScreen(bool fullScreen) {
  1116. - if (!fullScreen) {
  1117. - glfwSetWindowMonitor(win, NULL, internal->lastWindowX, internal->lastWindowY, internal->lastWindowWidth, internal->lastWindowHeight, GLFW_DONT_CARE);
  1118. +#ifdef DISTRHO_OS_WASM
  1119. + if (fullScreen)
  1120. + {
  1121. + try {
  1122. + emscripten_request_fullscreen(internal->tlw->getWindow().getApp().getClassName(), false);
  1123. + } DISTRHO_SAFE_EXCEPTION("fullscreen");
  1124. }
  1125. - else {
  1126. - glfwGetWindowPos(win, &internal->lastWindowX, &internal->lastWindowY);
  1127. - glfwGetWindowSize(win, &internal->lastWindowWidth, &internal->lastWindowHeight);
  1128. - GLFWmonitor* monitor = glfwGetPrimaryMonitor();
  1129. - const GLFWvidmode* mode = glfwGetVideoMode(monitor);
  1130. - glfwSetWindowMonitor(win, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
  1131. + else
  1132. + {
  1133. + emscripten_exit_fullscreen();
  1134. }
  1135. +#endif
  1136. }
  1137. bool Window::isFullScreen() {
  1138. - GLFWmonitor* monitor = glfwGetWindowMonitor(win);
  1139. - return monitor != NULL;
  1140. +#ifdef DISTRHO_OS_WASM
  1141. + EmscriptenFullscreenChangeEvent status;
  1142. + if (emscripten_get_fullscreen_status(&status) == EMSCRIPTEN_RESULT_SUCCESS)
  1143. + return status.isFullscreen;
  1144. + return false;
  1145. +#elif defined(CARDINAL_WINDOW_CAN_GENERATE_SCREENSHOTS) && defined(CARDINAL_TRANSPARENT_SCREENSHOTS)
  1146. + return internal->generateScreenshotStep != kScreenshotStepNone;
  1147. +#else
  1148. + return false;
  1149. +#endif
  1150. }
  1151. @@ -709,7 +857,7 @@
  1152. double Window::getFrameDurationRemaining() {
  1153. - double frameDuration = 1.f / settings::frameRateLimit;
  1154. + double frameDuration = 1.f / internal->monitorRefreshRate;
  1155. return frameDuration - (system::getTime() - internal->frameTime);
  1156. }
  1157. @@ -720,14 +868,15 @@
  1158. return pair->second;
  1159. // Load font
  1160. - std::shared_ptr<Font> font;
  1161. + std::shared_ptr<FontWithOriginalContext> font;
  1162. try {
  1163. - font = std::make_shared<Font>();
  1164. + font = std::make_shared<FontWithOriginalContext>();
  1165. + font->ofilename = filename;
  1166. font->loadFile(filename, vg);
  1167. }
  1168. catch (Exception& e) {
  1169. WARN("%s", e.what());
  1170. - font = NULL;
  1171. + font = nullptr;
  1172. }
  1173. internal->fontCache[filename] = font;
  1174. return font;
  1175. @@ -740,14 +889,15 @@
  1176. return pair->second;
  1177. // Load image
  1178. - std::shared_ptr<Image> image;
  1179. + std::shared_ptr<ImageWithOriginalContext> image;
  1180. try {
  1181. - image = std::make_shared<Image>();
  1182. + image = std::make_shared<ImageWithOriginalContext>();
  1183. + image->ofilename = filename;
  1184. image->loadFile(filename, vg);
  1185. }
  1186. catch (Exception& e) {
  1187. WARN("%s", e.what());
  1188. - image = NULL;
  1189. + image = nullptr;
  1190. }
  1191. internal->imageCache[filename] = image;
  1192. return image;
  1193. @@ -764,28 +914,156 @@
  1194. }
  1195. -void init() {
  1196. - int err;
  1197. -
  1198. - // Set up GLFW
  1199. -#if defined ARCH_MAC
  1200. - glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_TRUE);
  1201. - glfwInitHint(GLFW_COCOA_MENUBAR, GLFW_FALSE);
  1202. +void generateScreenshot() {
  1203. +#ifdef CARDINAL_WINDOW_CAN_GENERATE_SCREENSHOTS
  1204. + APP->window->internal->generateScreenshotStep = kScreenshotStepStarted;
  1205. #endif
  1206. +}
  1207. - glfwSetErrorCallback(errorCallback);
  1208. - err = glfwInit();
  1209. - if (err != GLFW_TRUE) {
  1210. - osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Could not initialize GLFW.");
  1211. - throw Exception("Could not initialize GLFW");
  1212. - }
  1213. +
  1214. +void init() {
  1215. }
  1216. void destroy() {
  1217. - glfwTerminate();
  1218. }
  1219. } // namespace window
  1220. } // namespace rack
  1221. +
  1222. +
  1223. +START_NAMESPACE_DISTRHO
  1224. +
  1225. +void WindowParametersSave(rack::window::Window* const window)
  1226. +{
  1227. + if (d_isNotEqual(window->internal->params.cableOpacity, rack::settings::cableOpacity))
  1228. + {
  1229. + window->internal->params.cableOpacity = rack::settings::cableOpacity;
  1230. + if (window->internal->callback != nullptr)
  1231. + window->internal->callback->WindowParametersChanged(kWindowParameterCableOpacity,
  1232. + rack::settings::cableOpacity);
  1233. + }
  1234. + if (d_isNotEqual(window->internal->params.cableTension, rack::settings::cableTension))
  1235. + {
  1236. + window->internal->params.cableTension = rack::settings::cableTension;
  1237. + if (window->internal->callback != nullptr)
  1238. + window->internal->callback->WindowParametersChanged(kWindowParameterCableTension,
  1239. + rack::settings::cableTension);
  1240. + }
  1241. + if (d_isNotEqual(window->internal->params.rackBrightness, rack::settings::rackBrightness))
  1242. + {
  1243. + window->internal->params.rackBrightness = rack::settings::rackBrightness;
  1244. + if (window->internal->callback != nullptr)
  1245. + window->internal->callback->WindowParametersChanged(kWindowParameterRackBrightness,
  1246. + rack::settings::rackBrightness);
  1247. + }
  1248. + if (d_isNotEqual(window->internal->params.haloBrightness, rack::settings::haloBrightness))
  1249. + {
  1250. + window->internal->params.haloBrightness = rack::settings::haloBrightness;
  1251. + if (window->internal->callback != nullptr)
  1252. + window->internal->callback->WindowParametersChanged(kWindowParameterHaloBrightness,
  1253. + rack::settings::haloBrightness);
  1254. + }
  1255. + if (d_isNotEqual(window->internal->params.knobScrollSensitivity, rack::settings::knobScrollSensitivity))
  1256. + {
  1257. + window->internal->params.knobScrollSensitivity = rack::settings::knobScrollSensitivity;
  1258. + if (window->internal->callback != nullptr)
  1259. + window->internal->callback->WindowParametersChanged(kWindowParameterWheelSensitivity,
  1260. + rack::settings::knobScrollSensitivity);
  1261. + }
  1262. + if (d_isNotEqual(window->internal->params.browserZoom, rack::settings::browserZoom))
  1263. + {
  1264. + window->internal->params.browserZoom = rack::settings::browserZoom;
  1265. + if (window->internal->callback != nullptr)
  1266. + window->internal->callback->WindowParametersChanged(kWindowParameterBrowserZoom,
  1267. + rack::settings::browserZoom);
  1268. + }
  1269. + if (window->internal->params.knobMode != rack::settings::knobMode)
  1270. + {
  1271. + window->internal->params.knobMode = rack::settings::knobMode;
  1272. + if (window->internal->callback != nullptr)
  1273. + window->internal->callback->WindowParametersChanged(kWindowParameterKnobMode,
  1274. + rack::settings::knobMode);
  1275. + }
  1276. + if (window->internal->params.browserSort != rack::settings::browserSort)
  1277. + {
  1278. + window->internal->params.browserSort = rack::settings::browserSort;
  1279. + if (window->internal->callback != nullptr)
  1280. + window->internal->callback->WindowParametersChanged(kWindowParameterBrowserSort,
  1281. + rack::settings::browserSort);
  1282. + }
  1283. + if (window->internal->params.tooltips != rack::settings::tooltips)
  1284. + {
  1285. + window->internal->params.tooltips = rack::settings::tooltips;
  1286. + if (window->internal->callback != nullptr)
  1287. + window->internal->callback->WindowParametersChanged(kWindowParameterShowTooltips,
  1288. + rack::settings::tooltips);
  1289. + }
  1290. + if (window->internal->params.knobScroll != rack::settings::knobScroll)
  1291. + {
  1292. + window->internal->params.knobScroll = rack::settings::knobScroll;
  1293. + if (window->internal->callback != nullptr)
  1294. + window->internal->callback->WindowParametersChanged(kWindowParameterWheelKnobControl,
  1295. + rack::settings::knobScroll);
  1296. + }
  1297. + if (window->internal->params.lockModules != rack::settings::lockModules)
  1298. + {
  1299. + window->internal->params.lockModules = rack::settings::lockModules;
  1300. + if (window->internal->callback != nullptr)
  1301. + window->internal->callback->WindowParametersChanged(kWindowParameterLockModulePositions,
  1302. + rack::settings::lockModules);
  1303. + }
  1304. + if (window->internal->params.squeezeModules != rack::settings::squeezeModules)
  1305. + {
  1306. + window->internal->params.squeezeModules = rack::settings::squeezeModules;
  1307. + if (window->internal->callback != nullptr)
  1308. + window->internal->callback->WindowParametersChanged(kWindowParameterSqueezeModulePositions,
  1309. + rack::settings::squeezeModules);
  1310. + }
  1311. + if (window->internal->params.invertZoom != rack::settings::invertZoom)
  1312. + {
  1313. + window->internal->params.invertZoom = rack::settings::invertZoom;
  1314. + if (window->internal->callback != nullptr)
  1315. + window->internal->callback->WindowParametersChanged(kWindowParameterInvertZoom,
  1316. + rack::settings::invertZoom);
  1317. + }
  1318. + if (window->internal->params.rateLimit != rack::settings::rateLimit)
  1319. + {
  1320. + window->internal->params.rateLimit = rack::settings::rateLimit;
  1321. + if (window->internal->callback != nullptr)
  1322. + window->internal->callback->WindowParametersChanged(kWindowParameterUpdateRateLimit,
  1323. + rack::settings::rateLimit);
  1324. + }
  1325. +}
  1326. +
  1327. +void WindowParametersRestore(rack::window::Window* const window)
  1328. +{
  1329. + rack::settings::cableOpacity = window->internal->params.cableOpacity;
  1330. + rack::settings::cableTension = window->internal->params.cableTension;
  1331. + rack::settings::rackBrightness = window->internal->params.rackBrightness;
  1332. + rack::settings::haloBrightness = window->internal->params.haloBrightness;
  1333. + rack::settings::knobScrollSensitivity = window->internal->params.knobScrollSensitivity;
  1334. + rack::settings::browserZoom = window->internal->params.browserZoom;
  1335. + rack::settings::knobMode = static_cast<rack::settings::KnobMode>(window->internal->params.knobMode);
  1336. + rack::settings::browserSort = static_cast<rack::settings::BrowserSort>(window->internal->params.browserSort);
  1337. + rack::settings::tooltips = window->internal->params.tooltips;
  1338. + rack::settings::knobScroll = window->internal->params.knobScroll;
  1339. + rack::settings::lockModules = window->internal->params.lockModules;
  1340. + rack::settings::squeezeModules = window->internal->params.squeezeModules;
  1341. + rack::settings::invertZoom = window->internal->params.invertZoom;
  1342. + rack::settings::rateLimit = window->internal->params.rateLimit;
  1343. +}
  1344. +
  1345. +void WindowParametersSetCallback(rack::window::Window* const window, WindowParametersCallback* const callback)
  1346. +{
  1347. + window->internal->callback = callback;
  1348. +}
  1349. +
  1350. +void WindowParametersSetValues(rack::window::Window* const window, const WindowParameters& params)
  1351. +{
  1352. + window->internal->params = params;
  1353. +}
  1354. +
  1355. +END_NAMESPACE_DISTRHO
  1356. +