#include "Connection.h" #include "Fl_Osc_Interface.h" #include "../globals.h" #include #include #include #include #include #include "Fl_Osc_Tree.H" #include "common.H" #include "MasterUI.h" #include "../Misc/MiddleWare.h" #ifdef NTK_GUI #include #include #include #include #endif // NTK_GUI #ifndef NO_UI #include "Fl_Osc_Widget.H" #endif using namespace GUI; class MasterUI *ui; #ifdef NTK_GUI static Fl_Tiled_Image *module_backdrop; #endif int undo_redo_handler(int) { const bool undo_ = Fl::event_ctrl() && Fl::event_key() == 'z'; const bool redo = Fl::event_ctrl() && Fl::event_key() == 'r'; if(undo_) ui->osc->write("/undo", ""); else if(redo) ui->osc->write("/redo", ""); return undo_ || redo; } void set_module_parameters ( Fl_Widget *o ) { #ifdef NTK_GUI o->box( FL_DOWN_FRAME ); o->align( o->align() | FL_ALIGN_IMAGE_BACKDROP ); o->color( FL_BLACK ); o->image( module_backdrop ); o->labeltype( FL_SHADOW_LABEL ); if(o->parent()) { o->parent()->labeltype(FL_NO_LABEL); o->parent()->box(FL_NO_BOX); } #else o->box( FL_PLASTIC_UP_BOX ); o->color( FL_CYAN ); o->labeltype( FL_EMBOSSED_LABEL ); #endif } ui_handle_t GUI::createUi(Fl_Osc_Interface *osc, void *exit) { #ifdef NTK_GUI fl_register_images(); Fl_Dial::default_style(Fl_Dial::PIXMAP_DIAL); #ifdef CARLA_VERSION_STRING if(Fl_Shared_Image *img = Fl_Shared_Image::get(gUiPixmapPath + "knob.png")) Fl_Dial::default_image(img); #else if(Fl_Shared_Image *img = Fl_Shared_Image::get(PIXMAP_PATH "/knob.png")) Fl_Dial::default_image(img); #endif else if(Fl_Shared_Image *img = Fl_Shared_Image::get(SOURCE_DIR "/pixmaps/knob.png")) Fl_Dial::default_image(img); else errx(1, "ERROR: Cannot find pixmaps/knob.png"); #ifdef CARLA_VERSION_STRING if(Fl_Shared_Image *img = Fl_Shared_Image::get(gUiPixmapPath + "/window_backdrop.png")) Fl::scheme_bg(new Fl_Tiled_Image(img)); #else if(Fl_Shared_Image *img = Fl_Shared_Image::get(PIXMAP_PATH "/window_backdrop.png")) Fl::scheme_bg(new Fl_Tiled_Image(img)); #endif else if(Fl_Shared_Image *img = Fl_Shared_Image::get(SOURCE_DIR "/pixmaps/window_backdrop.png")) Fl::scheme_bg(new Fl_Tiled_Image(img)); else errx(1, "ERROR: Cannot find pixmaps/window_backdrop.png"); #ifdef CARLA_VERSION_STRING if(Fl_Shared_Image *img = Fl_Shared_Image::get(gUiPixmapPath + "/module_backdrop.png")) module_backdrop = new Fl_Tiled_Image(img); #else if(Fl_Shared_Image *img = Fl_Shared_Image::get(PIXMAP_PATH "/module_backdrop.png")) module_backdrop = new Fl_Tiled_Image(img); #endif else if(Fl_Shared_Image *img = Fl_Shared_Image::get(SOURCE_DIR "/pixmaps/module_backdrop.png")) module_backdrop = new Fl_Tiled_Image(img); else errx(1, "ERROR: Cannot find pixmaps/module_backdrop"); Fl::background(50, 50, 50); Fl::background2(70, 70, 70); Fl::foreground(255, 255, 255); #endif //Fl_Window *midi_win = new Fl_Double_Window(400, 400, "Midi connections"); //Fl_Osc_Tree *tree = new Fl_Osc_Tree(0,0,400,400); //midi_win->resizable(tree); //tree->root_ports = &Master::ports; //tree->osc = osc; //midi_win->show(); Fl::add_handler(undo_redo_handler); return (void*) (ui = new MasterUI((int*)exit, osc)); } void GUI::destroyUi(ui_handle_t ui) { delete static_cast(ui); } #define BEGIN(x) {x,":non-realtime\0",NULL,[](const char *m, rtosc::RtData d){ \ MasterUI *ui = static_cast(d.obj); \ rtosc_arg_t a0 = {0}, a1 = {0}; \ if(rtosc_narguments(m) > 0) \ a0 = rtosc_argument(m,0); \ if(rtosc_narguments(m) > 1) \ a1 = rtosc_argument(m,1); \ (void)ui;(void)a1;(void)a0; #define END }}, struct uiPorts { static rtosc::Ports ports; }; //DSL based ports rtosc::Ports uiPorts::ports = { BEGIN("show:i") { ui->showUI(a0.i); } END BEGIN("alert:s") { fl_alert("%s",a0.s); } END BEGIN("session-type:s") { if(strcmp(a0.s,"LASH")) return; ui->sm_indicator1->value(1); ui->sm_indicator2->value(1); ui->sm_indicator1->tooltip("LASH"); ui->sm_indicator2->tooltip("LASH"); } END BEGIN("save-master:s") { ui->do_save_master(a0.s); } END BEGIN("load-master:s") { ui->do_load_master(a0.s); } END BEGIN("vu-meter:bb") { if(a0.b.len == sizeof(vuData) && a1.b.len == sizeof(float)*NUM_MIDI_PARTS) { //Refresh the primary VU meters ui->simplemastervu->update((vuData*)a0.b.data); ui->mastervu->update((vuData*)a0.b.data); float *partvu = (float*)a1.b.data; for(int i=0; ipanellistitem[i]->partvu->update(partvu[i]); } } END BEGIN("close-ui") { ui->close(); } END }; void GUI::raiseUi(ui_handle_t gui, const char *message) { if(!gui) return; MasterUI *mui = (MasterUI*)gui; if(string("/damage") == message && rtosc_type(message, 0) == 's') { string damage_str = rtosc_argument(message,0).s; int npart = -1; if(sscanf(damage_str.c_str(), "/part%d", &npart) == 1 && damage_str.size() < 10) { if(mui->npartcounter->value()-1 == npart) { mui->partui->showparameters(0,-1); mui->npartcounter->do_callback(); } } mui->osc->damage(rtosc_argument(message,0).s); } mui->osc->tryLink(message); //printf("got message for UI '%s'\n", message); char buffer[1024]; memset(buffer, 0, sizeof(buffer)); rtosc::RtData d; d.loc = buffer; d.loc_size = 1024; d.obj = gui; uiPorts::ports.dispatch(message+1, d); } void GUI::raiseUi(ui_handle_t gui, const char *dest, const char *args, ...) { char buffer[1024]; va_list va; va_start(va,args); if(rtosc_vmessage(buffer,1024,dest,args,va)) raiseUi(gui, buffer); va_end(va); } void GUI::tickUi(ui_handle_t) { Fl::wait(0.02f); } /****************************************************************************** * OSC Interface For User Interface * * * * This is a largely out of date section of code * * Most type specific write methods are no longer used * * See UI/Fl_Osc_* to see what is actually used in this interface * ******************************************************************************/ class UI_Interface:public Fl_Osc_Interface { public: UI_Interface(MiddleWare *impl_) :impl(impl_) {} void requestValue(string s) override { assert(s!="/Psysefxvol-1/part0"); //Fl_Osc_Interface::requestValue(s); char *tmp = strdup(s.c_str()); s = rtosc::Ports::collapsePath(tmp); free(tmp); if(impl->activeUrl() != "GUI") { impl->transmitMsg("/echo", "ss", "OSC_URL", "GUI"); impl->activeUrl("GUI"); } impl->transmitMsg(s.c_str(),""); } void write(string s, const char *args, ...) override { char *tmp = strdup(s.c_str()); s = rtosc::Ports::collapsePath(tmp); free(tmp); va_list va; va_start(va, args); //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 4 + 30, 0 + 40); ////fprintf(stderr, "."); //fprintf(stderr, "write(%s:%s)\n", s.c_str(), args); //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40); impl->transmitMsg_va(s.c_str(), args, va); va_end(va); } void writeRaw(const char *msg) override { //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 4 + 30, 0 + 40); ////fprintf(stderr, "."); //fprintf(stderr, "rawWrite(%s:%s)\n", msg, rtosc_argument_string(msg)); //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40); impl->transmitMsg(rtosc::Ports::collapsePath((char*)msg)); } void writeValue(string s, string ss) override { //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 4 + 30, 0 + 40); //fprintf(stderr, "writevalue(%s,%s)\n", s.c_str(),ss.c_str()); //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40); write(s, "s", ss.c_str()); } void writeValue(string s, char c) override { //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 4 + 30, 0 + 40); //fprintf(stderr, "writevalue(%s,%d)\n", s.c_str(),c); //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40); write(s, "c", c); } void writeValue(string s, float f) override { //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 4 + 30, 0 + 40); //fprintf(stderr, "writevalue(%s,%f)\n", s.c_str(),f); //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40); write(s, "f", f); } void createLink(string s, class Fl_Osc_Widget*w) override { char *tmp = strdup(s.c_str()); s = rtosc::Ports::collapsePath(tmp); free(tmp); assert(s.length() != 0); Fl_Osc_Interface::createLink(s,w); assert(!strstr(s.c_str(), "/part0/kit-1")); map.insert(std::pair(s,w)); } void renameLink(string old, string newer, Fl_Osc_Widget *w) override { //fprintf(stdout, "renameLink('%s','%s',%p)\n", // old.c_str(), newer.c_str(), w); removeLink(old, w); createLink(newer, w); } void removeLink(string s, class Fl_Osc_Widget*w) override { char *tmp = strdup(s.c_str()); s = rtosc::Ports::collapsePath(tmp); free(tmp); for(auto i = map.begin(); i != map.end(); ++i) { if(i->first == s && i->second == w) { map.erase(i); break; } } //printf("[%d] removing '%s' (%p)...\n", map.size(), s.c_str(), w); } virtual void removeLink(class Fl_Osc_Widget *w) override { bool processing = true; while(processing) { //Verify Iterator invalidation sillyness processing = false;//Exit if no new elements are found for(auto i = map.begin(); i != map.end(); ++i) { if(i->second == w) { //printf("[%d] removing '%s' (%p)...\n", map.size()-1, // i->first.c_str(), w); map.erase(i); processing = true; break; } } } } //A very simplistic implementation of a UI agnostic refresh method virtual void damage(const char *path) override { #ifndef NO_UI printf("\n\nDamage(\"%s\")\n", path); std::set to_update; for(auto pair:map) { if(strstr(pair.first.c_str(), path)) { auto *tmp = dynamic_cast(pair.second); if(!tmp || tmp->visible_r()) { to_update.insert(pair.second); } } } for(auto elm:to_update) elm->update(); #endif } void tryLink(const char *msg) override { //DEBUG //if(strcmp(msg, "/vu-meter"))//Ignore repeated message // printf("trying the link for a '%s'<%s>\n", msg, rtosc_argument_string(msg)); const char *handle = rindex(msg,'/'); if(handle) ++handle; int found_count = 0; auto range = map.equal_range(msg); for(auto itr = range.first; itr != range.second; ++itr) { auto widget = itr->second; found_count++; const char *arg_str = rtosc_argument_string(msg); //Always provide the raw message widget->OSC_raw(msg); if(!strcmp(arg_str, "b")) { widget->OSC_value(rtosc_argument(msg,0).b.len, rtosc_argument(msg,0).b.data, handle); } else if(!strcmp(arg_str, "c")) { widget->OSC_value((char)rtosc_argument(msg,0).i, handle); } else if(!strcmp(arg_str, "s")) { widget->OSC_value((const char*)rtosc_argument(msg,0).s, handle); } else if(!strcmp(arg_str, "i")) { widget->OSC_value((int)rtosc_argument(msg,0).i, handle); } else if(!strcmp(arg_str, "f")) { widget->OSC_value((float)rtosc_argument(msg,0).f, handle); } else if(!strcmp(arg_str, "T") || !strcmp(arg_str, "F")) { widget->OSC_value((bool)rtosc_argument(msg,0).T, handle); } } if(found_count == 0 && strcmp(msg, "/vu-meter") && strcmp(msg, "undo_change") && !strstr(msg, "parameter") && !strstr(msg, "Prespoint")) { //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 1, 7 + 30, 0 + 40); //fprintf(stderr, "Unknown widget '%s'\n", msg); //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40); } }; void dumpLookupTable(void) { if(!map.empty()) { printf("Leaked controls:\n"); for(auto i = map.begin(); i != map.end(); ++i) { printf("Known control '%s' (%p)...\n", i->first.c_str(), i->second); } } } private: std::multimap map; MiddleWare *impl; }; Fl_Osc_Interface *GUI::genOscInterface(MiddleWare *mw) { return new UI_Interface(mw); }