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.

506 lines
17KB

  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 <context.hpp>
  19. #include <helpers.hpp>
  20. #include <patch.hpp>
  21. #include <settings.hpp>
  22. #include <ui/Button.hpp>
  23. #include <ui/MenuItem.hpp>
  24. #include <ui/MenuSeparator.hpp>
  25. #include <window/Window.hpp>
  26. #ifdef NDEBUG
  27. # undef DEBUG
  28. #endif
  29. #include <Application.hpp>
  30. #include "DistrhoUI.hpp"
  31. #include "PluginContext.hpp"
  32. #include "WindowParameters.hpp"
  33. #include "ResizeHandle.hpp"
  34. GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window) { return nullptr; }
  35. GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char*) {}
  36. GLFWAPI const char* glfwGetKeyName(int key, int scancode) { return nullptr; }
  37. GLFWAPI int glfwGetKeyScancode(int key) { return 0; }
  38. namespace rack {
  39. namespace app {
  40. widget::Widget* createMenuBar(Window& window, bool isStandalone);
  41. }
  42. namespace window {
  43. void WindowInit(Window* window, DISTRHO_NAMESPACE::UI* ui);
  44. void WindowMods(Window* window, int mods);
  45. }
  46. }
  47. START_NAMESPACE_DISTRHO
  48. // -----------------------------------------------------------------------------------------------------------
  49. CardinalPluginContext* getRackContextFromPlugin(void* ptr);
  50. // -----------------------------------------------------------------------------------------------------------
  51. class CardinalUI : public UI,
  52. public WindowParametersCallback
  53. {
  54. CardinalPluginContext* const fContext;
  55. rack::math::Vec fLastMousePos;
  56. ResizeHandle fResizeHandle;
  57. WindowParameters fWindowParameters;
  58. struct ScopedContext {
  59. CardinalPluginContext* const context;
  60. const MutexLocker cml;
  61. ScopedContext(CardinalUI* const ui)
  62. : context(ui->fContext),
  63. cml(context->mutex)
  64. {
  65. rack::contextSet(context);
  66. WindowParametersRestore(context->window);
  67. }
  68. ScopedContext(CardinalUI* const ui, const int mods)
  69. : context(ui->fContext),
  70. cml(context->mutex)
  71. {
  72. rack::contextSet(context);
  73. rack::window::WindowMods(context->window, mods);
  74. WindowParametersRestore(context->window);
  75. }
  76. ~ScopedContext()
  77. {
  78. if (context->window != nullptr)
  79. WindowParametersSave(context->window);
  80. rack::contextSet(nullptr);
  81. }
  82. };
  83. public:
  84. CardinalUI()
  85. : UI(1228, 666),
  86. fContext(getRackContextFromPlugin(getPluginInstancePointer())),
  87. fResizeHandle(this)
  88. {
  89. if (isResizable())
  90. fResizeHandle.hide();
  91. const double scaleFactor = getScaleFactor();
  92. if (scaleFactor != 1)
  93. setSize(1228 * scaleFactor, 666 * scaleFactor);
  94. fContext->window = new rack::window::Window;
  95. {
  96. const ScopedContext sc(this);
  97. rack::window::WindowInit(fContext->window, this);
  98. fContext->scene->removeChild(fContext->scene->menuBar);
  99. fContext->scene->menuBar = rack::app::createMenuBar(getWindow(), getApp().isStandalone());
  100. fContext->scene->addChildBelow(fContext->scene->menuBar, fContext->scene->rackScroll);
  101. }
  102. WindowParametersSetCallback(fContext->window, this);
  103. }
  104. ~CardinalUI() override
  105. {
  106. const ScopedContext sc(this);
  107. delete fContext->window;
  108. fContext->window = nullptr;
  109. }
  110. void onNanoDisplay() override
  111. {
  112. const ScopedContext sc(this);
  113. fContext->window->step();
  114. }
  115. void uiIdle() override
  116. {
  117. repaint();
  118. }
  119. void WindowParametersChanged(const WindowParameterList param, float value) override
  120. {
  121. float mult = 1.0f;
  122. switch (param)
  123. {
  124. case kWindowParameterShowTooltips:
  125. fWindowParameters.tooltips = value > 0.5f;
  126. break;
  127. case kWindowParameterCableOpacity:
  128. mult = 100.0f;
  129. fWindowParameters.cableOpacity = value;
  130. break;
  131. case kWindowParameterCableTension:
  132. mult = 100.0f;
  133. fWindowParameters.cableTension = value;
  134. break;
  135. case kWindowParameterRackBrightness:
  136. mult = 100.0f;
  137. fWindowParameters.rackBrightness = value;
  138. break;
  139. case kWindowParameterHaloBrightness:
  140. mult = 100.0f;
  141. fWindowParameters.haloBrightness = value;
  142. break;
  143. case kWindowParameterKnobMode:
  144. switch (static_cast<int>(value + 0.5f))
  145. {
  146. case rack::settings::KNOB_MODE_LINEAR:
  147. value = 0;
  148. fWindowParameters.knobMode = rack::settings::KNOB_MODE_LINEAR;
  149. break;
  150. case rack::settings::KNOB_MODE_ROTARY_ABSOLUTE:
  151. value = 1;
  152. fWindowParameters.knobMode = rack::settings::KNOB_MODE_ROTARY_ABSOLUTE;
  153. break;
  154. case rack::settings::KNOB_MODE_ROTARY_RELATIVE:
  155. value = 2;
  156. fWindowParameters.knobMode = rack::settings::KNOB_MODE_ROTARY_RELATIVE;
  157. break;
  158. }
  159. break;
  160. case kWindowParameterWheelKnobControl:
  161. fWindowParameters.knobScroll = value > 0.5f;
  162. break;
  163. case kWindowParameterWheelSensitivity:
  164. mult = 1000.0f;
  165. fWindowParameters.knobScrollSensitivity = value;
  166. break;
  167. case kWindowParameterLockModulePositions:
  168. fWindowParameters.lockModules = value > 0.5f;
  169. break;
  170. default:
  171. return;
  172. }
  173. setParameterValue(kModuleParameters + param, value * mult);
  174. }
  175. protected:
  176. /* --------------------------------------------------------------------------------------------------------
  177. * DSP/Plugin Callbacks */
  178. /**
  179. A parameter has changed on the plugin side.
  180. This is called by the host to inform the UI about parameter changes.
  181. */
  182. void parameterChanged(const uint32_t index, const float value) override
  183. {
  184. if (index < kModuleParameters)
  185. return;
  186. switch (index - kModuleParameters)
  187. {
  188. case kWindowParameterShowTooltips:
  189. fWindowParameters.tooltips = value > 0.5f;
  190. break;
  191. case kWindowParameterCableOpacity:
  192. fWindowParameters.cableOpacity = value / 100.0f;
  193. break;
  194. case kWindowParameterCableTension:
  195. fWindowParameters.cableTension = value / 100.0f;
  196. break;
  197. case kWindowParameterRackBrightness:
  198. fWindowParameters.rackBrightness = value / 100.0f;
  199. break;
  200. case kWindowParameterHaloBrightness:
  201. fWindowParameters.haloBrightness = value / 100.0f;
  202. break;
  203. case kWindowParameterKnobMode:
  204. switch (static_cast<int>(value + 0.5f))
  205. {
  206. case 0:
  207. fWindowParameters.knobMode = rack::settings::KNOB_MODE_LINEAR;
  208. break;
  209. case 1:
  210. fWindowParameters.knobMode = rack::settings::KNOB_MODE_ROTARY_ABSOLUTE;
  211. break;
  212. case 2:
  213. fWindowParameters.knobMode = rack::settings::KNOB_MODE_ROTARY_RELATIVE;
  214. break;
  215. }
  216. break;
  217. case kWindowParameterWheelKnobControl:
  218. fWindowParameters.knobScroll = value > 0.5f;
  219. break;
  220. case kWindowParameterWheelSensitivity:
  221. fWindowParameters.knobScrollSensitivity = value / 1000.0f;
  222. break;
  223. case kWindowParameterLockModulePositions:
  224. fWindowParameters.lockModules = value > 0.5f;
  225. break;
  226. default:
  227. return;
  228. }
  229. WindowParametersSetValues(fContext->window, fWindowParameters);
  230. }
  231. void stateChanged(const char* key, const char* value) override
  232. {
  233. }
  234. // -------------------------------------------------------------------------------------------------------
  235. static int glfwMods(const uint mod) noexcept
  236. {
  237. int mods = 0;
  238. if (mod & kModifierControl)
  239. mods |= GLFW_MOD_CONTROL;
  240. if (mod & kModifierShift)
  241. mods |= GLFW_MOD_SHIFT;
  242. if (mod & kModifierAlt)
  243. mods |= GLFW_MOD_ALT;
  244. /*
  245. if (glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)
  246. mods |= GLFW_MOD_SHIFT;
  247. if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS)
  248. mods |= GLFW_MOD_CONTROL;
  249. if (glfwGetKey(win, GLFW_KEY_LEFT_ALT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)
  250. mods |= GLFW_MOD_ALT;
  251. if (glfwGetKey(win, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)
  252. mods |= GLFW_MOD_SUPER;
  253. */
  254. return mods;
  255. }
  256. bool onMouse(const MouseEvent& ev) override
  257. {
  258. const int action = ev.press ? GLFW_PRESS : GLFW_RELEASE;
  259. const int mods = glfwMods(ev.mod);
  260. int button;
  261. #ifdef DISTRHO_OS_MAC
  262. switch (ev.button)
  263. {
  264. case 1:
  265. button = GLFW_MOUSE_BUTTON_LEFT;
  266. break;
  267. case 2:
  268. button = GLFW_MOUSE_BUTTON_RIGHT;
  269. break;
  270. case 3:
  271. button = GLFW_MOUSE_BUTTON_MIDDLE;
  272. break;
  273. default:
  274. button = 0;
  275. break;
  276. }
  277. #else
  278. switch (ev.button)
  279. {
  280. case 1:
  281. button = GLFW_MOUSE_BUTTON_LEFT;
  282. break;
  283. case 2:
  284. button = GLFW_MOUSE_BUTTON_MIDDLE;
  285. break;
  286. case 3:
  287. button = GLFW_MOUSE_BUTTON_RIGHT;
  288. break;
  289. // case 4:
  290. // button = GLFW_MOUSE_WHEELUP;
  291. // break;
  292. // case 5:
  293. // button = GLFW_MOUSE_WHEELDOWN;
  294. // break;
  295. default:
  296. button = 0;
  297. break;
  298. }
  299. #endif
  300. /*
  301. #if defined ARCH_MAC
  302. // Remap Ctrl-left click to right click on Mac
  303. if (button == GLFW_MOUSE_BUTTON_LEFT && (mods & RACK_MOD_MASK) == GLFW_MOD_CONTROL) {
  304. button = GLFW_MOUSE_BUTTON_RIGHT;
  305. mods &= ~GLFW_MOD_CONTROL;
  306. }
  307. // Remap Ctrl-shift-left click to middle click on Mac
  308. if (button == GLFW_MOUSE_BUTTON_LEFT && (mods & RACK_MOD_MASK) == (GLFW_MOD_CONTROL | GLFW_MOD_SHIFT)) {
  309. button = GLFW_MOUSE_BUTTON_MIDDLE;
  310. mods &= ~(GLFW_MOD_CONTROL | GLFW_MOD_SHIFT);
  311. }
  312. #endif
  313. */
  314. const ScopedContext sc(this, mods);
  315. return fContext->event->handleButton(fLastMousePos, button, action, mods);
  316. }
  317. bool onMotion(const MotionEvent& ev) override
  318. {
  319. const rack::math::Vec mousePos = rack::math::Vec(ev.pos.getX(), ev.pos.getY()).div(getScaleFactor()).round();
  320. const rack::math::Vec mouseDelta = mousePos.minus(fLastMousePos);
  321. fLastMousePos = mousePos;
  322. const ScopedContext sc(this, glfwMods(ev.mod));
  323. return fContext->event->handleHover(mousePos, mouseDelta);
  324. }
  325. bool onScroll(const ScrollEvent& ev) override
  326. {
  327. rack::math::Vec scrollDelta = rack::math::Vec(ev.delta.getX(), ev.delta.getY());
  328. #ifdef DISTRHO_OS_MAC
  329. scrollDelta = scrollDelta.mult(10.0);
  330. #else
  331. scrollDelta = scrollDelta.mult(50.0);
  332. #endif
  333. const ScopedContext sc(this, glfwMods(ev.mod));
  334. return fContext->event->handleScroll(fLastMousePos, scrollDelta);
  335. }
  336. bool onCharacterInput(const CharacterInputEvent& ev) override
  337. {
  338. if (ev.character <= ' ' || ev.character >= kKeyDelete)
  339. return false;
  340. const ScopedContext sc(this, glfwMods(ev.mod));
  341. return fContext->event->handleText(fLastMousePos, ev.character);
  342. }
  343. bool onKeyboard(const KeyboardEvent& ev) override
  344. {
  345. const int action = ev.press ? GLFW_PRESS : GLFW_RELEASE;
  346. const int mods = glfwMods(ev.mod);
  347. /* These are unsupported in pugl right now
  348. #define GLFW_KEY_KP_0 320
  349. #define GLFW_KEY_KP_1 321
  350. #define GLFW_KEY_KP_2 322
  351. #define GLFW_KEY_KP_3 323
  352. #define GLFW_KEY_KP_4 324
  353. #define GLFW_KEY_KP_5 325
  354. #define GLFW_KEY_KP_6 326
  355. #define GLFW_KEY_KP_7 327
  356. #define GLFW_KEY_KP_8 328
  357. #define GLFW_KEY_KP_9 329
  358. #define GLFW_KEY_KP_DECIMAL 330
  359. #define GLFW_KEY_KP_DIVIDE 331
  360. #define GLFW_KEY_KP_MULTIPLY 332
  361. #define GLFW_KEY_KP_SUBTRACT 333
  362. #define GLFW_KEY_KP_ADD 334
  363. #define GLFW_KEY_KP_ENTER 335
  364. #define GLFW_KEY_KP_EQUAL 336
  365. */
  366. int key;
  367. switch (ev.key)
  368. {
  369. case '\r': key = GLFW_KEY_ENTER; break;
  370. case '\t': key = GLFW_KEY_TAB; break;
  371. case kKeyBackspace: key = GLFW_KEY_BACKSPACE; break;
  372. case kKeyEscape: key = GLFW_KEY_ESCAPE; break;
  373. case kKeyDelete: key = GLFW_KEY_DELETE; break;
  374. case kKeyF1: key = GLFW_KEY_F1; break;
  375. case kKeyF2: key = GLFW_KEY_F2; break;
  376. case kKeyF3: key = GLFW_KEY_F3; break;
  377. case kKeyF4: key = GLFW_KEY_F4; break;
  378. case kKeyF5: key = GLFW_KEY_F5; break;
  379. case kKeyF6: key = GLFW_KEY_F6; break;
  380. case kKeyF7: key = GLFW_KEY_F7; break;
  381. case kKeyF8: key = GLFW_KEY_F8; break;
  382. case kKeyF9: key = GLFW_KEY_F9; break;
  383. case kKeyF10: key = GLFW_KEY_F10; break;
  384. case kKeyF11: key = GLFW_KEY_F11; break;
  385. case kKeyF12: key = GLFW_KEY_F12; break;
  386. case kKeyLeft: key = GLFW_KEY_LEFT; break;
  387. case kKeyUp: key = GLFW_KEY_UP; break;
  388. case kKeyRight: key = GLFW_KEY_RIGHT; break;
  389. case kKeyDown: key = GLFW_KEY_DOWN; break;
  390. case kKeyPageUp: key = GLFW_KEY_PAGE_UP; break;
  391. case kKeyPageDown: key = GLFW_KEY_PAGE_DOWN; break;
  392. case kKeyHome: key = GLFW_KEY_HOME; break;
  393. case kKeyEnd: key = GLFW_KEY_END; break;
  394. case kKeyInsert: key = GLFW_KEY_INSERT; break;
  395. case kKeyShiftL: key = GLFW_KEY_LEFT_SHIFT; break;
  396. case kKeyShiftR: key = GLFW_KEY_RIGHT_SHIFT; break;
  397. case kKeyControlL: key = GLFW_KEY_LEFT_CONTROL; break;
  398. case kKeyControlR: key = GLFW_KEY_RIGHT_CONTROL; break;
  399. case kKeyAltL: key = GLFW_KEY_LEFT_ALT; break;
  400. case kKeyAltR: key = GLFW_KEY_RIGHT_ALT; break;
  401. case kKeySuperL: key = GLFW_KEY_LEFT_SUPER; break;
  402. case kKeySuperR: key = GLFW_KEY_RIGHT_SUPER; break;
  403. case kKeyMenu: key = GLFW_KEY_MENU; break;
  404. case kKeyCapsLock: key = GLFW_KEY_CAPS_LOCK; break;
  405. case kKeyScrollLock: key = GLFW_KEY_SCROLL_LOCK; break;
  406. case kKeyNumLock: key = GLFW_KEY_NUM_LOCK; break;
  407. case kKeyPrintScreen: key = GLFW_KEY_PRINT_SCREEN; break;
  408. case kKeyPause: key = GLFW_KEY_PAUSE; break;
  409. default: key = ev.key; break;
  410. }
  411. rack::window::WindowMods(fContext->window, mods);
  412. const ScopedContext sc(this, mods);
  413. return fContext->event->handleKey(fLastMousePos, key, ev.keycode, action, mods);
  414. }
  415. void uiFocus(const bool focus, CrossingMode) override
  416. {
  417. if (focus)
  418. return;
  419. const ScopedContext sc(this, 0);
  420. fContext->event->handleLeave();
  421. }
  422. void uiFileBrowserSelected(const char* const filename) override
  423. {
  424. if (filename == nullptr)
  425. return;
  426. const ScopedContext sc(this);
  427. fContext->patch->loadAction(filename);
  428. }
  429. private:
  430. /**
  431. Set our UI class as non-copyable and add a leak detector just in case.
  432. */
  433. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CardinalUI)
  434. };
  435. /* ------------------------------------------------------------------------------------------------------------
  436. * UI entry point, called by DPF to create a new UI instance. */
  437. UI* createUI()
  438. {
  439. return new CardinalUI();
  440. }
  441. // -----------------------------------------------------------------------------------------------------------
  442. END_NAMESPACE_DISTRHO