bsd lang segv fix, enable bsd lv2, lv2 gui enable fix, proxy/ffmpeg toggle resize...
[goodguy/history.git] / cinelerra-5.1 / cinelerra / pluginlv2ui.C
index 904f9549ddbbc55d10a29470c9c65a344f877f06..17f072a2c6bf63b6c5960f21d7c364aa10e5199c 100644 (file)
@@ -1,4 +1,3 @@
-#ifdef HAVE_LV2UI
 
 // shared between parent/child fork
 #include "language.h"
@@ -10,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;
@@ -50,8 +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);
-//printf("set %s = %f\n", opt->get_symbol(), opt->get_value());
-                       ++updates;
+                       updates |= UPDATE_HOST;
                        break;
                }
        }
@@ -79,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);
@@ -112,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
@@ -126,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),
@@ -159,152 +114,342 @@ bool PluginLV2UI::lv2ui_resizable()
        return !fs_matches && !nrs_matches;
 }
 
-int PluginLV2UI::update_lv2(float *vals, int force)
+int PluginLV2UI::update_lv2_input(float *vals, int force)
 {
        int ret = 0;
-       float *ctls = (float *)config.ctls;
+       float *ctls = config.ctls;
        for( int i=0; i<config.size(); ++i ) {
                int idx = config[i]->idx;
                float val = vals[idx];
                if( !force && ctls[idx] == val ) continue;
-               update_control(idx, sizeof(val), 0, &val);
+               ctls[idx] = val;
+               update_control(idx, sizeof(ctls[idx]), 0, &ctls[idx]);
                ++ret;
        }
-       for( int i=0; i<config.nb_ports; ++i ) ctls[i] = vals[i];
        return ret;
 }
 
-static void lilv_destroy(GtkWidget* widget, gpointer data)
+void PluginLV2UI::update_lv2_output()
 {
-       PluginLV2UI *the = (PluginLV2UI*)data;
-       the->hidden = 1;
-       the->top_level = 0;
-       ++the->updates;
+       int *ports = config.ports;
+       float *ctls = config.ctls;
+       for( int i=0; i<config.nb_ports; ++i ) {
+               if( !(ports[i] & PORTS_UPDATE) ) continue;
+               ports[i] &= ~PORTS_UPDATE;
+               update_control(i, sizeof(ctls[i]), 0, &ctls[i]);
+       }
 }
 
-void PluginLV2UI::start_gui()
+void PluginLV2UI::run_lilv(int samples)
 {
-       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);
+       float ctls[config.nb_ports];
+       for( int i=0; i<config.nb_ports; ++i ) ctls[i] = config.ctls[i];
 
-       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);
+       lilv_instance_run(inst, samples);
 
-       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);
-       float *ctls = (float *)config.ctls;
-       update_lv2(ctls, 1);
-       connect_ports(config, TYP_CONTROL);
-       lilv_instance_run(inst, 0);
-       gtk_window_present(GTK_WINDOW(top_level));
-}
+       if( worker_iface ) worker_responses();
 
