lv2 phase 2 fixups
authorGood Guy <[email protected]>
Sun, 10 Jun 2018 22:42:45 +0000 (16:42 -0600)
committerGood Guy <[email protected]>
Sun, 10 Jun 2018 22:42:45 +0000 (16:42 -0600)
cinelerra-5.1/cinelerra/forkbase.C
cinelerra-5.1/cinelerra/forkbase.h
cinelerra-5.1/cinelerra/lv2ui.C
cinelerra-5.1/cinelerra/pluginlv2.C
cinelerra-5.1/cinelerra/pluginlv2.h
cinelerra-5.1/cinelerra/pluginlv2client.C
cinelerra-5.1/cinelerra/pluginlv2client.h
cinelerra-5.1/cinelerra/pluginlv2config.h
cinelerra-5.1/cinelerra/pluginlv2gui.C
cinelerra-5.1/cinelerra/pluginlv2ui.C
cinelerra-5.1/cinelerra/pluginlv2ui.h

index 174019ddd6c27f1d07c604b6c08200bfc8103650..5886424dd7dd1b1a7977b93530dab89fc9d30151 100644 (file)
@@ -64,13 +64,6 @@ int ForkChild::is_running()
        return !ppid || !kill(ppid, 0) ? 1 : 0;
 }
 
