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.

547 lines
14KB

  1. /*
  2. * DISTRHO Cardinal Plugin
  3. * Copyright (C) 2021-2022 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. /**
  18. * This file is an edited version of VCVRack's app/Scene.cpp
  19. * Copyright (C) 2016-2021 VCV.
  20. *
  21. * This program is free software: you can redistribute it and/or
  22. * modify it under the terms of the GNU General Public License as
  23. * published by the Free Software Foundation; either version 3 of
  24. * the License, or (at your option) any later version.
  25. */
  26. #include <thread>
  27. #include <osdialog.h>
  28. #include <app/Scene.hpp>
  29. #include <app/Browser.hpp>
  30. #include <app/TipWindow.hpp>
  31. #include <app/MenuBar.hpp>
  32. #include <context.hpp>
  33. #include <engine/Engine.hpp>
  34. #include <system.hpp>
  35. #include <network.hpp>
  36. #include <history.hpp>
  37. #include <settings.hpp>
  38. #include <patch.hpp>
  39. #include <asset.hpp>
  40. #ifdef NDEBUG
  41. # undef DEBUG
  42. #endif
  43. #ifdef HAVE_LIBLO
  44. # include <lo/lo.h>
  45. #endif
  46. #include "../CardinalCommon.hpp"
  47. #include "DistrhoUtils.hpp"
  48. namespace rack {
  49. namespace app {
  50. struct ResizeHandle : widget::OpaqueWidget {
  51. math::Vec size;
  52. void draw(const DrawArgs& args) override {
  53. nvgStrokeColor(args.vg, nvgRGBf(1, 1, 1));
  54. nvgStrokeWidth(args.vg, 1);
  55. nvgBeginPath(args.vg);
  56. nvgMoveTo(args.vg, box.size.x, 0);
  57. nvgLineTo(args.vg, 0, box.size.y);
  58. nvgStroke(args.vg);
  59. nvgBeginPath(args.vg);
  60. nvgMoveTo(args.vg, box.size.x + 5, 0);
  61. nvgLineTo(args.vg, 0, box.size.y + 5);
  62. nvgStroke(args.vg);
  63. nvgBeginPath(args.vg);
  64. nvgMoveTo(args.vg, box.size.x + 10, 0);
  65. nvgLineTo(args.vg, 0, box.size.y + 10);
  66. nvgStroke(args.vg);
  67. nvgStrokeColor(args.vg, nvgRGBf(0, 0, 0));
  68. nvgBeginPath(args.vg);
  69. nvgMoveTo(args.vg, box.size.x+1, 0);
  70. nvgLineTo(args.vg, 0, box.size.y+1);
  71. nvgStroke(args.vg);
  72. nvgBeginPath(args.vg);
  73. nvgMoveTo(args.vg, box.size.x + 6, 0);
  74. nvgLineTo(args.vg, 0, box.size.y + 6);
  75. nvgStroke(args.vg);
  76. nvgBeginPath(args.vg);
  77. nvgMoveTo(args.vg, box.size.x + 11, 0);
  78. nvgLineTo(args.vg, 0, box.size.y + 11);
  79. nvgStroke(args.vg);
  80. }
  81. void onHover(const HoverEvent& e) override {
  82. e.consume(this);
  83. }
  84. void onEnter(const EnterEvent& e) override {
  85. glfwSetCursor(nullptr, (GLFWcursor*)0x1);
  86. }
  87. void onLeave(const LeaveEvent& e) override {
  88. glfwSetCursor(nullptr, nullptr);
  89. }
  90. void onDragStart(const DragStartEvent&) override {
  91. size = APP->window->getSize();
  92. }
  93. void onDragMove(const DragMoveEvent& e) override {
  94. size = size.plus(e.mouseDelta);
  95. APP->window->setSize(size.round());
  96. }
  97. };
  98. struct Scene::Internal {
  99. ResizeHandle* resizeHandle;
  100. bool heldArrowKeys[4] = {};
  101. #ifdef HAVE_LIBLO
  102. double lastSceneChangeTime = 0.0;
  103. int historyActionIndex = -1;
  104. bool oscAutoDeploy = false;
  105. bool oscConnected = false;
  106. lo_server oscServer = nullptr;
  107. static int osc_handler(const char* const path, const char* const types, lo_arg** argv, const int argc, lo_message, void* const self)
  108. {
  109. d_stdout("osc_handler(\"%s\", \"%s\", %p, %i)", path, types, argv, argc);
  110. if (std::strcmp(path, "/resp") == 0 && argc == 2 && types[0] == 's' && types[1] == 's') {
  111. d_stdout("osc_handler(\"%s\", ...) - got resp | '%s' '%s'", path, &argv[0]->s, &argv[1]->s);
  112. if (std::strcmp(&argv[0]->s, "hello") == 0 && std::strcmp(&argv[1]->s, "ok") == 0)
  113. static_cast<Internal*>(self)->oscConnected = true;
  114. }
  115. return 0;
  116. }
  117. ~Internal() {
  118. lo_server_free(oscServer);
  119. }
  120. #endif
  121. };
  122. Scene::Scene() {
  123. internal = new Internal;
  124. rackScroll = new RackScrollWidget;
  125. addChild(rackScroll);
  126. rack = rackScroll->rackWidget;
  127. menuBar = createMenuBar();
  128. addChild(menuBar);
  129. browser = browserCreate();
  130. browser->hide();
  131. addChild(browser);
  132. internal->resizeHandle = new ResizeHandle;
  133. internal->resizeHandle->box.size = math::Vec(16, 16);
  134. addChild(internal->resizeHandle);
  135. }
  136. Scene::~Scene() {
  137. delete internal;
  138. }
  139. math::Vec Scene::getMousePos() {
  140. return mousePos;
  141. }
  142. void Scene::step() {
  143. if (APP->window->isFullScreen()) {
  144. // Expand RackScrollWidget to cover entire screen if fullscreen
  145. rackScroll->box.pos.y = 0;
  146. }
  147. else {
  148. // Always show MenuBar if not fullscreen
  149. menuBar->show();
  150. rackScroll->box.pos.y = menuBar->box.size.y;
  151. }
  152. internal->resizeHandle->box.pos = box.size.minus(internal->resizeHandle->box.size);
  153. // Resize owned descendants
  154. menuBar->box.size.x = box.size.x;
  155. rackScroll->box.size = box.size.minus(rackScroll->box.pos);
  156. // Scroll RackScrollWidget with arrow keys
  157. math::Vec arrowDelta;
  158. if (internal->heldArrowKeys[0]) {
  159. arrowDelta.x -= 1;
  160. }
  161. if (internal->heldArrowKeys[1]) {
  162. arrowDelta.x += 1;
  163. }
  164. if (internal->heldArrowKeys[2]) {
  165. arrowDelta.y -= 1;
  166. }
  167. if (internal->heldArrowKeys[3]) {
  168. arrowDelta.y += 1;
  169. }
  170. if (!arrowDelta.isZero()) {
  171. int mods = APP->window->getMods();
  172. float arrowSpeed = 32.f;
  173. if ((mods & RACK_MOD_MASK) == RACK_MOD_CTRL)
  174. arrowSpeed /= 4.f;
  175. if ((mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT)
  176. arrowSpeed *= 4.f;
  177. if ((mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT))
  178. arrowSpeed /= 16.f;
  179. rackScroll->offset += arrowDelta * arrowSpeed;
  180. }
  181. #ifdef HAVE_LIBLO
  182. if (internal->oscServer != nullptr) {
  183. while (lo_server_recv_noblock(internal->oscServer, 0) != 0) {}
  184. if (internal->oscAutoDeploy) {
  185. const int actionIndex = APP->history->actionIndex;
  186. const double time = system::getTime();
  187. if (internal->historyActionIndex != actionIndex && time - internal->lastSceneChangeTime >= 5.0) {
  188. internal->historyActionIndex = actionIndex;
  189. internal->lastSceneChangeTime = time;
  190. patchUtils::deployToRemote();
  191. }
  192. }
  193. }
  194. #endif
  195. Widget::step();
  196. }
  197. void Scene::draw(const DrawArgs& args) {
  198. Widget::draw(args);
  199. }
  200. void Scene::onHover(const HoverEvent& e) {
  201. mousePos = e.pos;
  202. if (mousePos.y < menuBar->box.size.y) {
  203. menuBar->show();
  204. }
  205. OpaqueWidget::onHover(e);
  206. }
  207. void Scene::onDragHover(const DragHoverEvent& e) {
  208. mousePos = e.pos;
  209. OpaqueWidget::onDragHover(e);
  210. }
  211. void Scene::onHoverKey(const HoverKeyEvent& e) {
  212. // Key commands that override children
  213. if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
  214. // DEBUG("key '%d '%c' scancode %d '%c' keyName '%s'", e.key, e.key, e.scancode, e.scancode, e.keyName.c_str());
  215. if (e.keyName == "n" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  216. patchUtils::loadTemplateDialog();
  217. e.consume(this);
  218. }
  219. if (e.keyName == "q" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  220. APP->window->close();
  221. e.consume(this);
  222. }
  223. if (e.keyName == "o" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  224. patchUtils::loadDialog();
  225. e.consume(this);
  226. }
  227. if (e.keyName == "o" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  228. patchUtils::revertDialog();
  229. e.consume(this);
  230. }
  231. if (e.keyName == "s" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  232. // NOTE: will do nothing if path is empty, intentionally
  233. patchUtils::saveDialog(APP->patch->path);
  234. e.consume(this);
  235. }
  236. if (e.keyName == "s" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  237. patchUtils::saveAsDialog();
  238. e.consume(this);
  239. }
  240. if (e.keyName == "z" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  241. APP->history->undo();
  242. e.consume(this);
  243. }
  244. if (e.keyName == "z" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  245. APP->history->redo();
  246. e.consume(this);
  247. }
  248. if (e.keyName == "-" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  249. float zoom = std::log2(APP->scene->rackScroll->getZoom());
  250. zoom *= 2;
  251. zoom = std::ceil(zoom - 0.01f) - 1;
  252. zoom /= 2;
  253. APP->scene->rackScroll->setZoom(std::pow(2.f, zoom));
  254. e.consume(this);
  255. }
  256. // Numpad has a "+" key, but the main keyboard section hides it under "="
  257. if ((e.keyName == "=" || e.keyName == "+") && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  258. float zoom = std::log2(APP->scene->rackScroll->getZoom());
  259. zoom *= 2;
  260. zoom = std::floor(zoom + 0.01f) + 1;
  261. zoom /= 2;
  262. APP->scene->rackScroll->setZoom(std::pow(2.f, zoom));
  263. e.consume(this);
  264. }
  265. if ((e.keyName == "0" || e.keyName == "1") && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  266. APP->scene->rackScroll->setZoom(1.f);
  267. e.consume(this);
  268. }
  269. if (e.keyName == "2" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  270. APP->scene->rackScroll->setZoom(2.f);
  271. e.consume(this);
  272. }
  273. if (e.key == GLFW_KEY_F1 && (e.mods & RACK_MOD_MASK) == 0) {
  274. system::openBrowser("https://vcvrack.com/manual/");
  275. e.consume(this);
  276. }
  277. if (e.key == GLFW_KEY_F3 && (e.mods & RACK_MOD_MASK) == 0) {
  278. settings::cpuMeter ^= true;
  279. e.consume(this);
  280. }
  281. if (e.key == GLFW_KEY_F7 && (e.mods & RACK_MOD_MASK) == 0) {
  282. patchUtils::deployToRemote();
  283. e.consume(this);
  284. }
  285. if (e.key == GLFW_KEY_F9 && (e.mods & RACK_MOD_MASK) == 0) {
  286. window::generateScreenshot();
  287. e.consume(this);
  288. }
  289. // Module selections
  290. if (e.keyName == "a" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  291. rack->selectAll();
  292. e.consume(this);
  293. }
  294. if (e.keyName == "a" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  295. rack->deselectAll();
  296. e.consume(this);
  297. }
  298. if (e.keyName == "c" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  299. if (rack->hasSelection()) {
  300. rack->copyClipboardSelection();
  301. e.consume(this);
  302. }
  303. }
  304. if (e.keyName == "i" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  305. if (rack->hasSelection()) {
  306. rack->resetSelectionAction();
  307. e.consume(this);
  308. }
  309. }
  310. if (e.keyName == "r" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  311. if (rack->hasSelection()) {
  312. rack->randomizeSelectionAction();
  313. e.consume(this);
  314. }
  315. }
  316. if (e.keyName == "u" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  317. if (rack->hasSelection()) {
  318. rack->disconnectSelectionAction();
  319. e.consume(this);
  320. }
  321. }
  322. if (e.keyName == "e" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  323. if (rack->hasSelection()) {
  324. rack->bypassSelectionAction(!rack->isSelectionBypassed());
  325. e.consume(this);
  326. }
  327. }
  328. if (e.keyName == "d" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  329. if (rack->hasSelection()) {
  330. rack->cloneSelectionAction(false);
  331. e.consume(this);
  332. }
  333. }
  334. if (e.keyName == "d" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  335. if (rack->hasSelection()) {
  336. rack->cloneSelectionAction(true);
  337. e.consume(this);
  338. }
  339. }
  340. if ((e.key == GLFW_KEY_DELETE || e.key == GLFW_KEY_BACKSPACE) && (e.mods & RACK_MOD_MASK) == 0) {
  341. if (rack->hasSelection()) {
  342. rack->deleteSelectionAction();
  343. e.consume(this);
  344. }
  345. }
  346. }
  347. // Scroll RackScrollWidget with arrow keys
  348. if (e.action == GLFW_PRESS || e.action == GLFW_RELEASE) {
  349. if (e.key == GLFW_KEY_LEFT) {
  350. internal->heldArrowKeys[0] = (e.action == GLFW_PRESS);
  351. e.consume(this);
  352. }
  353. if (e.key == GLFW_KEY_RIGHT) {
  354. internal->heldArrowKeys[1] = (e.action == GLFW_PRESS);
  355. e.consume(this);
  356. }
  357. if (e.key == GLFW_KEY_UP) {
  358. internal->heldArrowKeys[2] = (e.action == GLFW_PRESS);
  359. e.consume(this);
  360. }
  361. if (e.key == GLFW_KEY_DOWN) {
  362. internal->heldArrowKeys[3] = (e.action == GLFW_PRESS);
  363. e.consume(this);
  364. }
  365. }
  366. if (e.isConsumed())
  367. return;
  368. OpaqueWidget::onHoverKey(e);
  369. if (e.isConsumed())
  370. return;
  371. // Key commands that can be overridden by children
  372. if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
  373. if (e.keyName == "v" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  374. rack->pasteClipboardAction();
  375. e.consume(this);
  376. }
  377. if ((e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER) && (e.mods & RACK_MOD_MASK) == 0) {
  378. browser->show();
  379. e.consume(this);
  380. }
  381. }
  382. }
  383. void Scene::onPathDrop(const PathDropEvent& e) {
  384. if (e.paths.size() >= 1) {
  385. const std::string& path = e.paths[0];
  386. std::string extension = system::getExtension(path);
  387. if (extension == ".vcv") {
  388. patchUtils::loadPathDialog(path);
  389. e.consume(this);
  390. return;
  391. }
  392. if (extension == ".vcvs") {
  393. APP->scene->rack->loadSelection(path);
  394. e.consume(this);
  395. return;
  396. }
  397. }
  398. OpaqueWidget::onPathDrop(e);
  399. }
  400. } // namespace app
  401. } // namespace rack
  402. namespace patchUtils {
  403. bool connectToRemote() {
  404. #ifdef HAVE_LIBLO
  405. rack::app::Scene::Internal* const internal = APP->scene->internal;
  406. if (internal->oscServer == nullptr) {
  407. const lo_server oscServer = lo_server_new_with_proto(nullptr, LO_UDP, nullptr);
  408. DISTRHO_SAFE_ASSERT_RETURN(oscServer != nullptr, false);
  409. lo_server_add_method(oscServer, "/resp", nullptr, rack::app::Scene::Internal::osc_handler, internal);
  410. internal->oscServer = oscServer;
  411. }
  412. const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, REMOTE_HOST_PORT);
  413. DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr, false);
  414. lo_send(addr, "/hello", "");
  415. lo_address_free(addr);
  416. return true;
  417. #else
  418. return false;
  419. #endif
  420. }
  421. bool isRemoteConnected() {
  422. #ifdef HAVE_LIBLO
  423. return APP->scene->internal->oscConnected;
  424. #else
  425. return false;
  426. #endif
  427. }
  428. bool isRemoteAutoDeployed() {
  429. #ifdef HAVE_LIBLO
  430. return APP->scene->internal->oscAutoDeploy;
  431. #else
  432. return false;
  433. #endif
  434. }
  435. void setRemoteAutoDeploy(bool autoDeploy) {
  436. #ifdef HAVE_LIBLO
  437. APP->scene->internal->oscAutoDeploy = autoDeploy;
  438. #endif
  439. }
  440. void deployToRemote() {
  441. #ifdef HAVE_LIBLO
  442. const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, REMOTE_HOST_PORT);
  443. DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,);
  444. APP->engine->prepareSave();
  445. APP->patch->saveAutosave();
  446. APP->patch->cleanAutosave();
  447. std::vector<uint8_t> data(rack::system::archiveDirectory(APP->patch->autosavePath, 1));
  448. if (const lo_blob blob = lo_blob_new(data.size(), data.data())) {
  449. lo_send(addr, "/load", "b", blob);
  450. lo_blob_free(blob);
  451. }
  452. lo_address_free(addr);
  453. #endif
  454. }
  455. } // namespace patchUtils