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.

356 lines
11KB

  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/common.hpp>
  18. #include <app/Scene.hpp>
  19. #include <context.hpp>
  20. #include <patch.hpp>
  21. #include <ui/common.hpp>
  22. #include <window/Window.hpp>
  23. #include "PluginContext.hpp"
  24. #include "DistrhoUI.hpp"
  25. #include "ResizeHandle.hpp"
  26. GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window) { return nullptr; }
  27. GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char*) {}
  28. GLFWAPI const char* glfwGetKeyName(int key, int scancode) { return nullptr; }
  29. GLFWAPI int glfwGetKeyScancode(int key) { return 0; }
  30. namespace rack {
  31. namespace window {
  32. DISTRHO_NAMESPACE::UI* lastUI = nullptr;
  33. }
  34. }
  35. START_NAMESPACE_DISTRHO
  36. // -----------------------------------------------------------------------------------------------------------
  37. CardinalPluginContext* getRackContextFromPlugin(void* ptr);
  38. class CardinalUI : public UI
  39. {
  40. CardinalPluginContext* const fContext;
  41. rack::math::Vec fLastMousePos;
  42. ResizeHandle fResizeHandle;
  43. struct ScopedContext {
  44. ScopedContext(CardinalUI* const ui)
  45. {
  46. rack::contextSet(ui->fContext);
  47. }
  48. ~ScopedContext()
  49. {
  50. rack::contextSet(nullptr);
  51. }
  52. };
  53. public:
  54. CardinalUI()
  55. : UI(1280, 720),
  56. fContext(getRackContextFromPlugin(getPluginInstancePointer())),
  57. fResizeHandle(this)
  58. {
  59. const ScopedContext sc(this);
  60. fContext->event = new rack::widget::EventState;
  61. fContext->scene = new rack::app::Scene;
  62. fContext->event->rootWidget = fContext->scene;
  63. // Initialize context
  64. d_stdout("UI context ptr %p", NanoVG::getContext());
  65. rack::window::lastUI = this;
  66. fContext->window = new rack::window::Window;
  67. rack::window::lastUI = nullptr;
  68. // we need to reload current patch for things to show on screen :(
  69. // FIXME always save
  70. if (! fContext->patch->hasAutosave())
  71. fContext->patch->saveAutosave();
  72. fContext->patch->loadAutosave();
  73. }
  74. ~CardinalUI() override
  75. {
  76. const ScopedContext sc(this);
  77. delete fContext->window;
  78. fContext->window = nullptr;
  79. delete fContext->scene;
  80. fContext->scene = nullptr;
  81. delete fContext->event;
  82. fContext->event = nullptr;
  83. }
  84. void onNanoDisplay() override
  85. {
  86. const ScopedContext sc(this);
  87. fContext->window->step();
  88. }
  89. void uiIdle() override
  90. {
  91. repaint();
  92. }
  93. protected:
  94. /* --------------------------------------------------------------------------------------------------------
  95. * DSP/Plugin Callbacks */
  96. /**
  97. A parameter has changed on the plugin side.
  98. This is called by the host to inform the UI about parameter changes.
  99. */
  100. void parameterChanged(uint32_t index, float value) override
  101. {
  102. }
  103. // -------------------------------------------------------------------------------------------------------
  104. bool onMouse(const MouseEvent& ev) override
  105. {
  106. const ScopedContext sc(this);
  107. int button;
  108. int mods = 0;
  109. int action = ev.press ? GLFW_PRESS : GLFW_RELEASE;
  110. if (ev.mod & kModifierControl)
  111. mods |= GLFW_MOD_CONTROL;
  112. if (ev.mod & kModifierShift)
  113. mods |= GLFW_MOD_SHIFT;
  114. if (ev.mod & kModifierAlt)
  115. mods |= GLFW_MOD_ALT;
  116. #ifdef DISTRHO_OS_MAC
  117. switch (ev.button)
  118. {
  119. case 1:
  120. button = GLFW_MOUSE_BUTTON_LEFT;
  121. break;
  122. case 2:
  123. button = GLFW_MOUSE_BUTTON_RIGHT;
  124. break;
  125. case 3:
  126. button = GLFW_MOUSE_BUTTON_MIDDLE;
  127. break;
  128. default:
  129. button = 0;
  130. break;
  131. }
  132. #else
  133. switch (ev.button)
  134. {
  135. case 1:
  136. button = GLFW_MOUSE_BUTTON_LEFT;
  137. break;
  138. case 2:
  139. button = GLFW_MOUSE_BUTTON_MIDDLE;
  140. break;
  141. case 3:
  142. button = GLFW_MOUSE_BUTTON_RIGHT;
  143. break;
  144. // case 4:
  145. // button = GLFW_MOUSE_WHEELUP;
  146. // break;
  147. // case 5:
  148. // button = GLFW_MOUSE_WHEELDOWN;
  149. // break;
  150. default:
  151. button = 0;
  152. break;
  153. }
  154. #endif
  155. /*
  156. #if defined ARCH_MAC
  157. // Remap Ctrl-left click to right click on Mac
  158. if (button == GLFW_MOUSE_BUTTON_LEFT && (mods & RACK_MOD_MASK) == GLFW_MOD_CONTROL) {
  159. button = GLFW_MOUSE_BUTTON_RIGHT;
  160. mods &= ~GLFW_MOD_CONTROL;
  161. }
  162. // Remap Ctrl-shift-left click to middle click on Mac
  163. if (button == GLFW_MOUSE_BUTTON_LEFT && (mods & RACK_MOD_MASK) == (GLFW_MOD_CONTROL | GLFW_MOD_SHIFT)) {
  164. button = GLFW_MOUSE_BUTTON_MIDDLE;
  165. mods &= ~(GLFW_MOD_CONTROL | GLFW_MOD_SHIFT);
  166. }
  167. #endif
  168. */
  169. return fContext->event->handleButton(fLastMousePos, button, action, mods);
  170. }
  171. bool onMotion(const MotionEvent& ev) override
  172. {
  173. const ScopedContext sc(this);
  174. rack::math::Vec mousePos = rack::math::Vec(ev.pos.getX(), ev.pos.getY()).div(1).round();
  175. // .div(ctx->window->pixelRatio / ctx->window->windowRatio).round();
  176. rack::math::Vec mouseDelta = mousePos.minus(fLastMousePos);
  177. /*
  178. // Workaround for GLFW warping mouse to a different position when the cursor is locked or unlocked.
  179. if (ctx->window->internal->ignoreNextMouseDelta)
  180. {
  181. ctx->window->internal->ignoreNextMouseDelta = false;
  182. mouseDelta = rack::math::Vec();
  183. }
  184. */
  185. fLastMousePos = mousePos;
  186. return fContext->event->handleHover(mousePos, mouseDelta);
  187. }
  188. bool onScroll(const ScrollEvent& ev) override
  189. {
  190. const ScopedContext sc(this);
  191. rack::math::Vec scrollDelta = rack::math::Vec(ev.delta.getX(), ev.delta.getY());
  192. #ifdef DISTRHO_OS_MAC
  193. scrollDelta = scrollDelta.mult(10.0);
  194. #else
  195. scrollDelta = scrollDelta.mult(50.0);
  196. #endif
  197. return fContext->event->handleScroll(fLastMousePos, scrollDelta);
  198. }
  199. bool onCharacterInput(const CharacterInputEvent& ev) override
  200. {
  201. if (ev.character == 0)
  202. return false;
  203. const ScopedContext sc(this);
  204. return fContext->event->handleText(fLastMousePos, ev.character);
  205. }
  206. bool onKeyboard(const KeyboardEvent& ev) override
  207. {
  208. const ScopedContext sc(this);
  209. int key;
  210. int mods = 0;
  211. int action = ev.press ? GLFW_PRESS : GLFW_RELEASE;
  212. /* These are unsupported in pugl right now
  213. #define GLFW_KEY_KP_0 320
  214. #define GLFW_KEY_KP_1 321
  215. #define GLFW_KEY_KP_2 322
  216. #define GLFW_KEY_KP_3 323
  217. #define GLFW_KEY_KP_4 324
  218. #define GLFW_KEY_KP_5 325
  219. #define GLFW_KEY_KP_6 326
  220. #define GLFW_KEY_KP_7 327
  221. #define GLFW_KEY_KP_8 328
  222. #define GLFW_KEY_KP_9 329
  223. #define GLFW_KEY_KP_DECIMAL 330
  224. #define GLFW_KEY_KP_DIVIDE 331
  225. #define GLFW_KEY_KP_MULTIPLY 332
  226. #define GLFW_KEY_KP_SUBTRACT 333
  227. #define GLFW_KEY_KP_ADD 334
  228. #define GLFW_KEY_KP_ENTER 335
  229. #define GLFW_KEY_KP_EQUAL 336
  230. */
  231. switch (ev.key)
  232. {
  233. case '\r': key = GLFW_KEY_ENTER; break;
  234. case '\t': key = GLFW_KEY_TAB; break;
  235. case kKeyBackspace: key = GLFW_KEY_BACKSPACE; break;
  236. case kKeyEscape: key = GLFW_KEY_ESCAPE; break;
  237. case kKeyDelete: key = GLFW_KEY_DELETE; break;
  238. case kKeyF1: key = GLFW_KEY_F1; break;
  239. case kKeyF2: key = GLFW_KEY_F2; break;
  240. case kKeyF3: key = GLFW_KEY_F3; break;
  241. case kKeyF4: key = GLFW_KEY_F4; break;
  242. case kKeyF5: key = GLFW_KEY_F5; break;
  243. case kKeyF6: key = GLFW_KEY_F6; break;
  244. case kKeyF7: key = GLFW_KEY_F7; break;
  245. case kKeyF8: key = GLFW_KEY_F8; break;
  246. case kKeyF9: key = GLFW_KEY_F9; break;
  247. case kKeyF10: key = GLFW_KEY_F10; break;
  248. case kKeyF11: key = GLFW_KEY_F11; break;
  249. case kKeyF12: key = GLFW_KEY_F12; break;
  250. case kKeyLeft: key = GLFW_KEY_LEFT; break;
  251. case kKeyUp: key = GLFW_KEY_UP; break;
  252. case kKeyRight: key = GLFW_KEY_RIGHT; break;
  253. case kKeyDown: key = GLFW_KEY_DOWN; break;
  254. case kKeyPageUp: key = GLFW_KEY_PAGE_UP; break;
  255. case kKeyPageDown: key = GLFW_KEY_PAGE_DOWN; break;
  256. case kKeyHome: key = GLFW_KEY_HOME; break;
  257. case kKeyEnd: key = GLFW_KEY_END; break;
  258. case kKeyInsert: key = GLFW_KEY_INSERT; break;
  259. case kKeyShiftL: key = GLFW_KEY_LEFT_SHIFT; break;
  260. case kKeyShiftR: key = GLFW_KEY_RIGHT_SHIFT; break;
  261. case kKeyControlL: key = GLFW_KEY_LEFT_CONTROL; break;
  262. case kKeyControlR: key = GLFW_KEY_RIGHT_CONTROL; break;
  263. case kKeyAltL: key = GLFW_KEY_LEFT_ALT; break;
  264. case kKeyAltR: key = GLFW_KEY_RIGHT_ALT; break;
  265. case kKeySuperL: key = GLFW_KEY_LEFT_SUPER; break;
  266. case kKeySuperR: key = GLFW_KEY_RIGHT_SUPER; break;
  267. case kKeyMenu: key = GLFW_KEY_MENU; break;
  268. case kKeyCapsLock: key = GLFW_KEY_CAPS_LOCK; break;
  269. case kKeyScrollLock: key = GLFW_KEY_SCROLL_LOCK; break;
  270. case kKeyNumLock: key = GLFW_KEY_NUM_LOCK; break;
  271. case kKeyPrintScreen: key = GLFW_KEY_PRINT_SCREEN; break;
  272. case kKeyPause: key = GLFW_KEY_PAUSE; break;
  273. default: key = ev.key; break;
  274. }
  275. if (ev.mod & kModifierControl)
  276. mods |= GLFW_MOD_CONTROL;
  277. if (ev.mod & kModifierShift)
  278. mods |= GLFW_MOD_SHIFT;
  279. if (ev.mod & kModifierAlt)
  280. mods |= GLFW_MOD_ALT;
  281. return fContext->event->handleKey(fLastMousePos, key, ev.keycode, action, mods);
  282. }
  283. void uiFocus(const bool focus, CrossingMode) override
  284. {
  285. const ScopedContext sc(this);
  286. if (! focus)
  287. fContext->event->handleLeave();
  288. }
  289. private:
  290. /**
  291. Set our UI class as non-copyable and add a leak detector just in case.
  292. */
  293. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CardinalUI)
  294. };
  295. /* ------------------------------------------------------------------------------------------------------------
  296. * UI entry point, called by DPF to create a new UI instance. */
  297. UI* createUI()
  298. {
  299. return new CardinalUI();
  300. }
  301. // -----------------------------------------------------------------------------------------------------------
  302. END_NAMESPACE_DISTRHO