-int ForkChild::child_iteration()
-{
-       int ret = read_child(100);
-       if( ret <= 0 ) return ret;
-       return handle_child();
-}
-
 void ForkParent::start_child()
 {
        lock("ForkParent::new_child");
@@ -94,7 +87,7 @@ void ForkParent::start_child()
 // Return -1 if the parent is dead
 // Return  0 if timeout
 // Return  1 if success
-int ForkBase::read_timeout(int ms, int fd, void *data, int bytes)
+int ForkBase::read_timeout(int64_t usec, int fd, void *data, int bytes)
 {
        fd_set rfds;
        struct timeval timeout_struct;
@@ -102,8 +95,8 @@ int ForkBase::read_timeout(int ms, int fd, void *data, int bytes)
        uint8_t *bp = (uint8_t *)data;
 
        while( bytes_read < bytes ) {
-               timeout_struct.tv_sec = ms / 1000;
-               timeout_struct.tv_usec = (ms % 1000) * 1000;
+               timeout_struct.tv_sec = usec / 1000000;
+               timeout_struct.tv_usec = usec % 1000000;
                FD_ZERO(&rfds);
                FD_SET(fd, &rfds);
                int result = select(fd+1, &rfds, 0, 0, &timeout_struct);
@@ -126,10 +119,10 @@ int ForkBase::is_running()
        return !pid || !kill(pid, 0) ? 1 : 0;
 }
 
-int ForkBase::read_parent(int ms)
+int ForkBase::read_parent(int64_t usec)
 {
        token_bfr_t bfr;
-       int ret = read_timeout(ms, parent_fd, &bfr, sizeof(bfr));
+       int ret = read_timeout(usec, parent_fd, &bfr, sizeof(bfr));
        if( ret > 0 ) {
                parent_token = bfr.token;
                parent_bytes = bfr.bytes;
@@ -138,7 +131,7 @@ int ForkBase::read_parent(int ms)
                        parent_data = new uint8_t[parent_allocated = parent_bytes];
                }
                if( parent_bytes ) {
-                       ret = read_timeout(1000, parent_fd, parent_data, parent_bytes);
+                       ret = read_timeout(1000000, parent_fd, parent_data, parent_bytes);
                        if( !ret ) {
                                printf("read_parent timeout: %d\n", parent_fd);
                                ret = -1;
@@ -148,10 +141,10 @@ int ForkBase::read_parent(int ms)
        return ret;
 }
 
-int ForkBase::read_child(int ms)
+int ForkBase::read_child(int64_t usec)
 {
        token_bfr_t bfr;
-       int ret = read_timeout(ms, child_fd, &bfr, sizeof(bfr));
+       int ret = read_timeout(usec, child_fd, &bfr, sizeof(bfr));
        if( ret > 0 ) {
                child_token = bfr.token;
                child_bytes = bfr.bytes;
@@ -160,7 +153,7 @@ int ForkBase::read_child(int ms)
                        child_data = new uint8_t[child_allocated = child_bytes];
                }
                if( child_bytes ) {
-                       ret = read_timeout(1000, child_fd, child_data, child_bytes);
+                       ret = read_timeout(1000000, child_fd, child_data, child_bytes);
                        if( !ret ) {
                                printf("read_child timeout: %d\n", child_fd);
                                ret = -1;
@@ -205,23 +198,17 @@ int ForkBase::send_child(int64_t token, const void *data, int bytes)
 
 ForkChild::ForkChild()
 {
-       done = 0;
+       parent_done = 0;
 }
 
 ForkChild::~ForkChild()
 {
 }
 
-int ForkChild::handle_child()
-{
-       printf("ForkChild::handle_child %d\n", __LINE__);
-       return 0;
-}
-
 ForkParent::ForkParent()
  : Thread(1, 0, 0)
 {
-       done = -1;
+       parent_done = -1;
 }
 
 ForkParent::~ForkParent()
@@ -253,7 +240,7 @@ int ForkParent::handle_parent()
 
 void ForkParent::start()
 {
-       done = 0;
+       parent_done = 0;
        Thread::start();
 }
 
@@ -270,7 +257,7 @@ void ForkParent::stop()
 
 void ForkParent::run()
 {
-       while( !done && parent_iteration() >= 0 );
-       done = 1;
+       while( !parent_done && parent_iteration() >= 0 );
+       parent_done = 1;
 }
 
index 2ca84b71d4c6c56ffc71db925e0840de2ac642ff..6b09a5c24361170012f001dee3c4899439b2749b 100644 (file)
@@ -41,13 +41,15 @@ public:
 
        virtual int is_running() = 0;
        void send_bfr(int fd, const void *bfr, int len);
-       int read_timeout(int ms, int fd, void *data, int bytes);
-       int read_parent(int ms);
+       int read_timeout(int64_t usec, int fd, void *data, int bytes);
+
+       int read_parent(int64_t usec);
        int send_parent(int64_t value, const void *data, int bytes);
-       int read_child(int ms);
+       int read_child(int64_t usec);
        int send_child(int64_t value, const void *data, int bytes);
 
-       int done, ppid, pid;
+       int parent_done;
+       int ppid, pid;
        ForkChild *child;
 
        int child_fd;
@@ -68,8 +70,7 @@ class ForkChild : public ForkBase
 public:
        ForkChild();
        virtual ~ForkChild();
-       virtual int handle_child();
-       int child_iteration();
+       virtual int child_iteration(int64_t usec) = 0;
        int is_running();
        virtual void run() {}
 };
index 3a06102a4926ff4c1ab3f97a4cd94316d1272c9c..3ea5ba170f8ba006a2bddaca2ea58fef9c51df68 100644 (file)
@@ -8,28 +8,6 @@
 #include "pluginlv2client.h"
 #include "pluginlv2ui.h"
 
-int PluginLV2UI::run(int ac, char **av)
-{
-       int sample_rate = 48000;
-       if( ac > 2 ) sample_rate = atoi(av[2]);
-       if( init_ui(av[1], sample_rate) ) {
-               fprintf(stderr," init_ui failed\n");
-               return 1;
-       }
-       start_gui();
-       return run_ui();
-}
-
-int PluginLV2ChildUI::run(int ac, char **av)
-{
-       signal(SIGINT, SIG_IGN);
-       ForkBase::child_fd = atoi(av[1]);
-       ForkBase::parent_fd = atoi(av[2]);
-       ForkBase::ppid = atoi(av[3]);
-       return run_ui(this);
-}
-
-
 int main(int ac, char **av)
 {
 // to grab this task in the debugger
@@ -41,10 +19,29 @@ while( bug ) usleep(10000);
                signals.initialize("/tmp/lv2ui_%d.dmp");
                BC_Signals::set_catch_segv(1);
        }
-       gtk_set_locale();
-       gtk_init(&ac, &av);
-       return ac < 3 ?
-               PluginLV2UI().run(ac, av) :
-               PluginLV2ChildUI().run(ac, av);
+       return PluginLV2ChildUI().run(ac, av);
+}
+
+int PluginLV2ChildUI::run(int ac, char **av)
+{
+       this->ac = ac;
+       this->av = av;
+
+       if( ac > 3 ) {
+               signal(SIGINT, SIG_IGN);
+               ForkBase::child_fd = atoi(av[1]);
+               ForkBase::parent_fd = atoi(av[2]);
+               ForkBase::ppid = atoi(av[3]);
+       }
+       else {
+               int sample_rate = 48000;
+               if( ac > 2 ) sample_rate = atoi(av[2]);
+               if( init_ui(av[1], sample_rate) ) {
+                       fprintf(stderr," init_ui failed\n");
+                       return 1;
+               }
+               start_gui();
+       }
+       return run_ui();
 }
 
index bbfd76eaae7e3771cc6ca68fdb9806b180184f31..4ec32648ae3441d44048cee56fc570852b736473 100644 (file)
@@ -20,6 +20,11 @@ PluginLV2::PluginLV2()
        max_bufsz = 0;
        ui_features = 0;
 
+       samplerate = 44100;
+       refreshrate = 30.;
+       block_length = 4096;
+       midi_buf_size = 8192;
+
        world = 0;
        lilv = 0;
        lilv_uis = 0;
@@ -39,16 +44,29 @@ PluginLV2::PluginLV2()
        fixedBlockLength = 0;
        boundedBlockLength = 0;
        seq_out = 0;
+
+       worker_thread = 0;
+       memset(&schedule, 0, sizeof(schedule));
+       schedule.handle = (LV2_Worker_Schedule_Handle)this;
+       schedule.schedule_work = lv2_worker_schedule;
+       worker_iface = 0;  worker_done = -1;
+       pthread_mutex_init(&worker_lock, 0);
+       pthread_cond_init(&worker_ready, 0);
+       work_avail = 0;   work_input = 0;
+       work_output = 0;  work_tail = &work_output;
 }
 
 PluginLV2::~PluginLV2()
 {
        reset_lv2();
        if( world )     lilv_world_free(world);
+       pthread_mutex_destroy(&worker_lock);
+       pthread_cond_destroy(&worker_ready);
 }
 
 void PluginLV2::reset_lv2()
 {
+       worker_stop();
        if( inst ) lilv_instance_deactivate(inst);
        lilv_instance_free(inst);             inst = 0;
        lilv_uis_free(lilv_uis);              lilv_uis = 0;
@@ -94,7 +112,7 @@ int PluginLV2::load_lv2(const char *path, char *title)
        lilv = lilv_plugins_get_by_uri(all_plugins, uri);
        lilv_node_free(uri);
        if( !lilv ) {
-               printf("lv2: lilv_plugins_get_by_uriPlugin(%s) failed\n", path);
+               printf("lv2: lilv_plugins_get_by_uri (%s) failed\n", path);
                return 1;
        }
 
@@ -147,6 +165,10 @@ int PluginLV2::init_lv2(PluginLV2ClientConfig &conf, int sample_rate)
                }
        }
 
+
+       uri_map.callback_data = (LV2_URI_Map_Callback_Data)this;
+       uri_map.uri_to_id = uri_to_id;
+       features.append(new Lv2Feature(NS_EXT "uri-map", &uri_map));
        map.handle = (void*)&uri_table;
        map.map = uri_table_map;
        features.append(new Lv2Feature(LV2_URID_MAP_URI, &map));
@@ -156,15 +178,41 @@ int PluginLV2::init_lv2(PluginLV2ClientConfig &conf, int sample_rate)
        features.append(new Lv2Feature(LV2_BUF_SIZE__powerOf2BlockLength, 0));
        features.append(new Lv2Feature(LV2_BUF_SIZE__fixedBlockLength,    0));
        features.append(new Lv2Feature(LV2_BUF_SIZE__boundedBlockLength,  0));
+       features.append(new Lv2Feature(LV2_WORKER__schedule, &schedule));
+
+       if( sample_rate < 64 ) sample_rate = 44100;
+
+       atom_int   = uri_table.map(LV2_ATOM__Int);
+       atom_float = uri_table.map(LV2_ATOM__Float);
+       param_sampleRate = uri_table.map(LV2_PARAMETERS__sampleRate);
+       bufsz_minBlockLength =  uri_table.map(LV2_BUF_SIZE__minBlockLength);
+       bufsz_maxBlockLength = uri_table.map(LV2_BUF_SIZE__maxBlockLength);
+       bufsz_sequenceSize =  uri_table.map(LV2_BUF_SIZE__sequenceSize);
+       ui_updateRate = uri_table.map(LV2_UI__updateRate);
+
+       samplerate = sample_rate;
+       options.add(param_sampleRate, sizeof(float), atom_float, &samplerate);
+       options.add(bufsz_minBlockLength, sizeof(int), atom_int, &block_length);
+       options.add(bufsz_maxBlockLength, sizeof(int), atom_int, &block_length);
+       options.add(bufsz_sequenceSize, sizeof(int), atom_int, &midi_buf_size);
+       options.add(ui_updateRate, sizeof(float),  atom_float, &refreshrate);
+       options.add(0, 0, 0, 0);
+
+       features.append(new Lv2Feature(LV2_OPTIONS__options,  &options[0]));
        features.append(0);
 
-       if( sample_rate < 64 ) sample_rate = 64;
        inst = lilv_plugin_instantiate(lilv, sample_rate, features);
        if( !inst ) {
                printf("lv2: lilv_plugin_instantiate failed\n");
                return 1;
        }
 
+       const LV2_Descriptor *lilv_desc = inst->lv2_descriptor;
+       worker_iface = !lilv_desc->extension_data ? 0 :
+               (LV2_Worker_Interface*)lilv_desc->extension_data(LV2_WORKER__interface);
+       if( worker_iface )
+               worker_start();
+
        lilv_instance_activate(inst);
 // not sure what to do with these
        max_bufsz = nb_inputs &&
@@ -184,6 +232,13 @@ const char *PluginLV2::uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid
        return ((PluginLV2UriTable *)handle)->unmap(urid);
 }
 
+uint32_t PluginLV2::uri_to_id(LV2_URI_Map_Callback_Data callback_data,
+       const char *map, const char *uri)
+{
+       PluginLV2 *the = (PluginLV2 *)callback_data;
+       return the->map.map(the->uri_table, uri);
+}
+
 void PluginLV2::connect_ports(PluginLV2ClientConfig &conf, int ports)
 {
        int ich = 0, och = 0;
@@ -210,7 +265,7 @@ void PluginLV2::connect_ports(PluginLV2ClientConfig &conf, int ports)
                if( (port & PORTS_ATOM) ) {
                        if( (port & PORTS_INPUT) )
                                lilv_instance_connect_port(inst, i, &seq_in);
-                       else if( (port & PORTS_OUTPUT) )
+                       else
                                lilv_instance_connect_port(inst, i, seq_out);
                        continue;
                }
@@ -330,16 +385,157 @@ void PluginLV2::map_buffer()
 }
 
 
+// LV2 Worker
+
+PluginLV2Work::PluginLV2Work()
+{
+       next = 0;
+       alloc = used = 0;
+       data = 0;
+}
+
+PluginLV2Work::~PluginLV2Work()
+{
+       delete [] data;
+}
+
+void PluginLV2Work::load(const void *vp, unsigned size)
+{
+       if( alloc < size ) {
+               delete [] data;
+               data = new char[alloc=size];
+       }
+       memcpy(data, vp, used=size);
+}
+
+PluginLV2Work *PluginLV2::get_work()
+{
+// must hold worker_lock
+       if( !work_avail ) return new PluginLV2Work();
+       PluginLV2Work *wp = work_avail;
+       work_avail = wp->next;  wp->next = 0;
+       return wp;
+}
+
+void *PluginLV2::worker_func()
+{
+       pthread_mutex_lock(&worker_lock);
+       for(;;) {
+               while( !worker_done && !work_input )
+                       pthread_cond_wait(&worker_ready, &worker_lock);
+               if( worker_done ) break;
+               PluginLV2Work *wp = work_input;  work_input = wp->next;
+
+               pthread_mutex_unlock(&worker_lock);
+               worker_iface->work(inst, lv2_worker_respond, this, wp->used, wp->data);
+               pthread_mutex_lock(&worker_lock);
+               wp->next = work_avail;  work_avail = wp;
+       }
+       pthread_mutex_unlock(&worker_lock);
+       return NULL;
+}
+void *PluginLV2::worker_func(void* vp)
+{
+       PluginLV2 *the = (PluginLV2 *)vp;
+       return the->worker_func();
+}
+
+void PluginLV2::worker_start()
+{
+       pthread_create(&worker_thread, 0, worker_func, this);
+}
+
+void PluginLV2::worker_stop()
+{
+       if( !worker_done ) {
+               worker_done = 1;
+               pthread_mutex_lock(&worker_lock);
+               pthread_cond_signal(&worker_ready);
+               pthread_mutex_unlock(&worker_lock);
+               pthread_join(worker_thread, 0);
+               worker_thread = 0;
+       }
+       work_stop(work_avail);
+       work_stop(work_input);
+       work_stop(work_output);
+       work_tail = &work_output;
+}
+
+void PluginLV2::work_stop(PluginLV2Work *&work)
+{
+       while( work ) {
+               PluginLV2Work *wp = work;
+               work = wp->next;
+               delete wp;
+       }
+}
+
+LV2_Worker_Status PluginLV2::worker_schedule(uint32_t inp_size, const void *inp_data)
+{
+       if( is_forked() ) {
+               if( !pthread_mutex_trylock(&worker_lock) ) {
+                       PluginLV2Work *wp = get_work();
+                       wp->load(inp_data, inp_size);
+                       wp->next = work_input;  work_input = wp;
+                       pthread_cond_signal(&worker_ready);
+                       pthread_mutex_unlock(&worker_lock);
+               }
+       }
+       else if( worker_iface )
+               worker_iface->work(inst, lv2_worker_respond, this, inp_size, inp_data);
+       return LV2_WORKER_SUCCESS;
+}
+LV2_Worker_Status PluginLV2::lv2_worker_schedule(LV2_Worker_Schedule_Handle vp,
+                    uint32_t inp_size, const void *inp_data)
+{
+       PluginLV2 *the = (PluginLV2 *)vp;
+       return the->worker_schedule(inp_size, inp_data);
+}
+
+LV2_Worker_Status PluginLV2::worker_respond(uint32_t out_size, const void *out_data)
+{
+       pthread_mutex_lock(&worker_lock);
+       PluginLV2Work *wp = get_work();
+       wp->load(out_data, out_size);
+       *work_tail = wp;  work_tail = &wp->next;
+       pthread_mutex_unlock(&worker_lock);
+       return LV2_WORKER_SUCCESS;
+}
+LV2_Worker_Status PluginLV2::lv2_worker_respond(LV2_Worker_Respond_Handle vp,
+               uint32_t out_size, const void *out_data)
+{
+       PluginLV2 *the = (PluginLV2 *)vp;
+       return the->worker_respond(out_size, out_data);
+}
+
+void PluginLV2::worker_responses()
+{
+       pthread_mutex_lock(&worker_lock);
+       while( work_output ) {
+               PluginLV2Work *rp = work_output;
+               if( !(work_output=rp->next) ) work_tail = &work_output;
+               pthread_mutex_unlock(&worker_lock);
+               worker_iface->work_response(inst, rp->used, rp->data);
+               pthread_mutex_lock(&worker_lock);
+               rp->next = work_avail;  work_avail = rp;
+       }
+       pthread_mutex_unlock(&worker_lock);
+}
+
 #include "file.h"
 #include "pluginlv2ui.h"
 
 PluginLV2ChildUI::PluginLV2ChildUI()
- : ForkChild()
 {
+       ac = 0;
+       av = 0;
+       gui = 0;
+       hidden = 1;
 }
 
 PluginLV2ChildUI::~PluginLV2ChildUI()
 {
+       delete gui;
 }
 
 void PluginLV2ChildUI::run()
@@ -363,29 +559,28 @@ void PluginLV2ChildUI::run()
        _exit(1);
 }
 
+#define LV2_EXTERNAL_UI_URI__KX__Widget "http://kxstudio.sf.net/ns/lv2ext/external-ui#Widget"
 
 PluginLV2UI::PluginLV2UI()
- : PluginLV2()
 {
        lilv_ui = 0;
        lilv_type = 0;
-       uri_map = 0;
 
        done = -1;
        running = 0;
        updates = 0;
        hidden = 1;
        title[0] = 0;
-       child = 0;
 
-// only gtk-2
-       gtk_type = "http://lv2plug.in/ns/extensions/ui#GtkUI";
-       top_level = 0;
+       memset(&uri_map, 0, sizeof(uri_map));
+       memset(&extui_host, 0, sizeof(extui_host));
+       wgt_type = LV2_EXTERNAL_UI_URI__KX__Widget;
+       gtk_type = LV2_UI__GtkUI;
+       ui_type = 0;
 }
 
 PluginLV2UI::~PluginLV2UI ()
 {
-       reset_gui();
 }
 
 #endif
index 01fc461f94baa8f61f9d701c05dd2602b3cf5a42..9cd516a084e8fa4c2ac3f39e1b6dbba854823ee4 100644 (file)
@@ -23,6 +23,30 @@ typedef struct {
 #define PORTS_OUTPUT  0x10
 #define PORTS_UPDATE  0x20
 
+class PluginLV2Options : public ArrayList<LV2_Options_Option>
+{
+public:
+       PluginLV2Options() {}
+       ~PluginLV2Options() {}
+       void add(LV2_URID key, unsigned sz, LV2_URID typ, void *vp) {
+               LV2_Options_Option *ap = &append();
+               ap->context = LV2_OPTIONS_INSTANCE; ap->subject = 0;
+               ap->key = key; ap->size = sz; ap->type = typ; ap->value = vp;
+       }
+};
+
+class PluginLV2Work
+{
+public:
+       PluginLV2Work();
+       ~PluginLV2Work();
+       void load(const void *vp, unsigned size);
+
+       PluginLV2Work *next;
+       unsigned alloc, used;
+       char *data;
+};
+
 class PluginLV2
 {
 public:
@@ -39,9 +63,12 @@ public:
        void reset_lv2();
        int load_lv2(const char *path,char *title=0);
        int init_lv2(PluginLV2ClientConfig &conf, int sample_rate);
+       virtual int is_forked() { return 0; }
 
        static LV2_URID uri_table_map(LV2_URID_Map_Handle handle, const char *uri);
        static const char *uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid);
+       static uint32_t uri_to_id(LV2_URI_Map_Callback_Data callback_data,
+                       const char *map, const char *uri);
        void connect_ports(PluginLV2ClientConfig &conf, int ports);
        void del_buffer();
        void new_buffer(int64_t sz);
@@ -53,14 +80,19 @@ public:
        const LilvPlugin  *lilv;
        LilvUIs           *lilv_uis;
 
+       LV2_URI_Map_Feature uri_map;
        PluginLV2UriTable  uri_table;
        LV2_URID_Map       map;
        LV2_Feature        map_feature;
        LV2_URID_Unmap     unmap;
        LV2_Feature        unmap_feature;
+
+       PluginLV2Options   options;
        Lv2Features        features;
        LV2_Atom_Sequence  seq_in[2];
        LV2_Atom_Sequence  *seq_out;
+       float samplerate, refreshrate;
+       int block_length, midi_buf_size;
 
        LilvInstance *inst;
        SuilInstance *sinst;
@@ -77,6 +109,38 @@ public:
        LilvNode *powerOf2BlockLength;
        LilvNode *fixedBlockLength;
        LilvNode *boundedBlockLength;
+
+       LV2_URID atom_int;
+       LV2_URID atom_float;
+
+       LV2_URID param_sampleRate;
+       LV2_URID bufsz_minBlockLength;
+       LV2_URID bufsz_maxBlockLength;
+       LV2_URID bufsz_sequenceSize;
+       LV2_URID ui_updateRate;
+
+       pthread_t worker_thread;
+       LV2_Worker_Interface *worker_iface;
+       static void *worker_func(void *vp);
+       void *worker_func();
+       void worker_start();
+       void worker_stop();
+       LV2_Worker_Status worker_schedule(uint32_t inp_size, const void *inp_data);
+       static LV2_Worker_Status lv2_worker_schedule(LV2_Worker_Schedule_Handle vp,
+                       uint32_t inp_size, const void *inp_data);
+       LV2_Worker_Status worker_respond(uint32_t out_size, const void *out_data);
+       static LV2_Worker_Status lv2_worker_respond(LV2_Worker_Respond_Handle vp,
+                       uint32_t out_size, const void *out_data);
+       PluginLV2Work *get_work();
+       void work_stop(PluginLV2Work *&work);
+       void worker_responses();
+
+       LV2_Worker_Schedule schedule;
+       PluginLV2Work *work_avail, *work_input;
+       PluginLV2Work *work_output, **work_tail;
+       pthread_mutex_t worker_lock;
+       pthread_cond_t worker_ready;
+       int worker_done;
 };
 
 typedef struct { int sample_rate;  char path[1]; } open_bfr_t;
index 7ccb6ef8f2aad633d36c2a0c931fc7034c4872b0..6163f2dfd9867e1c7e16550d465acc15e51c785a 100644 (file)
@@ -307,6 +307,36 @@ int PluginLV2Client::process_realtime(int64_t size,
 }
 
 
+PluginLV2BlackList::PluginLV2BlackList(const char *path)
+{
+       set_array_delete();
+       char lv2_blacklist_path[BCTEXTLEN];
+       sprintf(lv2_blacklist_path, "%s/%s", File::get_cindat_path(), path);
+       FILE *bfp = fopen(lv2_blacklist_path, "r");
+       if( !bfp ) return;
+       while( fgets(lv2_blacklist_path, sizeof(lv2_blacklist_path), bfp) ) {
+               if( lv2_blacklist_path[0] == '#' ) continue;
+               int len = strlen(lv2_blacklist_path);
+               if( len > 0 && lv2_blacklist_path[len-1] == '\n' )
+                       lv2_blacklist_path[len-1] = 0;
+               if( !lv2_blacklist_path[0] ) continue;
+               append(cstrdup(lv2_blacklist_path));
+       }
+       fclose(bfp);
+}
+
+PluginLV2BlackList::~PluginLV2BlackList()
+{
+       remove_all_objects();
+}
+
+int PluginLV2BlackList::is_badboy(const char *uri)
+{
+       for( int i=size(); --i>=0; )
+               if( !strcmp(uri, get(i)) ) return 1;
+       return 0;
+}
+
 PluginServer* MWindow::new_lv2_server(MWindow *mwindow, const char *name)
 {
        return new PluginServer(mwindow, name, PLUGIN_TYPE_LV2);
@@ -323,6 +353,8 @@ PluginClient *PluginServer::new_lv2_plugin()
 int MWindow::init_lv2_index(MWindow *mwindow, Preferences *preferences, FILE *fp)
 {
        printf("init lv2 index:\n");
+       PluginLV2BlackList blacklist("lv2_blacklist.txt");
+
        LilvWorld *world = lilv_world_new();
        lilv_world_load_all(world);
        const LilvPlugins *all_plugins = lilv_world_get_all_plugins(world);
@@ -330,6 +362,8 @@ int MWindow::init_lv2_index(MWindow *mwindow, Preferences *preferences, FILE *fp
        LILV_FOREACH(plugins, i, all_plugins) {
                const LilvPlugin *lilv = lilv_plugins_get(all_plugins, i);
                const char *uri = lilv_node_as_uri(lilv_plugin_get_uri(lilv));
+               if( blacklist.is_badboy(uri) ) continue;
+printf("LOAD: %s\n", uri);
                PluginServer server(mwindow, uri, PLUGIN_TYPE_LV2);
                int result = server.open_plugin(1, preferences, 0, 0);
                if( !result ) {
@@ -424,16 +458,14 @@ int PluginLV2ParentUI::hide()
        return 0;
 }
 
-
-// stub in parent
-int PluginLV2ChildUI::handle_child() { return 0; }
-void PluginLV2UI::reset_gui() {}
-
 ForkChild *PluginLV2ParentUI::new_fork()
 {
        return new PluginLV2ChildUI();
 }
 
+// stub in parent
+int PluginLV2ChildUI::child_iteration(int64_t usec) { return -1; }
+int PluginLV2ChildUI::send_host(int64_t token, const void *data, int bytes) { return -1; }
 
 #else
 #include "mwindow.h"
index da703e8ede7cbba8cf5902e1c5ea24ae52ed04ad..1f5f3b356b426b6d962c287e61d27a567d804ca1 100644 (file)
@@ -53,6 +53,15 @@ public:
        static PluginLV2UIs plugin_lv2;
 };
 
+class PluginLV2BlackList : public ArrayList<const char *>
+{
+public:
+       PluginLV2BlackList(const char *path);
+       ~PluginLV2BlackList();
+
+       int is_badboy(const char *uri);
+};
+
 class PluginLV2Client : public PluginAClient, public PluginLV2
 {
 public:
index efe8d1c9c31b553f7b1f603c91c4edbac3b09650..5f4f41d6d09284d265a3fc1f508fc5b1feb4a54a 100644 (file)
@@ -43,6 +43,7 @@
 #include <lv2/lv2plug.in/ns/ext/worker/worker.h>
 #include <lv2/lv2plug.in/ns/ext/log/log.h>
 #include <lv2/lv2plug.in/ns/ext/options/options.h>
+#include <lv2/lv2plug.in/ns/ext/parameters/parameters.h>
 #include <lv2/lv2plug.in/ns/ext/buf-size/buf-size.h>
 #include <lv2/lv2plug.in/ns/ext/uri-map/uri-map.h>
 #include <lv2/lv2plug.in/ns/ext/data-access/data-access.h>
@@ -71,6 +72,7 @@ public:
        ~PluginLV2UriTable();
        LV2_URID map(const char *uri);
        const char *unmap(LV2_URID urid);
+       operator LV2_URID_Map_Handle() { return (LV2_URID_Map_Handle)this; }
 };
 
 #endif
index 3c0c45663fb946d5e6db5f59025d4fb2a64ec7c7..7d9f0e9c9671268af48b6b6a7d0c2b3397b7321f 100644 (file)
@@ -229,7 +229,6 @@ void PluginLV2ClientWindow::create_objects()
        add_subwindow(slider = new PluginLV2ClientSlider(this, x1, y+10));
        y += pot->get_h() + 10;
 
-       client->init_lv2();
        client->load_configuration();
        client->config.update();
 
index 929b5c676f4123669ca7fe6aa808c096e1df855e..b8f99ab1318d1ada59f5a20cfbdc4c91adbd1702 100644 (file)
@@ -9,39 +9,6 @@
 #include <ctype.h>
 #include <string.h>
 
-int PluginLV2UI::init_ui(const char *path, int sample_rate)
-{
-       if( load_lv2(path, title) ) return 1;
-       if( init_lv2(config, sample_rate) ) return 1;
-
-       lilv_uis = lilv_plugin_get_uis(lilv);
-       if( !lilv_uis ) {
-               printf("lv2: lilv_plugin_get_uis(%s) failed\n", path);
-               return 1;
-       }
-
-       if( gtk_type ) {
-               LilvNode *gui_type = lilv_new_uri(world, gtk_type);
-               LILV_FOREACH(uis, i, lilv_uis) {
-                       const LilvUI *ui = lilv_uis_get(lilv_uis, i);
-                       if( lilv_ui_is_supported(ui, suil_ui_supported, gui_type, &lilv_type)) {
-                               lilv_ui = ui;
-                               break;
-                       }
-               }
-               lilv_node_free(gui_type);
-       }
-       if( !lilv_ui )
-               lilv_ui = lilv_uis_get(lilv_uis, lilv_uis_begin(lilv_uis));
-       if( !lilv_ui ) {
-               printf("lv2_gui: init_ui failed: %s\n", title);
-               return 1;
-       }
-
-       lilv_instance_activate(inst);
-       return 0;
-}
-
 void PluginLV2UI::update_value(int idx, uint32_t bfrsz, uint32_t typ, const void *bfr)
 {
        if( idx >= config.nb_ports ) return;
@@ -49,7 +16,7 @@ void PluginLV2UI::update_value(int idx, uint32_t bfrsz, uint32_t typ, const void
                PluginLV2Client_Opt *opt = config[i];
                if( opt->idx == idx ) {
                        opt->set_value(*(const float*)bfr);
-                       updates = UPDATE_HOST;
+                       updates |= UPDATE_HOST;
                        break;
                }
        }
@@ -77,17 +44,6 @@ void PluginLV2UI::update_control(int idx, uint32_t bfrsz, uint32_t typ, const vo
 }
 
 
-#if 0
-void PluginLV2UI::touch(void *obj, uint32_t pidx, bool grabbed)
-{
-       PluginLV2UI* the = (PluginLV2GUI*)obj;
-       int idx = pidx;
-       if( idx >= the->config.nb_ports ) return;
-printf("%s %s(%u)\n", (grabbed? _("press") : _("release")),
-  the->config.names[idx], idx);
-}
-#endif
-
 uint32_t PluginLV2UI::uri_to_id(LV2_URID_Map_Handle handle, const char *map, const char *uri)
 {
        return ((PluginLV2UriTable *)handle)->map(uri);
@@ -110,8 +66,7 @@ void PluginLV2UI::lv2ui_instantiate(void *parent)
                        PluginLV2UI::write_from_ui,
                        PluginLV2UI::port_index,
                        0, 0);
-//             suil_host_set_touch_func(ui_host,
-//                     PluginLV2GUI::touch);
+//             suil_host_set_touch_func(ui_host, cb_touch);
        }
 
        features.remove();  // remove terminating zero
@@ -124,13 +79,15 @@ void PluginLV2UI::lv2ui_instantiate(void *parent)
        features.append(new Lv2Feature(LV2_DATA_ACCESS_URI, ext_data));
        features.append(new Lv2Feature(LV2_UI__parent, parent));
        features.append(new Lv2Feature(LV2_UI__idleInterface, 0));
+       features.append(new Lv2Feature(LV2_EXTERNAL_UI_URI, &extui_host));
+       features.append(new Lv2Feature(LV2_EXTERNAL_UI_URI__KX__Host, &extui_host));
        features.append(0); // add new terminating zero
 
        const char* bundle_uri  = lilv_node_as_uri(lilv_ui_get_bundle_uri(lilv_ui));
        char*       bundle_path = lilv_file_uri_parse(bundle_uri, NULL);
        const char* binary_uri  = lilv_node_as_uri(lilv_ui_get_binary_uri(lilv_ui));
        char*       binary_path = lilv_file_uri_parse(binary_uri, NULL);
-       sinst = suil_instance_new(ui_host, this, gtk_type,
+       sinst = suil_instance_new(ui_host, this, ui_type,
                lilv_node_as_uri(lilv_plugin_get_uri(lilv)),
                lilv_node_as_uri(lilv_ui_get_uri(lilv_ui)),
                lilv_node_as_uri(lilv_type),
@@ -157,11 +114,6 @@ bool PluginLV2UI::lv2ui_resizable()
        return !fs_matches && !nrs_matches;
 }
 
-int PluginLV2UI::send_host(int64_t token, const void *data, int bytes)
-{
-       return !child ? 0 : child->send_parent(token, data, bytes);
-}
-
 int PluginLV2UI::update_lv2_input(float *vals, int force)
 {
        int ret = 0;
@@ -188,37 +140,31 @@ void PluginLV2UI::update_lv2_output()
        }
 }
 
-static void lilv_destroy(GtkWidget* widget, gpointer data)
+void PluginLV2UI::run_lilv(int samples)
 {
-       PluginLV2UI *the = (PluginLV2UI*)data;
-       the->top_level = 0;
-}
+       float ctls[config.nb_ports];
+       for( int i=0; i<config.nb_ports; ++i ) ctls[i] = config.ctls[i];
 
-void PluginLV2UI::start_gui()
-{
-       if( !hidden ) return;
-       top_level = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-       g_signal_connect(top_level, "destroy", G_CALLBACK(lilv_destroy), this);
-       gtk_window_set_title(GTK_WINDOW(top_level), title);
+       lilv_instance_run(inst, samples);
 
-       GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
-       gtk_window_set_role(GTK_WINDOW(top_level), "plugin_ui");
-       gtk_container_add(GTK_CONTAINER(top_level), vbox);
+       if( worker_iface ) worker_responses();
 
-       GtkWidget *alignment = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
-       gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
-       gtk_widget_show(alignment);
-       lv2ui_instantiate(alignment);
-       GtkWidget* widget = (GtkWidget*)suil_instance_get_widget(sinst);
-       gtk_container_add(GTK_CONTAINER(alignment), widget);
-       gtk_window_set_resizable(GTK_WINDOW(top_level), lv2ui_resizable());
-       gtk_widget_show_all(vbox);
-       gtk_widget_grab_focus(widget);
+       for( int i=0; i<config.nb_ports; ++i ) {
+               if( !(config.ports[i] & PORTS_OUTPUT) ) continue;
+               if( !(config.ports[i] & PORTS_CONTROL) ) continue;
+               if( config.ctls[i] == ctls[i] ) continue;
+               config.ports[i] |= PORTS_UPDATE;
+               updates |= UPDATE_PORTS;
+       }
+}
+
+void PluginLV2ChildUI::start_gui()
+{
+       gui->start_gui();
        update_lv2_input(config.ctls, 1);
-       connect_ports(config, PORTS_CONTROL);
+       connect_ports(config, PORTS_CONTROL | PORTS_ATOM);
        updates = 0;
        run_lilv(0);
-       gtk_window_present(GTK_WINDOW(top_level));
        send_host(LV2_SHOW, 0, 0);
        hidden = 0;
 }
@@ -230,33 +176,89 @@ void PluginLV2UI::update_host()
        send_host(LV2_UPDATE, config.ctls, sizeof(float)*config.nb_ports);
 }
 
-void PluginLV2UI::reset_gui()
+
+static void lv2ui_extui_host_ui_closed(void *p)
+{
+       PluginLV2UI *ui = (PluginLV2UI *)p;
+       ui->hidden = -1;
+}
+
+int PluginLV2ChildUI::init_ui(const char *path, int sample_rate)
+{
+       char *title = PluginLV2UI::title;
+       if( load_lv2(path, title) ) return 1;
+       if( init_lv2(config, sample_rate) ) return 1;
+
+       lilv_uis = lilv_plugin_get_uis(lilv);
+       if( !lilv_uis ) {
+               printf("lv2: lilv_plugin_get_uis(%s) failed\n", path);
+               return 1;
+       }
+
+       if( !gui && wgt_type ) {
+               LilvNode *gui_type = lilv_new_uri(world, wgt_type);
+               LILV_FOREACH(uis, i, lilv_uis) {
+                       const LilvUI *ui = lilv_uis_get(lilv_uis, i);
+                       if( lilv_ui_is_supported(ui, suil_ui_supported, gui_type, &lilv_type)) {
+                               extui_host.ui_closed = lv2ui_extui_host_ui_closed;
+                               gui = new PluginLV2ChildWgtUI(this);
+                               lilv_ui = ui;
+                               ui_type = wgt_type;
+                               break;
+                       }
+               }
+               lilv_node_free(gui_type);
+       }
+       if( !gui && gtk_type ) {
+               LilvNode *gui_type = lilv_new_uri(world, gtk_type);
+               LILV_FOREACH(uis, i, lilv_uis) {
+                       const LilvUI *ui = lilv_uis_get(lilv_uis, i);
+                       if( lilv_ui_is_supported(ui, suil_ui_supported, gui_type, &lilv_type)) {
+                               gui = new PluginLV2ChildGtkUI(this);
+                               lilv_ui = ui;
+                               ui_type = gtk_type;
+                               break;
+                       }
+               }
+               lilv_node_free(gui_type);
+               ui_type = gtk_type;
+       }
+
+       if( !gui ) {
+               printf("lv2_gui: init_ui failed: %s\n", title);
+               return 1;
+       }
+
+       lilv_instance_activate(inst);
+       return 0;
+}
+
+void PluginLV2ChildUI::reset_gui()
 {
-       if( hidden ) return;
+       gui->reset_gui();
        if( sinst )     { suil_instance_free(sinst);  sinst = 0; }
        if( ui_host )   { suil_host_free(ui_host);    ui_host = 0; }
-       if( top_level ) { gtk_widget_destroy(top_level); top_level = 0; }
 
        while( features.size() > ui_features ) features.remove_object();
        features.append(0);
-       hidden = 1;
        send_host(LV2_HIDE, 0, 0);
+       hidden = 1;
 }
 
-
-// child main
-int PluginLV2UI::run_ui(PluginLV2ChildUI *child)
+// main loop
+int PluginLV2ChildUI::run_ui()
 {
-       this->child = child;
+       double last_time = 0, fps = 30;
+       int64_t frame_usecs = 1e6 / fps;
        running = 1;
        done = 0;
        while( !done ) {
-               if( gtk_events_pending() ) {
-                       gtk_main_iteration();
-                       continue;
-               }
-               if( !top_level && !hidden )
+               if( hidden < 0 ) {
+                       gui->top_level = 0;
                        reset_gui();
+                       done = -1;
+                       break;
+               }
                if( updates ) {
                        if( (updates & UPDATE_PORTS) )
                                update_lv2_output();
@@ -264,30 +266,18 @@ int PluginLV2UI::run_ui(PluginLV2ChildUI *child)
                                update_host();
                        updates = 0;
                }
-               if( !child ) usleep(10000);
-               else if( child->child_iteration() < 0 )
+               struct timeval tv;  gettimeofday(&tv, 0);
+               double t = tv.tv_sec + tv.tv_usec / 1e6;
+               double dt = t - last_time;  last_time = t;
+               int64_t usec = frame_usecs - dt*1e6;
+               if( usec < 0 ) usec = 0;
+               if( child_iteration(usec) < 0 )
                        done = 1;
        }
        running = 0;
        return 0;
 }
 
-void PluginLV2UI::run_lilv(int samples)
-{
-       float ctls[config.nb_ports];
-       for( int i=0; i<config.nb_ports; ++i ) ctls[i] = config.ctls[i];
-
-       lilv_instance_run(inst, samples);
-
-       for( int i=0; i<config.nb_ports; ++i ) {
-               if( !(config.ports[i] & PORTS_OUTPUT) ) continue;
-               if( !(config.ports[i] & PORTS_CONTROL) ) continue;
-               if( config.ctls[i] == ctls[i] ) continue;
-               config.ports[i] |= PORTS_UPDATE;
-               updates |= UPDATE_PORTS;
-       }
-}
-
 void PluginLV2ChildUI::run_buffer(int shmid)
 {
        if( !shm_buffer(shmid) ) return;
@@ -297,35 +287,148 @@ void PluginLV2ChildUI::run_buffer(int shmid)
        shm_bfr->done = 1;
 }
 
-int PluginLV2ChildUI::handle_child()
-{
-       switch( child_token ) {
-       case LV2_OPEN: {
-               open_bfr_t *open_bfr = (open_bfr_t *)child_data;
-               if( init_ui(open_bfr->path, open_bfr->sample_rate) ) exit(1);
-               break; }
-       case LV2_LOAD: {
-               float *vals = (float *)child_data;
-               update_lv2_input(vals, 1);
-               break; }
-       case LV2_UPDATE: {
-               float *vals = (float *)child_data;
-               update_lv2_input(vals, 0);
-               break; }
-       case LV2_SHOW: {
-               start_gui();
-               break; }
-       case LV2_HIDE: {
-               reset_gui();
-               break; }
-       case LV2_SHMID: {
-               int shmid = *(int *)child_data;
-               run_buffer(shmid);
-               send_parent(LV2_SHMID, 0, 0);
-               break; }
-       case EXIT_CODE:
-               return -1;
+int PluginLV2ChildUI::child_iteration(int64_t usec)
+{
+       int ret = 0;
+       if( is_forked() )
+               ret = read_child(usec);
+       else
+               usleep(usec);
+       if( ret > 0 ) {
+               switch( child_token ) {
+               case LV2_OPEN: {
+                       open_bfr_t *open_bfr = (open_bfr_t *)child_data;
+                       if( init_ui(open_bfr->path, open_bfr->sample_rate) ) {
+                               printf("lv2ui: unable to init: %s\n", open_bfr->path);
+                               exit(1);
+                       }
+                       break; }
+               case LV2_LOAD: {
+                       float *vals = (float *)child_data;
+                       update_lv2_input(vals, 1);
+                       break; }
+               case LV2_UPDATE: {
+                       float *vals = (float *)child_data;
+                       update_lv2_input(vals, 0);
+                       break; }
+               case LV2_SHOW: {
+                       start_gui();
+                       break; }
+               case LV2_HIDE: {
+                       reset_gui();
+                       break; }
+               case LV2_SHMID: {
+                       int shmid = *(int *)child_data;
+                       run_buffer(shmid);
+                       send_parent(LV2_SHMID, 0, 0);
+                       break; }
+               case EXIT_CODE:
+                       return -1;
+               }
+       }
+       if( ret >= 0 && gui )
+               gui->handle_child();
+       return ret;
+}
+
+int PluginLV2ChildUI::send_host(int64_t token, const void *data, int bytes)
+{
+       return is_forked() ? send_parent(token, data, bytes) : 0;
+}
+
+PluginLV2GUI::PluginLV2GUI(PluginLV2ChildUI *child_ui)
+{
+       this->child_ui = child_ui;
+       top_level = 0;
+}
+
+PluginLV2GUI::~PluginLV2GUI()
+{
+}
+
+PluginLV2ChildGtkUI::PluginLV2ChildGtkUI(PluginLV2ChildUI *child_ui)
+ : PluginLV2GUI(child_ui)
+{
+       gtk_set_locale();
+       gtk_init(&child_ui->ac, &child_ui->av);
+}
+
+PluginLV2ChildGtkUI::~PluginLV2ChildGtkUI()
+{
+}
+
+void PluginLV2ChildGtkUI::reset_gui()
+{
+       GtkWidget *top_level = (GtkWidget *)this->top_level;
+       if( top_level ) { gtk_widget_destroy(top_level); this->top_level = 0; }
+}
+
+static void lilv_gtk_destroy(GtkWidget* widget, gpointer data)
+{
+       PluginLV2ChildGtkUI *gui = (PluginLV2ChildGtkUI*)data;
+       gui->child_ui->hidden = -1;
+}
+
+void PluginLV2ChildGtkUI::start_gui()
+{
+       this->top_level = (void *)gtk_window_new(GTK_WINDOW_TOPLEVEL);
+       GtkWidget *top_level = (GtkWidget *)this->top_level;
+       g_signal_connect(top_level, "destroy", G_CALLBACK(lilv_gtk_destroy), this);
+       char *title = child_ui->PluginLV2UI::title;
+       gtk_window_set_title(GTK_WINDOW(top_level), title);
+
+       GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
+       gtk_window_set_role(GTK_WINDOW(top_level), "plugin_ui");
+       gtk_container_add(GTK_CONTAINER(top_level), vbox);
+
+       GtkWidget *alignment = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
+       gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
+       gtk_widget_show(alignment);
+       child_ui->lv2ui_instantiate(alignment);
+       GtkWidget* widget = (GtkWidget*)suil_instance_get_widget(child_ui->sinst);
+       gtk_container_add(GTK_CONTAINER(alignment), widget);
+       gtk_window_set_resizable(GTK_WINDOW(top_level), child_ui->lv2ui_resizable());
+       gtk_widget_show_all(vbox);
+       gtk_widget_grab_focus(widget);
+       gtk_window_present(GTK_WINDOW(top_level));
+}
+
+int PluginLV2ChildGtkUI::handle_child()
+{
+       if( gtk_events_pending() ) {
+               gtk_main_iteration();
        }
        return 1;
 }
 
+PluginLV2ChildWgtUI::PluginLV2ChildWgtUI(PluginLV2ChildUI *child_ui)
+ : PluginLV2GUI(child_ui)
+{
+}
+
+PluginLV2ChildWgtUI::~PluginLV2ChildWgtUI()
+{
+}
+
+void PluginLV2ChildWgtUI::reset_gui()
+{
+       lv2_external_ui *top_level = (lv2_external_ui *)this->top_level;
+       if( top_level ) { LV2_EXTERNAL_UI_HIDE(top_level); this->top_level = 0; }
+}
+
+void PluginLV2ChildWgtUI::start_gui()
+{
+       child_ui->lv2ui_instantiate(0);
+       this->top_level = (void *)suil_instance_get_widget(child_ui->sinst);
+       lv2_external_ui *top_level = (lv2_external_ui *)this->top_level;
+       if( top_level ) LV2_EXTERNAL_UI_SHOW(top_level);
+}
+
+int PluginLV2ChildWgtUI::handle_child()
+{
+       lv2_external_ui *top_level = (lv2_external_ui *)this->top_level;
+       if( top_level )
+               LV2_EXTERNAL_UI_RUN(top_level);
+       return 1;
+}
+
index c710060f6a1be7447ffcdf166a99df8524c431a1..e61bd8b86de906b40a061775f6e4eb11dff67ef6 100644 (file)
 #include "pluginlv2gui.inc"
 #include "pluginlv2ui.inc"
 
+//=== lv2_external_ui.h
+#define LV2_EXTERNAL_UI_URI "http://lv2plug.in/ns/extensions/ui#external"
+#define LV2_EXTERNAL_UI_URI__KX__Host "http://kxstudio.sf.net/ns/lv2ext/external-ui#Host"
+
+typedef struct _lv2_external_ui lv2_external_ui;
+struct _lv2_external_ui {
+        void (*run)(lv2_external_ui *ui);
+        void (*show)(lv2_external_ui *ui);
+        void (*hide)(lv2_external_ui *ui);
+        void *self;
+};
+
+typedef struct _lv2_external_ui_host lv2_external_ui_host;
+struct _lv2_external_ui_host {
+        void (*ui_closed)(void* controller);
+        const char *plugin_human_id;
+};
+
+#define LV2_EXTERNAL_UI_RUN(ptr) (ptr)->run(ptr)
+#define LV2_EXTERNAL_UI_SHOW(ptr) (ptr)->show(ptr)
+#define LV2_EXTERNAL_UI_HIDE(ptr) (ptr)->hide(ptr)
+//===
+
 typedef struct _GtkWidget GtkWidget;
 
 #define UPDATE_HOST 1
@@ -43,26 +66,22 @@ public:
        const LilvNode *lilv_type;
 
        LV2_Extension_Data_Feature *ext_data;
-       PluginLV2UriTable  uri_table;
-       LV2_URI_Map_Feature *uri_map;
-       LV2_URID_Map       map;
-       LV2_URID_Unmap     unmap;
+       lv2_external_ui_host extui_host;
+       const char *wgt_type;
+       const char *gtk_type;
+       const char *ui_type;
 
        char title[BCSTRLEN];
        PluginLV2ClientConfig config;
-       int updates, hidden;
        int done, running;
-       const char *gtk_type;
-       GtkWidget *top_level;
-       PluginLV2ChildUI *child;
+       int hidden, updates;
 
-       void reset_gui();
-       int init_ui(const char *path, int sample_rate);
        void start();
        void stop();
-       int send_host(int64_t token, const void *data, int bytes);
        int update_lv2_input(float *vals, int force);
        void update_lv2_output();
+       virtual int send_host(int64_t token, const void *data, int bytes) = 0;
+       virtual int child_iteration(int64_t usec) { return -1; }
 
        void run_lilv(int samples);
        void update_value(int idx, uint32_t bfrsz, uint32_t typ, const void *bfr);
@@ -77,10 +96,42 @@ public:
        static const char *uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid);
        void lv2ui_instantiate(void *parent);
        bool lv2ui_resizable();
-       void start_gui();
-       int run_ui(PluginLV2ChildUI *child=0);
        void update_host();
-       int run(int ac, char **av);
+};
+
+class PluginLV2GUI
+{
+public:
+       PluginLV2GUI(PluginLV2ChildUI *child_ui);
+       virtual ~PluginLV2GUI();
+       virtual void reset_gui() = 0;
+       virtual void start_gui() = 0;
+       virtual int handle_child() = 0;
+
+       PluginLV2ChildUI *child_ui;
+       void *top_level;
+};
+
+class PluginLV2ChildGtkUI : public PluginLV2GUI
+{
+public:
+       PluginLV2ChildGtkUI(PluginLV2ChildUI *child_ui);
+       ~PluginLV2ChildGtkUI();
+
+       void reset_gui();
+       void start_gui();
+       int handle_child();
+};
+
+class PluginLV2ChildWgtUI : public PluginLV2GUI
+{
+public:
+       PluginLV2ChildWgtUI(PluginLV2ChildUI *child_ui);
+       ~PluginLV2ChildWgtUI();
+
+       void reset_gui();
+       void start_gui();
+       int handle_child();
 };
 
 class PluginLV2ChildUI : public ForkChild, public PluginLV2UI
@@ -88,11 +139,23 @@ class PluginLV2ChildUI : public ForkChild, public PluginLV2UI
 public:
        PluginLV2ChildUI();
        ~PluginLV2ChildUI();
-       void run();
-       void run_buffer(int shmid);
 
+       int init_ui(const char *path, int sample_rate);
+       void reset_gui();
+       void start_gui();
        int handle_child();
+
+       int is_forked() { return ppid; }
+       int send_host(int64_t token, const void *data, int bytes);
+       int child_iteration(int64_t usec);
+       void run();
        int run(int ac, char **av);
+       int run_ui();
+       void run_buffer(int shmid);
+
+       int ac;
+       char **av;
+       PluginLV2GUI *gui;
 };
 
 #endif