Audio plugin host https://kx.studio/carla
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.

488 lines
16KB

  1. #include "Connection.h"
  2. #include "Fl_Osc_Interface.h"
  3. #include "../globals.h"
  4. #include <map>
  5. #include <cassert>
  6. #include <rtosc/rtosc.h>
  7. #include <rtosc/ports.h>
  8. #include <FL/Fl.H>
  9. #include "Fl_Osc_Tree.H"
  10. #include "common.H"
  11. #include "MasterUI.h"
  12. #include "../Misc/MiddleWare.h"
  13. #ifdef NTK_GUI
  14. #include <FL/Fl_Shared_Image.H>
  15. #include <FL/Fl_Tiled_Image.H>
  16. #include <FL/Fl_Dial.H>
  17. #include <err.h>
  18. #endif // NTK_GUI
  19. #ifndef NO_UI
  20. #include "Fl_Osc_Widget.H"
  21. #endif
  22. using namespace GUI;
  23. class MasterUI *ui;
  24. #ifdef NTK_GUI
  25. static Fl_Tiled_Image *module_backdrop;
  26. #endif
  27. int kb_shortcut_handler(int)
  28. {
  29. const bool undo_ = Fl::event_ctrl() && Fl::event_key() == 'z';
  30. const bool redo = Fl::event_ctrl() && Fl::event_key() == 'r';
  31. const bool show = Fl::event_ctrl() && Fl::event_shift() &&
  32. Fl::event_key() == 's';
  33. const bool panel = Fl::event_ctrl() && Fl::event_shift() &&
  34. Fl::event_key() == 'p';
  35. if(undo_)
  36. ui->osc->write("/undo", "");
  37. else if(redo)
  38. ui->osc->write("/redo", "");
  39. else if (show) {
  40. ui->simplemasterwindow->hide();
  41. ui->masterwindow->show();
  42. } else if (panel)
  43. ui->panelwindow->show();
  44. return undo_ || redo || show;
  45. }
  46. void
  47. set_module_parameters ( Fl_Widget *o )
  48. {
  49. #ifdef NTK_GUI
  50. o->box( FL_DOWN_FRAME );
  51. o->align( o->align() | FL_ALIGN_IMAGE_BACKDROP );
  52. o->color( FL_BLACK );
  53. o->image( module_backdrop );
  54. o->labeltype( FL_SHADOW_LABEL );
  55. if(o->parent()) {
  56. o->parent()->labeltype(FL_NO_LABEL);
  57. o->parent()->box(FL_NO_BOX);
  58. }
  59. #else
  60. o->box( FL_PLASTIC_UP_BOX );
  61. o->color( FL_CYAN );
  62. o->labeltype( FL_EMBOSSED_LABEL );
  63. #endif
  64. }
  65. ui_handle_t GUI::createUi(Fl_Osc_Interface *osc, void *exit)
  66. {
  67. #ifdef NTK_GUI
  68. fl_register_images();
  69. Fl_Dial::default_style(Fl_Dial::PIXMAP_DIAL);
  70. #ifdef CARLA_VERSION_STRING
  71. if(Fl_Shared_Image *img = Fl_Shared_Image::get(gUiPixmapPath + "knob.png"))
  72. Fl_Dial::default_image(img);
  73. #else
  74. if(Fl_Shared_Image *img = Fl_Shared_Image::get(PIXMAP_PATH "/knob.png"))
  75. Fl_Dial::default_image(img);
  76. #endif
  77. else if(Fl_Shared_Image *img = Fl_Shared_Image::get(SOURCE_DIR "/pixmaps/knob.png"))
  78. Fl_Dial::default_image(img);
  79. else
  80. errx(1, "ERROR: Cannot find pixmaps/knob.png");
  81. #ifdef CARLA_VERSION_STRING
  82. if(Fl_Shared_Image *img = Fl_Shared_Image::get(gUiPixmapPath + "/window_backdrop.png"))
  83. Fl::scheme_bg(new Fl_Tiled_Image(img));
  84. #else
  85. if(Fl_Shared_Image *img = Fl_Shared_Image::get(PIXMAP_PATH "/window_backdrop.png"))
  86. Fl::scheme_bg(new Fl_Tiled_Image(img));
  87. #endif
  88. else if(Fl_Shared_Image *img = Fl_Shared_Image::get(SOURCE_DIR "/pixmaps/window_backdrop.png"))
  89. Fl::scheme_bg(new Fl_Tiled_Image(img));
  90. else
  91. errx(1, "ERROR: Cannot find pixmaps/window_backdrop.png");
  92. #ifdef CARLA_VERSION_STRING
  93. if(Fl_Shared_Image *img = Fl_Shared_Image::get(gUiPixmapPath + "/module_backdrop.png"))
  94. module_backdrop = new Fl_Tiled_Image(img);
  95. #else
  96. if(Fl_Shared_Image *img = Fl_Shared_Image::get(PIXMAP_PATH "/module_backdrop.png"))
  97. module_backdrop = new Fl_Tiled_Image(img);
  98. #endif
  99. else if(Fl_Shared_Image *img = Fl_Shared_Image::get(SOURCE_DIR "/pixmaps/module_backdrop.png"))
  100. module_backdrop = new Fl_Tiled_Image(img);
  101. else
  102. errx(1, "ERROR: Cannot find pixmaps/module_backdrop");
  103. Fl::background(50, 50, 50);
  104. Fl::background2(70, 70, 70);
  105. Fl::foreground(255, 255, 255);
  106. #endif
  107. //Fl_Window *midi_win = new Fl_Double_Window(400, 400, "Midi connections");
  108. //Fl_Osc_Tree *tree = new Fl_Osc_Tree(0,0,400,400);
  109. //midi_win->resizable(tree);
  110. //tree->root_ports = &Master::ports;
  111. //tree->osc = osc;
  112. //midi_win->show();
  113. Fl::add_handler(kb_shortcut_handler);
  114. return (void*) (ui = new MasterUI((int*)exit, osc));
  115. }
  116. void GUI::destroyUi(ui_handle_t ui)
  117. {
  118. delete static_cast<MasterUI*>(ui);
  119. }
  120. #define BEGIN(x) {x,":non-realtime\0",NULL,[](const char *m, rtosc::RtData &d){ \
  121. MasterUI *ui = static_cast<MasterUI*>(d.obj); \
  122. rtosc_arg_t a0 = {0}, a1 = {0}; \
  123. if(rtosc_narguments(m) > 0) \
  124. a0 = rtosc_argument(m,0); \
  125. if(rtosc_narguments(m) > 1) \
  126. a1 = rtosc_argument(m,1); \
  127. (void)ui;(void)a1;(void)a0;
  128. #define END }},
  129. struct uiPorts {
  130. static rtosc::Ports ports;
  131. };
  132. //DSL based ports
  133. rtosc::Ports uiPorts::ports = {
  134. BEGIN("show:i") {
  135. ui->showUI(a0.i);
  136. } END
  137. BEGIN("alert:s") {
  138. fl_alert("%s",a0.s);
  139. } END
  140. BEGIN("alert-reload:i") {
  141. int res = fl_choice("Old autosave found, do you want to reload?",
  142. "Delete", "Reload", "Ignore");
  143. // 0 1 2
  144. if(1==res) {
  145. d.reply("/reload_auto_save", "i", a0.i);
  146. ui->refresh_master_ui();
  147. ui->updatepanel();
  148. } else if(0==res) {
  149. d.reply("/delete_auto_save", "i", a0.i);
  150. }
  151. } END
  152. BEGIN("session-type:s") {
  153. if(strcmp(a0.s,"LASH"))
  154. return;
  155. ui->sm_indicator1->value(1);
  156. ui->sm_indicator2->value(1);
  157. ui->sm_indicator1->tooltip("LASH");
  158. ui->sm_indicator2->tooltip("LASH");
  159. } END
  160. BEGIN("save-master:s") {
  161. ui->do_save_master(a0.s);
  162. } END
  163. BEGIN("load-master:s") {
  164. ui->do_load_master(a0.s);
  165. } END
  166. BEGIN("vu-meter:bb") {
  167. if(a0.b.len == sizeof(vuData) &&
  168. a1.b.len == sizeof(float)*NUM_MIDI_PARTS) {
  169. //Refresh the primary VU meters
  170. ui->simplemastervu->update((vuData*)a0.b.data);
  171. ui->mastervu->update((vuData*)a0.b.data);
  172. float *partvu = (float*)a1.b.data;
  173. for(int i=0; i<NUM_MIDI_PARTS; ++i)
  174. ui->panellistitem[i]->partvu->update(partvu[i]);
  175. }
  176. } END
  177. BEGIN("close-ui") {
  178. ui->close();
  179. } END
  180. };
  181. //very tiny rtdata ext
  182. class RtDataUI: public rtosc::RtData {
  183. public:
  184. RtDataUI(Fl_Osc_Interface *osc_)
  185. :osc(osc_)
  186. {}
  187. void reply(const char *path, const char *args, ...) override
  188. {
  189. va_list va;
  190. va_start(va,args);
  191. char buf[2048];
  192. rtosc_vmessage(buf,sizeof(buf),path,args,va);
  193. osc->writeRaw(buf);
  194. va_end(va);
  195. }
  196. Fl_Osc_Interface *osc;
  197. };
  198. void GUI::raiseUi(ui_handle_t gui, const char *message)
  199. {
  200. if(!gui)
  201. return;
  202. MasterUI *mui = (MasterUI*)gui;
  203. if(string("/damage") == message && rtosc_type(message, 0) == 's') {
  204. string damage_str = rtosc_argument(message,0).s;
  205. int npart = -1;
  206. if(sscanf(damage_str.c_str(), "/part%d", &npart) == 1 && damage_str.size() < 10) {
  207. if(mui->npartcounter->value()-1 == npart) {
  208. mui->partui->showparameters(0,-1);
  209. mui->npartcounter->do_callback();
  210. }
  211. }
  212. mui->osc->damage(rtosc_argument(message,0).s);
  213. }
  214. mui->osc->tryLink(message);
  215. //printf("got message for UI '%s'\n", message);
  216. char buffer[1024];
  217. memset(buffer, 0, sizeof(buffer));
  218. RtDataUI d(mui->osc);
  219. d.loc = buffer;
  220. d.loc_size = 1024;
  221. d.obj = gui;
  222. uiPorts::ports.dispatch(message+1, d);
  223. }
  224. void GUI::raiseUi(ui_handle_t gui, const char *dest, const char *args, ...)
  225. {
  226. char buffer[1024];
  227. va_list va;
  228. va_start(va,args);
  229. if(rtosc_vmessage(buffer,1024,dest,args,va))
  230. raiseUi(gui, buffer);
  231. va_end(va);
  232. }
  233. void GUI::tickUi(ui_handle_t)
  234. {
  235. Fl::wait(0.02f);
  236. }
  237. /******************************************************************************
  238. * OSC Interface For User Interface *
  239. * *
  240. * This is a largely out of date section of code *
  241. * Most type specific write methods are no longer used *
  242. * See UI/Fl_Osc_* to see what is actually used in this interface *
  243. ******************************************************************************/
  244. class UI_Interface:public Fl_Osc_Interface
  245. {
  246. public:
  247. UI_Interface(MiddleWare *impl_)
  248. :impl(impl_)
  249. {}
  250. void requestValue(string s) override
  251. {
  252. assert(s!="/Psysefxvol-1/part0");
  253. //Fl_Osc_Interface::requestValue(s);
  254. char *tmp = strdup(s.c_str());
  255. s = rtosc::Ports::collapsePath(tmp);
  256. free(tmp);
  257. if(impl->activeUrl() != "GUI") {
  258. impl->transmitMsg("/echo", "ss", "OSC_URL", "GUI");
  259. impl->activeUrl("GUI");
  260. }
  261. impl->transmitMsg(s.c_str(),"");
  262. }
  263. void write(string s, const char *args, ...) override
  264. {
  265. char *tmp = strdup(s.c_str());
  266. s = rtosc::Ports::collapsePath(tmp);
  267. free(tmp);
  268. va_list va;
  269. va_start(va, args);
  270. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 4 + 30, 0 + 40);
  271. ////fprintf(stderr, ".");
  272. //fprintf(stderr, "write(%s:%s)\n", s.c_str(), args);
  273. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  274. impl->transmitMsg_va(s.c_str(), args, va);
  275. va_end(va);
  276. }
  277. void writeRaw(const char *msg) override
  278. {
  279. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 4 + 30, 0 + 40);
  280. ////fprintf(stderr, ".");
  281. //fprintf(stderr, "rawWrite(%s:%s)\n", msg, rtosc_argument_string(msg));
  282. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  283. impl->transmitMsg(rtosc::Ports::collapsePath((char*)msg));
  284. }
  285. void writeValue(string s, string ss) override
  286. {
  287. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 4 + 30, 0 + 40);
  288. //fprintf(stderr, "writevalue<string>(%s,%s)\n", s.c_str(),ss.c_str());
  289. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  290. write(s, "s", ss.c_str());
  291. }
  292. void writeValue(string s, char c) override
  293. {
  294. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 4 + 30, 0 + 40);
  295. //fprintf(stderr, "writevalue<char>(%s,%d)\n", s.c_str(),c);
  296. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  297. write(s, "c", c);
  298. }
  299. void writeValue(string s, float f) override
  300. {
  301. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 4 + 30, 0 + 40);
  302. //fprintf(stderr, "writevalue<float>(%s,%f)\n", s.c_str(),f);
  303. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  304. write(s, "f", f);
  305. }
  306. void createLink(string s, class Fl_Osc_Widget*w) override
  307. {
  308. char *tmp = strdup(s.c_str());
  309. s = rtosc::Ports::collapsePath(tmp);
  310. free(tmp);
  311. assert(s.length() != 0);
  312. Fl_Osc_Interface::createLink(s,w);
  313. assert(!strstr(s.c_str(), "/part0/kit-1"));
  314. map.insert(std::pair<string,Fl_Osc_Widget*>(s,w));
  315. }
  316. void renameLink(string old, string newer, Fl_Osc_Widget *w) override
  317. {
  318. //fprintf(stdout, "renameLink('%s','%s',%p)\n",
  319. // old.c_str(), newer.c_str(), w);
  320. removeLink(old, w);
  321. createLink(newer, w);
  322. }
  323. void removeLink(string s, class Fl_Osc_Widget*w) override
  324. {
  325. char *tmp = strdup(s.c_str());
  326. s = rtosc::Ports::collapsePath(tmp);
  327. free(tmp);
  328. for(auto i = map.begin(); i != map.end(); ++i) {
  329. if(i->first == s && i->second == w) {
  330. map.erase(i);
  331. break;
  332. }
  333. }
  334. //printf("[%d] removing '%s' (%p)...\n", map.size(), s.c_str(), w);
  335. }
  336. virtual void removeLink(class Fl_Osc_Widget *w) override
  337. {
  338. bool processing = true;
  339. while(processing)
  340. {
  341. //Verify Iterator invalidation sillyness
  342. processing = false;//Exit if no new elements are found
  343. for(auto i = map.begin(); i != map.end(); ++i) {
  344. if(i->second == w) {
  345. //printf("[%d] removing '%s' (%p)...\n", map.size()-1,
  346. // i->first.c_str(), w);
  347. map.erase(i);
  348. processing = true;
  349. break;
  350. }
  351. }
  352. }
  353. }
  354. //A very simplistic implementation of a UI agnostic refresh method
  355. virtual void damage(const char *path) override
  356. {
  357. #ifndef NO_UI
  358. printf("\n\nDamage(\"%s\")\n", path);
  359. std::set<Fl_Osc_Widget*> to_update;
  360. for(auto pair:map) {
  361. if(strstr(pair.first.c_str(), path)) {
  362. auto *tmp = dynamic_cast<Fl_Widget*>(pair.second);
  363. if(!tmp || tmp->visible_r()) {
  364. to_update.insert(pair.second);
  365. }
  366. }
  367. }
  368. for(auto elm:to_update)
  369. elm->update();
  370. #endif
  371. }
  372. void tryLink(const char *msg) override
  373. {
  374. //DEBUG
  375. //if(strcmp(msg, "/vu-meter"))//Ignore repeated message
  376. // printf("trying the link for a '%s'<%s>\n", msg, rtosc_argument_string(msg));
  377. const char *handle = rindex(msg,'/');
  378. if(handle)
  379. ++handle;
  380. int found_count = 0;
  381. auto range = map.equal_range(msg);
  382. for(auto itr = range.first; itr != range.second; ++itr) {
  383. auto widget = itr->second;
  384. found_count++;
  385. const char *arg_str = rtosc_argument_string(msg);
  386. //Always provide the raw message
  387. widget->OSC_raw(msg);
  388. if(!strcmp(arg_str, "b")) {
  389. widget->OSC_value(rtosc_argument(msg,0).b.len,
  390. rtosc_argument(msg,0).b.data,
  391. handle);
  392. } else if(!strcmp(arg_str, "c")) {
  393. widget->OSC_value((char)rtosc_argument(msg,0).i,
  394. handle);
  395. } else if(!strcmp(arg_str, "s")) {
  396. widget->OSC_value((const char*)rtosc_argument(msg,0).s,
  397. handle);
  398. } else if(!strcmp(arg_str, "i")) {
  399. widget->OSC_value((int)rtosc_argument(msg,0).i,
  400. handle);
  401. } else if(!strcmp(arg_str, "f")) {
  402. widget->OSC_value((float)rtosc_argument(msg,0).f,
  403. handle);
  404. } else if(!strcmp(arg_str, "T") || !strcmp(arg_str, "F")) {
  405. widget->OSC_value((bool)rtosc_argument(msg,0).T, handle);
  406. }
  407. }
  408. if(found_count == 0
  409. && strcmp(msg, "/vu-meter")
  410. && strcmp(msg, "undo_change")
  411. && !strstr(msg, "parameter")
  412. && !strstr(msg, "Prespoint")) {
  413. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 1, 7 + 30, 0 + 40);
  414. //fprintf(stderr, "Unknown widget '%s'\n", msg);
  415. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  416. }
  417. };
  418. void dumpLookupTable(void)
  419. {
  420. if(!map.empty()) {
  421. printf("Leaked controls:\n");
  422. for(auto i = map.begin(); i != map.end(); ++i) {
  423. printf("Known control '%s' (%p)...\n", i->first.c_str(), i->second);
  424. }
  425. }
  426. }
  427. private:
  428. std::multimap<string,Fl_Osc_Widget*> map;
  429. MiddleWare *impl;
  430. };
  431. Fl_Osc_Interface *GUI::genOscInterface(MiddleWare *mw)
  432. {
  433. return new UI_Interface(mw);
  434. }