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.

776 lines
24KB

  1. /*
  2. * DISTRHO Cardinal Plugin
  3. * Copyright (C) 2021 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 3 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the LICENSE file.
  16. */
  17. #include <app/Scene.hpp>
  18. #include <asset.hpp>
  19. #include <context.hpp>
  20. #include <helpers.hpp>
  21. #include <patch.hpp>
  22. #include <settings.hpp>
  23. #include <string.hpp>
  24. #include <system.hpp>
  25. #include <ui/Button.hpp>
  26. #include <ui/MenuItem.hpp>
  27. #include <ui/MenuSeparator.hpp>
  28. #include <window/Window.hpp>
  29. #ifdef NDEBUG
  30. # undef DEBUG
  31. #endif
  32. #include <Application.hpp>
  33. #include "AsyncDialog.hpp"
  34. #include "PluginContext.hpp"
  35. #include "WindowParameters.hpp"
  36. #include "ResizeHandle.hpp"
  37. GLFWAPI int glfwGetKeyScancode(int) { return 0; }
  38. GLFWAPI const char* glfwGetClipboardString(GLFWwindow*)
  39. {
  40. CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP);
  41. DISTRHO_SAFE_ASSERT_RETURN(context != nullptr, nullptr);
  42. DISTRHO_SAFE_ASSERT_RETURN(context->ui != nullptr, nullptr);
  43. const char* mimeType = nullptr;
  44. size_t dataSize = 0;
  45. if (const void* const clipboard = context->ui->getClipboard(mimeType, dataSize))
  46. {
  47. if (mimeType == nullptr || std::strcmp(mimeType, "text/plain") != 0)
  48. return nullptr;
  49. return static_cast<const char*>(clipboard);
  50. }
  51. return nullptr;
  52. }
  53. GLFWAPI void glfwSetClipboardString(GLFWwindow*, const char* const text)
  54. {
  55. DISTRHO_SAFE_ASSERT_RETURN(text != nullptr,);
  56. CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP);
  57. DISTRHO_SAFE_ASSERT_RETURN(context != nullptr,);
  58. DISTRHO_SAFE_ASSERT_RETURN(context->ui != nullptr,);
  59. context->ui->setClipboard(nullptr, text, std::strlen(text)+1);
  60. }
  61. GLFWAPI double glfwGetTime(void)
  62. {
  63. CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP);
  64. DISTRHO_SAFE_ASSERT_RETURN(context != nullptr, 0.0);
  65. DISTRHO_SAFE_ASSERT_RETURN(context->ui != nullptr, 0.0);
  66. return context->ui->getApp().getTime();
  67. }
  68. GLFWAPI const char* glfwGetKeyName(const int key, int)
  69. {
  70. switch (key)
  71. {
  72. case '\"': return "\"";
  73. case '\'': return "\'";
  74. case '\\': return "\\";
  75. case ' ': return " ";
  76. case '!': return "!";
  77. case '#': return "#";
  78. case '$': return "$";
  79. case '%': return "%";
  80. case '&': return "&";
  81. case '(': return "(";
  82. case ')': return ")";
  83. case '*': return "*";
  84. case '+': return "+";
  85. case ',': return ",";
  86. case '-': return "-";
  87. case '.': return ".";
  88. case '/': return "/";
  89. case '0': return "0";
  90. case '1': return "1";
  91. case '2': return "2";
  92. case '3': return "3";
  93. case '4': return "4";
  94. case '5': return "5";
  95. case '6': return "6";
  96. case '7': return "7";
  97. case '8': return "8";
  98. case '9': return "9";
  99. case ':': return ":";
  100. case ';': return ";";
  101. case '<': return "<";
  102. case '=': return "=";
  103. case '>': return ">";
  104. case '?': return "?";
  105. case '@': return "@";
  106. case 'A': return "A";
  107. case 'B': return "B";
  108. case 'C': return "C";
  109. case 'D': return "D";
  110. case 'E': return "E";
  111. case 'F': return "F";
  112. case 'G': return "G";
  113. case 'H': return "H";
  114. case 'I': return "I";
  115. case 'J': return "J";
  116. case 'K': return "K";
  117. case 'L': return "L";
  118. case 'M': return "M";
  119. case 'N': return "N";
  120. case 'O': return "O";
  121. case 'P': return "P";
  122. case 'Q': return "Q";
  123. case 'R': return "R";
  124. case 'S': return "S";
  125. case 'T': return "T";
  126. case 'U': return "U";
  127. case 'V': return "V";
  128. case 'W': return "W";
  129. case 'X': return "X";
  130. case 'Y': return "Y";
  131. case 'Z': return "Z";
  132. case '[': return "[";
  133. case ']': return "]";
  134. case '^': return "^";
  135. case '_': return "_";
  136. case '`': return "`";
  137. case 'a': return "a";
  138. case 'b': return "b";
  139. case 'c': return "c";
  140. case 'd': return "d";
  141. case 'e': return "e";
  142. case 'f': return "f";
  143. case 'g': return "g";
  144. case 'h': return "h";
  145. case 'i': return "i";
  146. case 'j': return "j";
  147. case 'k': return "k";
  148. case 'l': return "l";
  149. case 'm': return "m";
  150. case 'n': return "n";
  151. case 'o': return "o";
  152. case 'p': return "p";
  153. case 'q': return "q";
  154. case 'r': return "r";
  155. case 's': return "s";
  156. case 't': return "t";
  157. case 'u': return "u";
  158. case 'v': return "v";
  159. case 'w': return "w";
  160. case 'x': return "x";
  161. case 'y': return "y";
  162. case 'z': return "z";
  163. default: return nullptr;
  164. }
  165. }
  166. namespace rack {
  167. namespace app {
  168. widget::Widget* createMenuBar(bool isStandalone);
  169. void hideResizeHandle(Scene* scene);
  170. }
  171. namespace window {
  172. void WindowSetPluginUI(Window* window, DISTRHO_NAMESPACE::UI* ui);
  173. void WindowSetMods(Window* window, int mods);
  174. }
  175. }
  176. START_NAMESPACE_DISTRHO
  177. // -----------------------------------------------------------------------------------------------------------
  178. bool CardinalPluginContext::addIdleCallback(IdleCallback* const cb)
  179. {
  180. if (ui == nullptr)
  181. return false;
  182. ui->addIdleCallback(cb);
  183. return true;
  184. }
  185. void CardinalPluginContext::removeIdleCallback(IdleCallback* const cb)
  186. {
  187. if (ui == nullptr)
  188. return;
  189. ui->removeIdleCallback(cb);
  190. }
  191. void handleHostParameterDrag(CardinalPluginContext* pcontext, uint index, bool started)
  192. {
  193. DISTRHO_SAFE_ASSERT_RETURN(pcontext->ui != nullptr,);
  194. if (started)
  195. {
  196. pcontext->ui->editParameter(index, true);
  197. pcontext->ui->setParameterValue(index, pcontext->parameters[index]);
  198. }
  199. else
  200. {
  201. pcontext->ui->editParameter(index, false);
  202. }
  203. }
  204. // -----------------------------------------------------------------------------------------------------------
  205. class CardinalUI : public CardinalBaseUI,
  206. public WindowParametersCallback
  207. {
  208. rack::math::Vec fLastMousePos;
  209. ResizeHandle fResizeHandle;
  210. WindowParameters fWindowParameters;
  211. struct ScopedContext {
  212. CardinalPluginContext* const context;
  213. ScopedContext(CardinalUI* const ui)
  214. : context(ui->context)
  215. {
  216. rack::contextSet(context);
  217. WindowParametersRestore(context->window);
  218. }
  219. ScopedContext(CardinalUI* const ui, const int mods)
  220. : context(ui->context)
  221. {
  222. rack::contextSet(context);
  223. rack::window::WindowSetMods(context->window, mods);
  224. WindowParametersRestore(context->window);
  225. }
  226. ~ScopedContext()
  227. {
  228. if (context->window != nullptr)
  229. WindowParametersSave(context->window);
  230. rack::contextSet(nullptr);
  231. }
  232. };
  233. public:
  234. CardinalUI()
  235. : CardinalBaseUI(1228, 666),
  236. fResizeHandle(this)
  237. {
  238. Window& window(getWindow());
  239. window.setIgnoringKeyRepeat(true);
  240. context->nativeWindowId = window.getNativeWindowHandle();
  241. if (isResizable())
  242. {
  243. fResizeHandle.hide();
  244. hideResizeHandle(context->scene);
  245. }
  246. const double scaleFactor = getScaleFactor();
  247. setGeometryConstraints(648 * scaleFactor, 538 * scaleFactor);
  248. if (scaleFactor != 1.0)
  249. setSize(1228 * scaleFactor, 666 * scaleFactor);
  250. rack::contextSet(context);
  251. rack::window::WindowSetPluginUI(context->window, this);
  252. if (context->scene->menuBar != nullptr)
  253. context->scene->removeChild(context->scene->menuBar);
  254. context->scene->menuBar = rack::app::createMenuBar(getApp().isStandalone());
  255. context->scene->addChildBelow(context->scene->menuBar, context->scene->rackScroll);
  256. // hide "Browse VCV Library" button
  257. rack::widget::Widget* const browser = context->scene->browser->children.back();
  258. rack::widget::Widget* const headerLayout = browser->children.front();
  259. rack::widget::Widget* const libraryButton = headerLayout->children.back();
  260. libraryButton->hide();
  261. // Report to user if something is wrong with the installation
  262. std::string errorMessage;
  263. if (rack::asset::systemDir.empty())
  264. {
  265. errorMessage = "Failed to locate Cardinal plugin bundle.\n"
  266. "Install Cardinal with its plugin bundle folder intact and try again.";
  267. }
  268. else if (! rack::system::exists(rack::asset::systemDir))
  269. {
  270. errorMessage = rack::string::f("System directory \"%s\" does not exist. "
  271. "Make sure Cardinal was downloaded and installed correctly.",
  272. rack::asset::systemDir.c_str());
  273. }
  274. if (! errorMessage.empty())
  275. asyncDialog::create(errorMessage.c_str());
  276. context->window->step();
  277. rack::contextSet(nullptr);
  278. WindowParametersSetCallback(context->window, this);
  279. }
  280. ~CardinalUI() override
  281. {
  282. rack::contextSet(context);
  283. context->nativeWindowId = 0;
  284. rack::widget::Widget* const menuBar = context->scene->menuBar;
  285. context->scene->menuBar = nullptr;
  286. context->scene->removeChild(menuBar);
  287. rack::window::WindowSetPluginUI(context->window, nullptr);
  288. rack::contextSet(nullptr);
  289. }
  290. void onNanoDisplay() override
  291. {
  292. const ScopedContext sc(this);
  293. context->window->step();
  294. }
  295. void uiIdle() override
  296. {
  297. if (filebrowserhandle != nullptr && fileBrowserIdle(filebrowserhandle))
  298. {
  299. {
  300. const char* const path = fileBrowserGetPath(filebrowserhandle);
  301. const ScopedContext sc(this);
  302. filebrowseraction(path != nullptr ? strdup(path) : nullptr);
  303. }
  304. fileBrowserClose(filebrowserhandle);
  305. filebrowseraction = nullptr;
  306. filebrowserhandle = nullptr;
  307. }
  308. repaint();
  309. }
  310. void WindowParametersChanged(const WindowParameterList param, float value) override
  311. {
  312. float mult = 1.0f;
  313. switch (param)
  314. {
  315. case kWindowParameterShowTooltips:
  316. fWindowParameters.tooltips = value > 0.5f;
  317. break;
  318. case kWindowParameterCableOpacity:
  319. mult = 100.0f;
  320. fWindowParameters.cableOpacity = value;
  321. break;
  322. case kWindowParameterCableTension:
  323. mult = 100.0f;
  324. fWindowParameters.cableTension = value;
  325. break;
  326. case kWindowParameterRackBrightness:
  327. mult = 100.0f;
  328. fWindowParameters.rackBrightness = value;
  329. break;
  330. case kWindowParameterHaloBrightness:
  331. mult = 100.0f;
  332. fWindowParameters.haloBrightness = value;
  333. break;
  334. case kWindowParameterKnobMode:
  335. switch (static_cast<int>(value + 0.5f))
  336. {
  337. case rack::settings::KNOB_MODE_LINEAR:
  338. value = 0;
  339. fWindowParameters.knobMode = rack::settings::KNOB_MODE_LINEAR;
  340. break;
  341. case rack::settings::KNOB_MODE_ROTARY_ABSOLUTE:
  342. value = 1;
  343. fWindowParameters.knobMode = rack::settings::KNOB_MODE_ROTARY_ABSOLUTE;
  344. break;
  345. case rack::settings::KNOB_MODE_ROTARY_RELATIVE:
  346. value = 2;
  347. fWindowParameters.knobMode = rack::settings::KNOB_MODE_ROTARY_RELATIVE;
  348. break;
  349. }
  350. break;
  351. case kWindowParameterWheelKnobControl:
  352. fWindowParameters.knobScroll = value > 0.5f;
  353. break;
  354. case kWindowParameterWheelSensitivity:
  355. mult = 1000.0f;
  356. fWindowParameters.knobScrollSensitivity = value;
  357. break;
  358. case kWindowParameterLockModulePositions:
  359. fWindowParameters.lockModules = value > 0.5f;
  360. break;
  361. default:
  362. return;
  363. }
  364. setParameterValue(kModuleParameters + param, value * mult);
  365. }
  366. protected:
  367. /* --------------------------------------------------------------------------------------------------------
  368. * DSP/Plugin Callbacks */
  369. /**
  370. A parameter has changed on the plugin side.
  371. This is called by the host to inform the UI about parameter changes.
  372. */
  373. void parameterChanged(const uint32_t index, const float value) override
  374. {
  375. if (index < kModuleParameters)
  376. return;
  377. switch (index - kModuleParameters)
  378. {
  379. case kWindowParameterShowTooltips:
  380. fWindowParameters.tooltips = value > 0.5f;
  381. break;
  382. case kWindowParameterCableOpacity:
  383. fWindowParameters.cableOpacity = value / 100.0f;
  384. break;
  385. case kWindowParameterCableTension:
  386. fWindowParameters.cableTension = value / 100.0f;
  387. break;
  388. case kWindowParameterRackBrightness:
  389. fWindowParameters.rackBrightness = value / 100.0f;
  390. break;
  391. case kWindowParameterHaloBrightness:
  392. fWindowParameters.haloBrightness = value / 100.0f;
  393. break;
  394. case kWindowParameterKnobMode:
  395. switch (static_cast<int>(value + 0.5f))
  396. {
  397. case 0:
  398. fWindowParameters.knobMode = rack::settings::KNOB_MODE_LINEAR;
  399. break;
  400. case 1:
  401. fWindowParameters.knobMode = rack::settings::KNOB_MODE_ROTARY_ABSOLUTE;
  402. break;
  403. case 2:
  404. fWindowParameters.knobMode = rack::settings::KNOB_MODE_ROTARY_RELATIVE;
  405. break;
  406. }
  407. break;
  408. case kWindowParameterWheelKnobControl:
  409. fWindowParameters.knobScroll = value > 0.5f;
  410. break;
  411. case kWindowParameterWheelSensitivity:
  412. fWindowParameters.knobScrollSensitivity = value / 1000.0f;
  413. break;
  414. case kWindowParameterLockModulePositions:
  415. fWindowParameters.lockModules = value > 0.5f;
  416. break;
  417. default:
  418. return;
  419. }
  420. WindowParametersSetValues(context->window, fWindowParameters);
  421. }
  422. void stateChanged(const char* key, const char* value) override
  423. {
  424. if (std::strcmp(key, "windowSize") != 0)
  425. return;
  426. int width = 0;
  427. int height = 0;
  428. std::sscanf(value, "%i:%i", &width, &height);
  429. if (width > 0 && height > 0)
  430. {
  431. const double scaleFactor = getScaleFactor();
  432. setSize(width * scaleFactor, height * scaleFactor);
  433. }
  434. }
  435. // -------------------------------------------------------------------------------------------------------
  436. static int glfwMods(const uint mod) noexcept
  437. {
  438. int mods = 0;
  439. if (mod & kModifierControl)
  440. mods |= GLFW_MOD_CONTROL;
  441. if (mod & kModifierShift)
  442. mods |= GLFW_MOD_SHIFT;
  443. if (mod & kModifierAlt)
  444. mods |= GLFW_MOD_ALT;
  445. if (mod & kModifierSuper)
  446. mods |= GLFW_MOD_SUPER;
  447. /*
  448. if (glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)
  449. mods |= GLFW_MOD_SHIFT;
  450. if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS)
  451. mods |= GLFW_MOD_CONTROL;
  452. if (glfwGetKey(win, GLFW_KEY_LEFT_ALT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)
  453. mods |= GLFW_MOD_ALT;
  454. if (glfwGetKey(win, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)
  455. mods |= GLFW_MOD_SUPER;
  456. */
  457. return mods;
  458. }
  459. bool onMouse(const MouseEvent& ev) override
  460. {
  461. const int action = ev.press ? GLFW_PRESS : GLFW_RELEASE;
  462. const int mods = glfwMods(ev.mod);
  463. int button;
  464. switch (ev.button)
  465. {
  466. case 1: button = GLFW_MOUSE_BUTTON_LEFT; break;
  467. #ifdef DISTRHO_OS_MAC
  468. case 2: button = GLFW_MOUSE_BUTTON_RIGHT; break;
  469. case 3: button = GLFW_MOUSE_BUTTON_MIDDLE; break;
  470. #else
  471. case 2: button = GLFW_MOUSE_BUTTON_MIDDLE; break;
  472. case 3: button = GLFW_MOUSE_BUTTON_RIGHT; break;
  473. #endif
  474. default:
  475. button = ev.button;
  476. break;
  477. }
  478. /*
  479. #if defined ARCH_MAC
  480. // Remap Ctrl-left click to right click on Mac
  481. if (button == GLFW_MOUSE_BUTTON_LEFT && (mods & RACK_MOD_MASK) == GLFW_MOD_CONTROL) {
  482. button = GLFW_MOUSE_BUTTON_RIGHT;
  483. mods &= ~GLFW_MOD_CONTROL;
  484. }
  485. // Remap Ctrl-shift-left click to middle click on Mac
  486. if (button == GLFW_MOUSE_BUTTON_LEFT && (mods & RACK_MOD_MASK) == (GLFW_MOD_CONTROL | GLFW_MOD_SHIFT)) {
  487. button = GLFW_MOUSE_BUTTON_MIDDLE;
  488. mods &= ~(GLFW_MOD_CONTROL | GLFW_MOD_SHIFT);
  489. }
  490. #endif
  491. */
  492. const ScopedContext sc(this, mods);
  493. return context->event->handleButton(fLastMousePos, button, action, mods);
  494. }
  495. bool onMotion(const MotionEvent& ev) override
  496. {
  497. const rack::math::Vec mousePos = rack::math::Vec(ev.pos.getX(), ev.pos.getY()).div(getScaleFactor()).round();
  498. const rack::math::Vec mouseDelta = mousePos.minus(fLastMousePos);
  499. fLastMousePos = mousePos;
  500. const ScopedContext sc(this, glfwMods(ev.mod));
  501. return context->event->handleHover(mousePos, mouseDelta);
  502. }
  503. bool onScroll(const ScrollEvent& ev) override
  504. {
  505. rack::math::Vec scrollDelta = rack::math::Vec(-ev.delta.getX(), ev.delta.getY());
  506. #ifdef DISTRHO_OS_MAC
  507. scrollDelta = scrollDelta.mult(10.0);
  508. #else
  509. scrollDelta = scrollDelta.mult(50.0);
  510. #endif
  511. const int mods = glfwMods(ev.mod);
  512. const ScopedContext sc(this, mods);
  513. return context->event->handleScroll(fLastMousePos, scrollDelta);
  514. }
  515. bool onCharacterInput(const CharacterInputEvent& ev) override
  516. {
  517. if (ev.character < ' ' || ev.character >= kKeyDelete)
  518. return false;
  519. const int mods = glfwMods(ev.mod);
  520. const ScopedContext sc(this, mods);
  521. return context->event->handleText(fLastMousePos, ev.character);
  522. }
  523. bool onKeyboard(const KeyboardEvent& ev) override
  524. {
  525. const int action = ev.press ? GLFW_PRESS : GLFW_RELEASE;
  526. const int mods = glfwMods(ev.mod);
  527. /* These are unsupported in pugl right now
  528. #define GLFW_KEY_KP_0 320
  529. #define GLFW_KEY_KP_1 321
  530. #define GLFW_KEY_KP_2 322
  531. #define GLFW_KEY_KP_3 323
  532. #define GLFW_KEY_KP_4 324
  533. #define GLFW_KEY_KP_5 325
  534. #define GLFW_KEY_KP_6 326
  535. #define GLFW_KEY_KP_7 327
  536. #define GLFW_KEY_KP_8 328
  537. #define GLFW_KEY_KP_9 329
  538. #define GLFW_KEY_KP_DECIMAL 330
  539. #define GLFW_KEY_KP_DIVIDE 331
  540. #define GLFW_KEY_KP_MULTIPLY 332
  541. #define GLFW_KEY_KP_SUBTRACT 333
  542. #define GLFW_KEY_KP_ADD 334
  543. #define GLFW_KEY_KP_ENTER 335
  544. #define GLFW_KEY_KP_EQUAL 336
  545. */
  546. int key;
  547. switch (ev.key)
  548. {
  549. case '\r': key = GLFW_KEY_ENTER; break;
  550. case '\t': key = GLFW_KEY_TAB; break;
  551. case kKeyBackspace: key = GLFW_KEY_BACKSPACE; break;
  552. case kKeyEscape: key = GLFW_KEY_ESCAPE; break;
  553. case kKeyDelete: key = GLFW_KEY_DELETE; break;
  554. case kKeyF1: key = GLFW_KEY_F1; break;
  555. case kKeyF2: key = GLFW_KEY_F2; break;
  556. case kKeyF3: key = GLFW_KEY_F3; break;
  557. case kKeyF4: key = GLFW_KEY_F4; break;
  558. case kKeyF5: key = GLFW_KEY_F5; break;
  559. case kKeyF6: key = GLFW_KEY_F6; break;
  560. case kKeyF7: key = GLFW_KEY_F7; break;
  561. case kKeyF8: key = GLFW_KEY_F8; break;
  562. case kKeyF9: key = GLFW_KEY_F9; break;
  563. case kKeyF10: key = GLFW_KEY_F10; break;
  564. case kKeyF11: key = GLFW_KEY_F11; break;
  565. case kKeyF12: key = GLFW_KEY_F12; break;
  566. case kKeyLeft: key = GLFW_KEY_LEFT; break;
  567. case kKeyUp: key = GLFW_KEY_UP; break;
  568. case kKeyRight: key = GLFW_KEY_RIGHT; break;
  569. case kKeyDown: key = GLFW_KEY_DOWN; break;
  570. case kKeyPageUp: key = GLFW_KEY_PAGE_UP; break;
  571. case kKeyPageDown: key = GLFW_KEY_PAGE_DOWN; break;
  572. case kKeyHome: key = GLFW_KEY_HOME; break;
  573. case kKeyEnd: key = GLFW_KEY_END; break;
  574. case kKeyInsert: key = GLFW_KEY_INSERT; break;
  575. case kKeyShiftL: key = GLFW_KEY_LEFT_SHIFT; break;
  576. case kKeyShiftR: key = GLFW_KEY_RIGHT_SHIFT; break;
  577. case kKeyControlL: key = GLFW_KEY_LEFT_CONTROL; break;
  578. case kKeyControlR: key = GLFW_KEY_RIGHT_CONTROL; break;
  579. case kKeyAltL: key = GLFW_KEY_LEFT_ALT; break;
  580. case kKeyAltR: key = GLFW_KEY_RIGHT_ALT; break;
  581. case kKeySuperL: key = GLFW_KEY_LEFT_SUPER; break;
  582. case kKeySuperR: key = GLFW_KEY_RIGHT_SUPER; break;
  583. case kKeyMenu: key = GLFW_KEY_MENU; break;
  584. case kKeyCapsLock: key = GLFW_KEY_CAPS_LOCK; break;
  585. case kKeyScrollLock: key = GLFW_KEY_SCROLL_LOCK; break;
  586. case kKeyNumLock: key = GLFW_KEY_NUM_LOCK; break;
  587. case kKeyPrintScreen: key = GLFW_KEY_PRINT_SCREEN; break;
  588. case kKeyPause: key = GLFW_KEY_PAUSE; break;
  589. default: key = ev.key; break;
  590. }
  591. const ScopedContext sc(this, mods);
  592. return context->event->handleKey(fLastMousePos, key, ev.keycode, action, mods);
  593. }
  594. void onResize(const ResizeEvent& ev) override
  595. {
  596. UI::onResize(ev);
  597. if (context->window != nullptr)
  598. context->window->setSize(rack::math::Vec(ev.size.getWidth(), ev.size.getHeight()));
  599. const double scaleFactor = getScaleFactor();
  600. char sizeString[64];
  601. std::snprintf(sizeString, sizeof(sizeString), "%d:%d",
  602. (int)(ev.size.getWidth() / scaleFactor), (int)(ev.size.getHeight() / scaleFactor));
  603. setState("windowSize", sizeString);
  604. }
  605. void uiFocus(const bool focus, CrossingMode) override
  606. {
  607. if (focus)
  608. return;
  609. const ScopedContext sc(this, 0);
  610. context->event->handleLeave();
  611. }
  612. void uiFileBrowserSelected(const char* const filename) override
  613. {
  614. if (filename == nullptr)
  615. return;
  616. rack::contextSet(context);
  617. WindowParametersRestore(context->window);
  618. std::string sfilename = filename;
  619. if (saving)
  620. {
  621. if (rack::system::getExtension(sfilename) != ".vcv")
  622. sfilename += ".vcv";
  623. try {
  624. context->patch->save(sfilename);
  625. }
  626. catch (rack::Exception& e) {
  627. std::string message = rack::string::f("Could not save patch: %s", e.what());
  628. asyncDialog::create(message.c_str());
  629. return;
  630. }
  631. }
  632. else
  633. {
  634. try {
  635. context->patch->load(sfilename);
  636. } catch (rack::Exception& e) {
  637. std::string message = rack::string::f("Could not load patch: %s", e.what());
  638. asyncDialog::create(message.c_str());
  639. return;
  640. }
  641. }
  642. context->patch->path = sfilename;
  643. context->history->setSaved();
  644. }
  645. #if 0
  646. void uiReshape(const uint width, const uint height) override
  647. {
  648. glEnable(GL_BLEND);
  649. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  650. glMatrixMode(GL_PROJECTION);
  651. glLoadIdentity();
  652. glOrtho(0.0, width, 0.0, height, -1.0, 1.0);
  653. glViewport(0, 0, width, height);
  654. glMatrixMode(GL_MODELVIEW);
  655. glLoadIdentity();
  656. }
  657. #endif
  658. private:
  659. /**
  660. Set our UI class as non-copyable and add a leak detector just in case.
  661. */
  662. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CardinalUI)
  663. };
  664. /* ------------------------------------------------------------------------------------------------------------
  665. * UI entry point, called by DPF to create a new UI instance. */
  666. UI* createUI()
  667. {
  668. return new CardinalUI();
  669. }
  670. // -----------------------------------------------------------------------------------------------------------
  671. END_NAMESPACE_DISTRHO