+       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 PluginLV2UI::host_update(PluginLV2ChildUI *child)
+void PluginLV2ChildUI::start_gui()
 {
-//printf("update\n");
-       host_updates = updates;
-       if( !child ) return;
-       if( host_hidden != hidden ) {
-               host_hidden = hidden;
-               if( hidden ) reset_gui();
-               child->send_parent(hidden ? LV2_HIDE : LV2_SHOW, 0, 0);
+       if( gui ) gui->start_gui();
+       update_lv2_input(config.ctls, 1);
+       connect_ports(config, PORTS_CONTROL | PORTS_ATOM);
+       int n = 0;
+#if 1
+// some plugins must have pointers, or they crash
+       float inp[nb_inputs], out[nb_outputs];
+       memset(&inp, 0, nb_inputs*sizeof(float));
+       memset(&out, 0, nb_outputs*sizeof(float));
+       int ich = 0, och = 0;
+       for( int i=0; i<config.nb_ports; ++i ) {
+               const LilvPort *lp = lilv_plugin_get_port_by_index(lilv, i);
+               if( !lp ) continue;
+               int port = config.ports[i];
+               if( !(port & PORTS_AUDIO) ) continue;
+               if( (port & PORTS_INPUT) ) {
+                       lilv_instance_connect_port(inst, i, &inp[ich++]);
+                       continue;
+               }
+               if( (port & PORTS_OUTPUT) ) {
+                       lilv_instance_connect_port(inst, i, &out[och++]);
+                       continue;
+               }
+       }
+       n = 1;
+#endif
+       updates = 0;
+       run_lilv(n);
+       if( gui ) {
+               send_host(LV2_SHOW, 0, 0);
+               hidden = 0;
        }
+}
+
+void PluginLV2UI::update_host()
+{
+// ignore update
        if( running < 0 ) { running = 1;  return; }
-       child->send_parent(LV2_UPDATE, config.ctls, sizeof(float)*config.nb_ports);
+       send_host(LV2_UPDATE, config.ctls, sizeof(float)*config.nb_ports);
+}
+
+
+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, int bfrsz)
+{
+       char *title = PluginLV2UI::title;
+       if( load_lv2(path, title) ) return 1;
+       if( init_lv2(config, sample_rate, bfrsz) ) return 1;
+
+       lilv_uis = lilv_plugin_get_uis(lilv);
+       if( !gui && lilv_uis && 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 && lilv_uis && 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;
+       }
+
+       lilv_instance_activate(inst);
+       return 0;
 }
 
-void PluginLV2UI::reset_gui()
+void PluginLV2ChildUI::reset_gui()
 {
+       if( gui ) 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);
+       send_host(LV2_HIDE, 0, 0);
        hidden = 1;
 }
 
-
-// child main
-int PluginLV2UI::run_ui(PluginLV2ChildUI *child)
+// main loop
+int PluginLV2ChildUI::run_ui()
 {
+       double last_time = 0;
+       int64_t frame_usecs = 1e6 / refreshrate;
        running = 1;
+       done = 0;
        while( !done ) {
-               if( gtk_events_pending() ) {
-                       gtk_main_iteration();
-                       continue;
+               if( hidden < 0 ) {
+                       if( gui ) gui->top_level = 0;
+                       reset_gui();
+                       if( !is_forked() ) {
+                               done = -1;
+                               break;
+                       }
+                       hidden = 1;
                }
-               if( running && host_updates != updates )
-                       host_update(child);
-               if( redraw ) {
-                       redraw = 0;
-                       update_lv2(config.ctls, 1);
+               if( updates ) {
+                       if( (updates & UPDATE_PORTS) )
+                               update_lv2_output();
+                       if( (updates & UPDATE_HOST) )
+                               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_buffer(int shmid)
+void PluginLV2ChildUI::run_buffer(int shmid)
 {
        if( !shm_buffer(shmid) ) return;
        map_buffer();
-       int samples = shm_bfr->samples;
-       connect_ports(config);
-       lilv_instance_run(inst, samples);
+       connect_ports(config, PORTS_ALL);
+       run_lilv(shm_bfr->samples);
        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 *ctls = (float *)child_data;
-               update_lv2(ctls, 1);
-               break; }
-       case LV2_UPDATE: {
-               float *ctls = (float *)child_data;
-               if( update_lv2(ctls, 0) > 0 )
-                       ++updates;
-               break; }
-       case LV2_SHOW: {
-               start_gui();
-               hidden = 0;  ++updates;
-               break; }
-       case LV2_HIDE: {
-               hidden = 1;  ++updates;
-               break; }
-       case LV2_SET: {
-               control_bfr_t *ctl_bfr = (control_bfr_t *)child_data;
-               config.ctls[ctl_bfr->idx] = ctl_bfr->value;
-               redraw = 1;
-               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 if( gui )
+               usleep(usec);
+       else
+               ret = -1;
+
+       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, open_bfr->bfrsz) ) {
+                               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;
 }
 
-#endif /* HAVE_LV2UI */