2 // shared between parent/child fork
4 #include "pluginlv2ui.h"
12 void PluginLV2UI::update_value(int idx, uint32_t bfrsz, uint32_t typ, const void *bfr)
14 if( idx >= config.nb_ports ) return;
15 for( int i=0, sz=config.size(); i<sz; ++i ) {
16 PluginLV2Client_Opt *opt = config[i];
17 if( opt->idx == idx ) {
18 opt->set_value(*(const float*)bfr);
19 updates |= UPDATE_HOST;
24 void PluginLV2UI::write_from_ui(void *the, uint32_t idx,
25 uint32_t bfrsz, uint32_t typ, const void *bfr)
27 ((PluginLV2UI*)the)->update_value(idx, bfrsz, typ, bfr);
30 uint32_t PluginLV2UI::port_index(void* obj, const char* sym)
32 PluginLV2UI *the = (PluginLV2UI*)obj;
33 for( int i=0, sz=the->config.size(); i<sz; ++i ) {
34 PluginLV2Client_Opt *opt = the->config[i];
35 if( !strcmp(sym, opt->sym) ) return opt->idx;
40 void PluginLV2UI::update_control(int idx, uint32_t bfrsz, uint32_t typ, const void *bfr)
42 if( !sinst || idx >= config.nb_ports ) return;
43 suil_instance_port_event(sinst, idx, bfrsz, typ, bfr);
47 uint32_t PluginLV2UI::uri_to_id(LV2_URID_Map_Handle handle, const char *map, const char *uri)
49 return ((PluginLV2UriTable *)handle)->map(uri);
52 LV2_URID PluginLV2UI::uri_table_map(LV2_URID_Map_Handle handle, const char *uri)
54 return ((PluginLV2UriTable *)handle)->map(uri);
57 const char *PluginLV2UI::uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid)
59 return ((PluginLV2UriTable *)handle)->unmap(urid);
62 void PluginLV2UI::lv2ui_instantiate(void *parent)
65 ui_host = suil_host_new(
66 PluginLV2UI::write_from_ui,
67 PluginLV2UI::port_index,
69 // suil_host_set_touch_func(ui_host, cb_touch);
72 features.remove(); // remove terminating zero
73 ui_features = features.size();
74 LV2_Handle lilv_handle = lilv_instance_get_handle(inst);
75 features.append(new Lv2Feature(NS_EXT "instance-access", lilv_handle));
76 const LV2_Descriptor *lilv_desc = lilv_instance_get_descriptor(inst);
77 ext_data = new LV2_Extension_Data_Feature();
78 ext_data->data_access = lilv_desc->extension_data;
79 features.append(new Lv2Feature(LV2_DATA_ACCESS_URI, ext_data));
80 features.append(new Lv2Feature(LV2_UI__parent, parent));
81 features.append(new Lv2Feature(LV2_UI__idleInterface, 0));
82 features.append(new Lv2Feature(LV2_EXTERNAL_UI_URI, &extui_host));
83 features.append(new Lv2Feature(LV2_EXTERNAL_UI_URI__KX__Host, &extui_host));
84 features.append(0); // add new terminating zero
86 const char* bundle_uri = lilv_node_as_uri(lilv_ui_get_bundle_uri(lilv_ui));
87 char* bundle_path = lilv_file_uri_parse(bundle_uri, NULL);
88 const char* binary_uri = lilv_node_as_uri(lilv_ui_get_binary_uri(lilv_ui));
89 char* binary_path = lilv_file_uri_parse(binary_uri, NULL);
90 sinst = suil_instance_new(ui_host, this, ui_type,
91 lilv_node_as_uri(lilv_plugin_get_uri(lilv)),
92 lilv_node_as_uri(lilv_ui_get_uri(lilv_ui)),
93 lilv_node_as_uri(lilv_type),
94 bundle_path, binary_path, features);
96 lilv_free(binary_path);
97 lilv_free(bundle_path);
100 bool PluginLV2UI::lv2ui_resizable()
102 if( !lilv_ui ) return false;
103 const LilvNode* s = lilv_ui_get_uri(lilv_ui);
104 LilvNode *p = lilv_new_uri(world, LV2_CORE__optionalFeature);
105 LilvNode *fs = lilv_new_uri(world, LV2_UI__fixedSize);
106 LilvNode *nrs = lilv_new_uri(world, LV2_UI__noUserResize);
107 LilvNodes *fs_matches = lilv_world_find_nodes(world, s, p, fs);
108 LilvNodes *nrs_matches = lilv_world_find_nodes(world, s, p, nrs);
109 lilv_nodes_free(nrs_matches);
110 lilv_nodes_free(fs_matches);
114 return !fs_matches && !nrs_matches;
117 //force= 1:ctls(all),gui(all) 0:changed(ctls) -1:gui(all)
118 int PluginLV2UI::update_lv2_input(float *vals, int force)
121 float *ctls = config.ctls;
122 for( int i=0; i<config.size(); ++i ) {
123 int idx = config[i]->idx;
124 float val = vals[idx];
125 if( !force && ctls[idx] == val ) continue;
131 update_control(idx, sizeof(val), 0, &val);
136 void PluginLV2UI::update_lv2_output()
138 int *ports = config.ports;
139 float *ctls = config.ctls;
140 for( int i=0; i<config.nb_ports; ++i ) {
141 if( !(ports[i] & PORTS_UPDATE) ) continue;
142 ports[i] &= ~PORTS_UPDATE;
143 update_control(i, sizeof(ctls[i]), 0, &ctls[i]);
147 void PluginLV2UI::run_lilv(int samples)
149 float ctls[config.nb_ports];
150 for( int i=0; i<config.nb_ports; ++i ) ctls[i] = config.ctls[i];
152 lilv_instance_run(inst, samples);
154 if( worker_iface ) worker_responses();
156 for( int i=0; i<config.nb_ports; ++i ) {
157 if( !(config.ports[i] & PORTS_OUTPUT) ) continue;
158 if( !(config.ports[i] & PORTS_CONTROL) ) continue;
159 if( config.ctls[i] == ctls[i] ) continue;
160 config.ports[i] |= PORTS_UPDATE;
161 updates |= UPDATE_PORTS;
165 void PluginLV2ChildUI::start_gui()
167 if( gui ) gui->start_gui();
168 update_lv2_input(config.ctls, 1);
169 connect_ports(config, PORTS_CONTROL | PORTS_ATOM);
172 // some plugins must have pointers, or they crash
173 float inp[nb_inputs], out[nb_outputs];
174 memset(&inp, 0, nb_inputs*sizeof(float));
175 memset(&out, 0, nb_outputs*sizeof(float));
176 int ich = 0, och = 0;
177 for( int i=0; i<config.nb_ports; ++i ) {
178 const LilvPort *lp = lilv_plugin_get_port_by_index(lilv, i);
180 int port = config.ports[i];
181 if( !(port & PORTS_AUDIO) ) continue;
182 if( (port & PORTS_INPUT) ) {
183 lilv_instance_connect_port(inst, i, &inp[ich++]);
186 if( (port & PORTS_OUTPUT) ) {
187 lilv_instance_connect_port(inst, i, &out[och++]);
196 send_host(LV2_SHOW, 0, 0);
201 void PluginLV2UI::update_host()
204 if( running < 0 ) { running = 1; return; }
205 send_host(LV2_UPDATE, config.ctls, sizeof(float)*config.nb_ports);
209 static void lv2ui_extui_host_ui_closed(void *p)
211 PluginLV2UI *ui = (PluginLV2UI *)p;
215 int PluginLV2ChildUI::init_ui(const char *path, int sample_rate, int bfrsz)
217 char *title = PluginLV2UI::title;
218 if( load_lv2(path, title) ) return 1;
219 if( init_lv2(config, sample_rate, bfrsz) ) return 1;
221 lilv_uis = lilv_plugin_get_uis(lilv);
222 if( !gui && lilv_uis && wgt_type ) {
223 LilvNode *gui_type = lilv_new_uri(world, wgt_type);
224 LILV_FOREACH(uis, i, lilv_uis) {
225 const LilvUI *ui = lilv_uis_get(lilv_uis, i);
226 if( lilv_ui_is_supported(ui, suil_ui_supported, gui_type, &lilv_type)) {
227 extui_host.ui_closed = lv2ui_extui_host_ui_closed;
228 gui = new PluginLV2ChildWgtUI(this);
234 lilv_node_free(gui_type);
236 if( !gui && lilv_uis && gtk_type ) {
237 LilvNode *gui_type = lilv_new_uri(world, gtk_type);
238 LILV_FOREACH(uis, i, lilv_uis) {
239 const LilvUI *ui = lilv_uis_get(lilv_uis, i);
240 if( lilv_ui_is_supported(ui, suil_ui_supported, gui_type, &lilv_type)) {
241 gui = new PluginLV2ChildGtkUI(this);
247 lilv_node_free(gui_type);
251 lilv_instance_activate(inst);
255 void PluginLV2ChildUI::reset_gui()
257 if( gui ) gui->reset_gui();
258 if( sinst ) { suil_instance_free(sinst); sinst = 0; }
259 if( ui_host ) { suil_host_free(ui_host); ui_host = 0; }
261 while( features.size() > ui_features ) features.remove_object();
263 send_host(LV2_HIDE, 0, 0);
268 int PluginLV2ChildUI::run_ui()
270 double last_time = 0;
271 int64_t frame_usecs = 1e6 / refreshrate;
276 if( gui ) gui->top_level = 0;
285 if( (updates & UPDATE_PORTS) )
287 if( (updates & UPDATE_HOST) )
291 struct timeval tv; gettimeofday(&tv, 0);
292 double t = tv.tv_sec + tv.tv_usec / 1e6;
293 double dt = t - last_time; last_time = t;
294 int64_t usec = frame_usecs - dt*1e6;
295 if( usec < 0 ) usec = 0;
296 if( child_iteration(usec) < 0 )
303 void PluginLV2ChildUI::run_buffer(int shmid)
305 if( !shm_buffer(shmid) ) return;
307 connect_ports(config, PORTS_ALL);
308 run_lilv(shm_bfr->samples);
312 int PluginLV2ChildUI::child_iteration(int64_t usec)
316 ret = read_child(usec);
323 switch( child_token ) {
325 open_bfr_t *open_bfr = (open_bfr_t *)child_data;
326 if( init_ui(open_bfr->path, open_bfr->sample_rate, open_bfr->bfrsz) ) {
327 printf("lv2ui: unable to init: %s\n", open_bfr->path);
332 float *vals = (float *)child_data;
333 update_lv2_input(vals, 1);
336 float *vals = (float *)child_data;
337 update_lv2_input(vals, 0);
340 float *vals = (float *)child_data;
341 update_lv2_input(vals, -1);
350 int shmid = *(int *)child_data;
352 send_parent(LV2_SHMID, 0, 0);
358 if( ret >= 0 && gui )
363 int PluginLV2ChildUI::send_host(int64_t token, const void *data, int bytes)
365 return is_forked() ? send_parent(token, data, bytes) : 0;
368 PluginLV2GUI::PluginLV2GUI(PluginLV2ChildUI *child_ui)
370 this->child_ui = child_ui;
374 PluginLV2GUI::~PluginLV2GUI()
378 PluginLV2ChildGtkUI::PluginLV2ChildGtkUI(PluginLV2ChildUI *child_ui)
379 : PluginLV2GUI(child_ui)
382 gtk_init(&child_ui->ac, &child_ui->av);
385 PluginLV2ChildGtkUI::~PluginLV2ChildGtkUI()
389 void PluginLV2ChildGtkUI::reset_gui()
391 GtkWidget *top_level = (GtkWidget *)this->top_level;
392 if( top_level ) { gtk_widget_destroy(top_level); this->top_level = 0; }
395 static void lilv_gtk_destroy(GtkWidget* widget, gpointer data)
397 PluginLV2ChildGtkUI *gui = (PluginLV2ChildGtkUI*)data;
398 gui->child_ui->hidden = -1;
401 void PluginLV2ChildGtkUI::start_gui()
403 this->top_level = (void *)gtk_window_new(GTK_WINDOW_TOPLEVEL);
404 GtkWidget *top_level = (GtkWidget *)this->top_level;
405 g_signal_connect(top_level, "destroy", G_CALLBACK(lilv_gtk_destroy), this);
406 char *title = child_ui->PluginLV2UI::title;
407 gtk_window_set_title(GTK_WINDOW(top_level), title);
409 GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
410 gtk_window_set_role(GTK_WINDOW(top_level), "plugin_ui");
411 gtk_container_add(GTK_CONTAINER(top_level), vbox);
413 GtkWidget *alignment = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
414 gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
415 gtk_widget_show(alignment);
416 child_ui->lv2ui_instantiate(alignment);
417 GtkWidget* widget = (GtkWidget*)suil_instance_get_widget(child_ui->sinst);
418 gtk_container_add(GTK_CONTAINER(alignment), widget);
419 gtk_window_set_resizable(GTK_WINDOW(top_level), child_ui->lv2ui_resizable());
420 gtk_widget_show_all(vbox);
421 gtk_widget_grab_focus(widget);
422 gtk_window_present(GTK_WINDOW(top_level));
425 int PluginLV2ChildGtkUI::handle_child()
427 if( gtk_events_pending() ) {
428 gtk_main_iteration();
433 PluginLV2ChildWgtUI::PluginLV2ChildWgtUI(PluginLV2ChildUI *child_ui)
434 : PluginLV2GUI(child_ui)
438 PluginLV2ChildWgtUI::~PluginLV2ChildWgtUI()
442 void PluginLV2ChildWgtUI::reset_gui()
444 lv2_external_ui *top_level = (lv2_external_ui *)this->top_level;
445 if( top_level ) { LV2_EXTERNAL_UI_HIDE(top_level); this->top_level = 0; }
448 void PluginLV2ChildWgtUI::start_gui()
450 child_ui->lv2ui_instantiate(0);
451 this->top_level = (void *)suil_instance_get_widget(child_ui->sinst);
452 lv2_external_ui *top_level = (lv2_external_ui *)this->top_level;
453 if( top_level ) LV2_EXTERNAL_UI_SHOW(top_level);
456 int PluginLV2ChildWgtUI::handle_child()
458 lv2_external_ui *top_level = (lv2_external_ui *)this->top_level;
460 LV2_EXTERNAL_UI_RUN(top_level);