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