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.

432 lines
12KB

  1. --- ../Rack/src/app/Scene.cpp 2022-09-21 20:25:53.591040280 +0100
  2. +++ Scene.cpp 2022-09-21 20:18:50.294557597 +0100
  3. @@ -1,3 +1,30 @@
  4. +/*
  5. + * DISTRHO Cardinal Plugin
  6. + * Copyright (C) 2021-2022 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 app/Scene.cpp
  23. + * Copyright (C) 2016-2021 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 <thread>
  32. #include <osdialog.h>
  33. @@ -7,6 +34,7 @@
  34. #include <app/TipWindow.hpp>
  35. #include <app/MenuBar.hpp>
  36. #include <context.hpp>
  37. +#include <engine/Engine.hpp>
  38. #include <system.hpp>
  39. #include <network.hpp>
  40. #include <history.hpp>
  41. @@ -14,6 +42,22 @@
  42. #include <patch.hpp>
  43. #include <asset.hpp>
  44. +#ifdef NDEBUG
  45. +# undef DEBUG
  46. +#endif
  47. +
  48. +#ifdef STATIC_BUILD
  49. +# undef HAVE_LIBLO
  50. +#endif
  51. +
  52. +#ifdef HAVE_LIBLO
  53. +# include <lo/lo.h>
  54. +#endif
  55. +
  56. +#include "../CardinalCommon.hpp"
  57. +#include "extra/Base64.hpp"
  58. +#include "DistrhoUtils.hpp"
  59. +
  60. namespace rack {
  61. namespace app {
  62. @@ -23,32 +67,94 @@
  63. math::Vec size;
  64. void draw(const DrawArgs& args) override {
  65. + nvgStrokeColor(args.vg, nvgRGBf(1, 1, 1));
  66. + nvgStrokeWidth(args.vg, 1);
  67. +
  68. nvgBeginPath(args.vg);
  69. - nvgMoveTo(args.vg, box.size.x, box.size.y);
  70. + nvgMoveTo(args.vg, box.size.x, 0);
  71. nvgLineTo(args.vg, 0, box.size.y);
  72. - nvgLineTo(args.vg, box.size.x, 0);
  73. - nvgClosePath(args.vg);
  74. - nvgFillColor(args.vg, nvgRGBAf(1, 1, 1, 0.15));
  75. - nvgFill(args.vg);
  76. + nvgStroke(args.vg);
  77. +
  78. + nvgBeginPath(args.vg);
  79. + nvgMoveTo(args.vg, box.size.x + 5, 0);
  80. + nvgLineTo(args.vg, 0, box.size.y + 5);
  81. + nvgStroke(args.vg);
  82. +
  83. + nvgBeginPath(args.vg);
  84. + nvgMoveTo(args.vg, box.size.x + 10, 0);
  85. + nvgLineTo(args.vg, 0, box.size.y + 10);
  86. + nvgStroke(args.vg);
  87. +
  88. + nvgStrokeColor(args.vg, nvgRGBf(0, 0, 0));
  89. +
  90. + nvgBeginPath(args.vg);
  91. + nvgMoveTo(args.vg, box.size.x+1, 0);
  92. + nvgLineTo(args.vg, 0, box.size.y+1);
  93. + nvgStroke(args.vg);
  94. +
  95. + nvgBeginPath(args.vg);
  96. + nvgMoveTo(args.vg, box.size.x + 6, 0);
  97. + nvgLineTo(args.vg, 0, box.size.y + 6);
  98. + nvgStroke(args.vg);
  99. +
  100. + nvgBeginPath(args.vg);
  101. + nvgMoveTo(args.vg, box.size.x + 11, 0);
  102. + nvgLineTo(args.vg, 0, box.size.y + 11);
  103. + nvgStroke(args.vg);
  104. + }
  105. +
  106. + void onHover(const HoverEvent& e) override {
  107. + e.consume(this);
  108. + }
  109. +
  110. + void onEnter(const EnterEvent& e) override {
  111. + glfwSetCursor(APP->window->win, glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR));
  112. + }
  113. +
  114. + void onLeave(const LeaveEvent& e) override {
  115. + glfwSetCursor(APP->window->win, nullptr);
  116. }
  117. - void onDragStart(const DragStartEvent& e) override {
  118. + void onDragStart(const DragStartEvent&) override {
  119. size = APP->window->getSize();
  120. }
  121. void onDragMove(const DragMoveEvent& e) override {
  122. - size = size.plus(e.mouseDelta);
  123. + size = size.plus(e.mouseDelta.mult(APP->window->pixelRatio));
  124. APP->window->setSize(size.round());
  125. }
  126. };
  127. struct Scene::Internal {
  128. - ResizeHandle* resizeHandle;
  129. -
  130. - double lastAutosaveTime = 0.0;
  131. + ResizeHandle* resizeHandle = nullptr;
  132. bool heldArrowKeys[4] = {};
  133. +
  134. +#ifdef HAVE_LIBLO
  135. + double lastSceneChangeTime = 0.0;
  136. + int historyActionIndex = -1;
  137. +
  138. + bool oscAutoDeploy = false;
  139. + bool oscConnected = false;
  140. + lo_server oscServer = nullptr;
  141. +
  142. + static int osc_handler(const char* const path, const char* const types, lo_arg** argv, const int argc, lo_message, void* const self)
  143. + {
  144. + d_stdout("osc_handler(\"%s\", \"%s\", %p, %i)", path, types, argv, argc);
  145. +
  146. + if (std::strcmp(path, "/resp") == 0 && argc == 2 && types[0] == 's' && types[1] == 's') {
  147. + d_stdout("osc_handler(\"%s\", ...) - got resp | '%s' '%s'", path, &argv[0]->s, &argv[1]->s);
  148. + if (std::strcmp(&argv[0]->s, "hello") == 0 && std::strcmp(&argv[1]->s, "ok") == 0)
  149. + static_cast<Internal*>(self)->oscConnected = true;
  150. + }
  151. + return 0;
  152. + }
  153. +
  154. + ~Internal() {
  155. + lo_server_free(oscServer);
  156. + }
  157. +#endif
  158. };
  159. @@ -67,13 +173,11 @@
  160. browser->hide();
  161. addChild(browser);
  162. - if (settings::showTipsOnLaunch) {
  163. - addChild(tipWindowCreate());
  164. - }
  165. + if (isStandalone())
  166. + return;
  167. internal->resizeHandle = new ResizeHandle;
  168. - internal->resizeHandle->box.size = math::Vec(15, 15);
  169. - internal->resizeHandle->hide();
  170. + internal->resizeHandle->box.size = math::Vec(16, 16);
  171. addChild(internal->resizeHandle);
  172. }
  173. @@ -99,22 +203,13 @@
  174. rackScroll->box.pos.y = menuBar->box.size.y;
  175. }
  176. - internal->resizeHandle->box.pos = box.size.minus(internal->resizeHandle->box.size);
  177. + if (internal->resizeHandle != nullptr)
  178. + internal->resizeHandle->box.pos = box.size.minus(internal->resizeHandle->box.size);
  179. // Resize owned descendants
  180. menuBar->box.size.x = box.size.x;
  181. rackScroll->box.size = box.size.minus(rackScroll->box.pos);
  182. - // Autosave periodically
  183. - if (settings::autosaveInterval > 0.0) {
  184. - double time = system::getTime();
  185. - if (time - internal->lastAutosaveTime >= settings::autosaveInterval) {
  186. - internal->lastAutosaveTime = time;
  187. - APP->patch->saveAutosave();
  188. - settings::save();
  189. - }
  190. - }
  191. -
  192. // Scroll RackScrollWidget with arrow keys
  193. math::Vec arrowDelta;
  194. if (internal->heldArrowKeys[0]) {
  195. @@ -143,6 +238,23 @@
  196. rackScroll->offset += arrowDelta * arrowSpeed;
  197. }
  198. +#ifdef HAVE_LIBLO
  199. + if (internal->oscServer != nullptr) {
  200. + while (lo_server_recv_noblock(internal->oscServer, 0) != 0) {}
  201. +
  202. + if (internal->oscAutoDeploy) {
  203. + const int actionIndex = APP->history->actionIndex;
  204. + const double time = system::getTime();
  205. + if (internal->historyActionIndex != actionIndex && time - internal->lastSceneChangeTime >= 5.0) {
  206. + internal->historyActionIndex = actionIndex;
  207. + internal->lastSceneChangeTime = time;
  208. + patchUtils::deployToRemote();
  209. + window::generateScreenshot();
  210. + }
  211. + }
  212. + }
  213. +#endif
  214. +
  215. Widget::step();
  216. }
  217. @@ -172,7 +284,7 @@
  218. if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
  219. // DEBUG("key '%d '%c' scancode %d '%c' keyName '%s'", e.key, e.key, e.scancode, e.scancode, e.keyName.c_str());
  220. if (e.keyName == "n" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  221. - APP->patch->loadTemplateDialog();
  222. + patchUtils::loadTemplateDialog();
  223. e.consume(this);
  224. }
  225. if (e.keyName == "q" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  226. @@ -180,19 +292,22 @@
  227. e.consume(this);
  228. }
  229. if (e.keyName == "o" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  230. - APP->patch->loadDialog();
  231. + patchUtils::loadDialog();
  232. e.consume(this);
  233. }
  234. if (e.keyName == "o" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  235. - APP->patch->revertDialog();
  236. + patchUtils::revertDialog();
  237. e.consume(this);
  238. }
  239. +#ifndef DISTRHO_OS_WASM
  240. if (e.keyName == "s" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  241. - APP->patch->saveDialog();
  242. + // NOTE: will do nothing if path is empty, intentionally
  243. + patchUtils::saveDialog(APP->patch->path);
  244. e.consume(this);
  245. }
  246. +#endif
  247. if (e.keyName == "s" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  248. - APP->patch->saveAsDialog();
  249. + patchUtils::saveAsDialog();
  250. e.consume(this);
  251. }
  252. if (e.keyName == "z" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  253. @@ -220,24 +335,37 @@
  254. APP->scene->rackScroll->setZoom(std::pow(2.f, zoom));
  255. e.consume(this);
  256. }
  257. - if ((e.keyName == "0") && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  258. + if ((e.keyName == "0" || e.keyName == "1") && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  259. APP->scene->rackScroll->setZoom(1.f);
  260. e.consume(this);
  261. }
  262. + if (e.keyName == "2" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  263. + APP->scene->rackScroll->setZoom(2.f);
  264. + e.consume(this);
  265. + }
  266. if (e.key == GLFW_KEY_F1 && (e.mods & RACK_MOD_MASK) == 0) {
  267. - system::openBrowser("https://vcvrack.com/manual/");
  268. + patchUtils::openBrowser("https://vcvrack.com/manual/");
  269. e.consume(this);
  270. }
  271. if (e.key == GLFW_KEY_F3 && (e.mods & RACK_MOD_MASK) == 0) {
  272. settings::cpuMeter ^= true;
  273. e.consume(this);
  274. }
  275. + if (e.key == GLFW_KEY_F7 && (e.mods & RACK_MOD_MASK) == 0) {
  276. + patchUtils::deployToRemote();
  277. + window::generateScreenshot();
  278. + e.consume(this);
  279. + }
  280. + if (e.key == GLFW_KEY_F9 && (e.mods & RACK_MOD_MASK) == 0) {
  281. + window::generateScreenshot();
  282. + e.consume(this);
  283. + }
  284. +#ifdef DISTRHO_OS_WASM
  285. if (e.key == GLFW_KEY_F11 && (e.mods & RACK_MOD_MASK) == 0) {
  286. APP->window->setFullScreen(!APP->window->isFullScreen());
  287. - // The MenuBar will be hidden when the mouse moves over the RackScrollWidget.
  288. - // menuBar->hide();
  289. e.consume(this);
  290. }
  291. +#endif
  292. // Module selections
  293. if (e.keyName == "a" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  294. @@ -326,13 +454,6 @@
  295. // Key commands that can be overridden by children
  296. if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
  297. - // Alternative key command for exiting fullscreen, since F11 doesn't work reliably on Mac due to "Show desktop" OS binding.
  298. - if (e.key == GLFW_KEY_ESCAPE && (e.mods & RACK_MOD_MASK) == 0) {
  299. - if (APP->window->isFullScreen()) {
  300. - APP->window->setFullScreen(false);
  301. - e.consume(this);
  302. - }
  303. - }
  304. if (e.keyName == "v" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  305. rack->pasteClipboardAction();
  306. e.consume(this);
  307. @@ -351,7 +472,7 @@
  308. std::string extension = system::getExtension(path);
  309. if (extension == ".vcv") {
  310. - APP->patch->loadPathDialog(path);
  311. + patchUtils::loadPathDialog(path);
  312. e.consume(this);
  313. return;
  314. }
  315. @@ -368,3 +489,94 @@
  316. } // namespace app
  317. } // namespace rack
  318. +
  319. +
  320. +namespace patchUtils {
  321. +
  322. +
  323. +bool connectToRemote() {
  324. +#ifdef HAVE_LIBLO
  325. + rack::app::Scene::Internal* const internal = APP->scene->internal;
  326. +
  327. + if (internal->oscServer == nullptr) {
  328. + const lo_server oscServer = lo_server_new_with_proto(nullptr, LO_UDP, nullptr);
  329. + DISTRHO_SAFE_ASSERT_RETURN(oscServer != nullptr, false);
  330. + lo_server_add_method(oscServer, "/resp", nullptr, rack::app::Scene::Internal::osc_handler, internal);
  331. + internal->oscServer = oscServer;
  332. + }
  333. +
  334. + const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, REMOTE_HOST_PORT);
  335. + DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr, false);
  336. + lo_send(addr, "/hello", "");
  337. + lo_address_free(addr);
  338. +
  339. + return true;
  340. +#else
  341. + return false;
  342. +#endif
  343. +}
  344. +
  345. +
  346. +bool isRemoteConnected() {
  347. +#ifdef HAVE_LIBLO
  348. + return APP->scene->internal->oscConnected;
  349. +#else
  350. + return false;
  351. +#endif
  352. +}
  353. +
  354. +
  355. +bool isRemoteAutoDeployed() {
  356. +#ifdef HAVE_LIBLO
  357. + return APP->scene->internal->oscAutoDeploy;
  358. +#else
  359. + return false;
  360. +#endif
  361. +}
  362. +
  363. +
  364. +void setRemoteAutoDeploy(bool autoDeploy) {
  365. +#ifdef HAVE_LIBLO
  366. + APP->scene->internal->oscAutoDeploy = autoDeploy;
  367. +#endif
  368. +}
  369. +
  370. +
  371. +void deployToRemote() {
  372. +#ifdef HAVE_LIBLO
  373. + const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, REMOTE_HOST_PORT);
  374. + DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,);
  375. +
  376. + APP->engine->prepareSave();
  377. + APP->patch->saveAutosave();
  378. + APP->patch->cleanAutosave();
  379. + std::vector<uint8_t> data(rack::system::archiveDirectory(APP->patch->autosavePath, 1));
  380. +
  381. + if (const lo_blob blob = lo_blob_new(data.size(), data.data())) {
  382. + lo_send(addr, "/load", "b", blob);
  383. + lo_blob_free(blob);
  384. + }
  385. +
  386. + lo_address_free(addr);
  387. +#endif
  388. +}
  389. +
  390. +
  391. +void sendScreenshotToRemote(const char* const screenshot) {
  392. +#ifdef HAVE_LIBLO
  393. + const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, REMOTE_HOST_PORT);
  394. + DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,);
  395. +
  396. + std::vector<uint8_t> data(d_getChunkFromBase64String(screenshot));
  397. +
  398. + if (const lo_blob blob = lo_blob_new(data.size(), data.data())) {
  399. + lo_send(addr, "/screenshot", "b", blob);
  400. + lo_blob_free(blob);
  401. + }
  402. +
  403. + lo_address_free(addr);
  404. +#endif
  405. +}
  406. +
  407. +
  408. +} // namespace patchUtils