904f9549ddbbc55d10a29470c9c65a344f877f06
[goodguy/history.git] / cinelerra-5.1 / cinelerra / pluginlv2ui.C
1 #ifdef HAVE_LV2UI
2
3 // shared between parent/child fork
4 #include "language.h"
5 #include "pluginlv2ui.h"
6
7 #include <gtk/gtk.h>
8 #include <gdk/gdk.h>
9
10 #include <ctype.h>
11 #include <string.h>
12
13 int PluginLV2UI::init_ui(const char *path, int sample_rate)
14 {
15         if( load_lv2(path, title) ) return 1;
16         if( init_lv2(config, sample_rate) ) return 1;
17
18         lilv_uis = lilv_plugin_get_uis(lilv);
19         if( !lilv_uis ) {
20                 printf("lv2: lilv_plugin_get_uis(%s) failed\n", path);
21                 return 1;
22         }
23
24         if( gtk_type ) {
25                 LilvNode *gui_type = lilv_new_uri(world, gtk_type);
26                 LILV_FOREACH(uis, i, lilv_uis) {
27                         const LilvUI *ui = lilv_uis_get(lilv_uis, i);
28                         if( lilv_ui_is_supported(ui, suil_ui_supported, gui_type, &lilv_type)) {
29                                 lilv_ui = ui;
30                                 break;
31                         }
32                 }
33                 lilv_node_free(gui_type);
34         }
35         if( !lilv_ui )
36                 lilv_ui = lilv_uis_get(lilv_uis, lilv_uis_begin(lilv_uis));
37         if( !lilv_ui ) {
38                 printf("lv2_gui: init_ui failed: %s\n", title);
39                 return 1;
40         }
41
42         lilv_instance_activate(inst);
43         return 0;
44 }
45
46 void PluginLV2UI::update_value(int idx, uint32_t bfrsz, uint32_t typ, const void *bfr)
47 {
48         if( idx >= config.nb_ports ) return;
49         for( int i=0, sz=config.size(); i<sz; ++i ) {
50                 PluginLV2Client_Opt *opt = config[i];
51                 if( opt->idx == idx ) {
52                         opt->set_value(*(const float*)bfr);
53 //printf("set %s = %f\n", opt->get_symbol(), opt->get_value());
54                         ++updates;
55                         break;
56                 }
57         }
58 }
59 void PluginLV2UI::write_from_ui(void *the, uint32_t idx,
60                 uint32_t bfrsz, uint32_t typ, const void *bfr)
61 {
62         ((PluginLV2UI*)the)->update_value(idx, bfrsz, typ, bfr);
63 }
64
65 uint32_t PluginLV2UI::port_index(void* obj, const char* sym)
66 {
67         PluginLV2UI *the = (PluginLV2UI*)obj;
68         for( int i=0, sz=the->config.size(); i<sz; ++i ) {
69                 PluginLV2Client_Opt *opt = the->config[i];
70                 if( !strcmp(sym, opt->sym) ) return opt->idx;
71         }
72         return UINT32_MAX;
73 }
74
75 void PluginLV2UI::update_control(int idx, uint32_t bfrsz, uint32_t typ, const void *bfr)
76 {
77         if( !sinst || idx >= config.nb_ports ) return;
78         suil_instance_port_event(sinst, idx, bfrsz, typ, bfr);
79 }
80
81
82 #if 0
83 void PluginLV2UI::touch(void *obj, uint32_t pidx, bool grabbed)
84 {
85         PluginLV2UI* the = (PluginLV2GUI*)obj;
86         int idx = pidx;
87         if( idx >= the->config.nb_ports ) return;
88 printf("%s %s(%u)\n", (grabbed? _("press") : _("release")),
89   the->config.names[idx], idx);
90 }
91 #endif
92
93 uint32_t PluginLV2UI::uri_to_id(LV2_URID_Map_Handle handle, const char *map, const char *uri)
94 {
95         return ((PluginLV2UriTable *)handle)->map(uri);
96 }
97
98 LV2_URID PluginLV2UI::uri_table_map(LV2_URID_Map_Handle handle, const char *uri)
99 {
100         return ((PluginLV2UriTable *)handle)->map(uri);
101 }
102
103 const char *PluginLV2UI::uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid)
104 {
105         return ((PluginLV2UriTable *)handle)->unmap(urid);
106 }
107
108 void PluginLV2UI::lv2ui_instantiate(void *parent)
109 {
110         if ( !ui_host ) {
111                 ui_host = suil_host_new(
112                         PluginLV2UI::write_from_ui,
113                         PluginLV2UI::port_index,
114                         0, 0);
115 //              suil_host_set_touch_func(ui_host,
116 //                      PluginLV2GUI::touch);
117         }
118
119         features.remove();  // remove terminating zero
120         ui_features = features.size();
121         LV2_Handle lilv_handle = lilv_instance_get_handle(inst);
122         features.append(new Lv2Feature(NS_EXT "instance-access", lilv_handle));
123         const LV2_Descriptor *lilv_desc = lilv_instance_get_descriptor(inst);
124         ext_data = new LV2_Extension_Data_Feature();
125         ext_data->data_access = lilv_desc->extension_data;
126         features.append(new Lv2Feature(LV2_DATA_ACCESS_URI, ext_data));
127         features.append(new Lv2Feature(LV2_UI__parent, parent));
128         features.append(new Lv2Feature(LV2_UI__idleInterface, 0));
129         features.append(0); // add new terminating zero
130
131         const char* bundle_uri  = lilv_node_as_uri(lilv_ui_get_bundle_uri(lilv_ui));
132         char*       bundle_path = lilv_file_uri_parse(bundle_uri, NULL);
133         const char* binary_uri  = lilv_node_as_uri(lilv_ui_get_binary_uri(lilv_ui));
134         char*       binary_path = lilv_file_uri_parse(binary_uri, NULL);
135         sinst = suil_instance_new(ui_host, this, gtk_type,
136                 lilv_node_as_uri(lilv_plugin_get_uri(lilv)),
137                 lilv_node_as_uri(lilv_ui_get_uri(lilv_ui)),
138                 lilv_node_as_uri(lilv_type),
139                 bundle_path, binary_path, features);
140
141         lilv_free(binary_path);
142         lilv_free(bundle_path);
143 }
144
145 bool PluginLV2UI::lv2ui_resizable()
146 {
147         if( !lilv_ui ) return false;
148         const LilvNode* s   = lilv_ui_get_uri(lilv_ui);
149         LilvNode *p   = lilv_new_uri(world, LV2_CORE__optionalFeature);
150         LilvNode *fs  = lilv_new_uri(world, LV2_UI__fixedSize);
151         LilvNode *nrs = lilv_new_uri(world, LV2_UI__noUserResize);
152         LilvNodes *fs_matches = lilv_world_find_nodes(world, s, p, fs);
153         LilvNodes *nrs_matches = lilv_world_find_nodes(world, s, p, nrs);
154         lilv_nodes_free(nrs_matches);
155         lilv_nodes_free(fs_matches);
156         lilv_node_free(nrs);
157         lilv_node_free(fs);
158         lilv_node_free(p);
159         return !fs_matches && !nrs_matches;
160 }
161
162 int PluginLV2UI::update_lv2(float *vals, int force)
163 {
164         int ret = 0;
165         float *ctls = (float *)config.ctls;
166         for( int i=0; i<config.size(); ++i ) {
167                 int idx = config[i]->idx;
168                 float val = vals[idx];
169                 if( !force && ctls[idx] == val ) continue;
170                 update_control(idx, sizeof(val), 0, &val);
171                 ++ret;
172         }
173         for( int i=0; i<config.nb_ports; ++i ) ctls[i] = vals[i];
174         return ret;
175 }
176
177 static void lilv_destroy(GtkWidget* widget, gpointer data)
178 {
179         PluginLV2UI *the = (PluginLV2UI*)data;
180         the->hidden = 1;
181         the->top_level = 0;
182         ++the->updates;
183 }
184
185 void PluginLV2UI::start_gui()
186 {
187         top_level = gtk_window_new(GTK_WINDOW_TOPLEVEL);
188         g_signal_connect(top_level, "destroy", G_CALLBACK(lilv_destroy), this);
189         gtk_window_set_title(GTK_WINDOW(top_level), title);
190
191         GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
192         gtk_window_set_role(GTK_WINDOW(top_level), "plugin_ui");
193         gtk_container_add(GTK_CONTAINER(top_level), vbox);
194
195         GtkWidget *alignment = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
196         gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
197         gtk_widget_show(alignment);
198         lv2ui_instantiate(alignment);
199         GtkWidget* widget = (GtkWidget*)suil_instance_get_widget(sinst);
200         gtk_container_add(GTK_CONTAINER(alignment), widget);
201         gtk_window_set_resizable(GTK_WINDOW(top_level), lv2ui_resizable());
202         gtk_widget_show_all(vbox);
203         gtk_widget_grab_focus(widget);
204         float *ctls = (float *)config.ctls;
205         update_lv2(ctls, 1);
206         connect_ports(config, TYP_CONTROL);
207         lilv_instance_run(inst, 0);
208         gtk_window_present(GTK_WINDOW(top_level));
209 }
210
211
212 void PluginLV2UI::host_update(PluginLV2ChildUI *child)
213 {
214 //printf("update\n");
215         host_updates = updates;
216         if( !child ) return;
217         if( host_hidden != hidden ) {
218                 host_hidden = hidden;
219                 if( hidden ) reset_gui();
220                 child->send_parent(hidden ? LV2_HIDE : LV2_SHOW, 0, 0);
221         }
222         if( running < 0 ) { running = 1;  return; }
223         child->send_parent(LV2_UPDATE, config.ctls, sizeof(float)*config.nb_ports);
224 }
225
226 void PluginLV2UI::reset_gui()
227 {
228         if( sinst )     { suil_instance_free(sinst);  sinst = 0; }
229         if( ui_host )   { suil_host_free(ui_host);    ui_host = 0; }
230         if( top_level ) { gtk_widget_destroy(top_level); top_level = 0; }
231
232         while( features.size() > ui_features ) features.remove_object();
233         features.append(0);
234         hidden = 1;
235 }
236
237
238 // child main
239 int PluginLV2UI::run_ui(PluginLV2ChildUI *child)
240 {
241         running = 1;
242         while( !done ) {
243                 if( gtk_events_pending() ) {
244                         gtk_main_iteration();
245                         continue;
246                 }
247                 if( running && host_updates != updates )
248                         host_update(child);
249                 if( redraw ) {
250                         redraw = 0;
251                         update_lv2(config.ctls, 1);
252                 }
253                 if( !child ) usleep(10000);
254                 else if( child->child_iteration() < 0 )
255                         done = 1;
256         }
257         running = 0;
258         return 0;
259 }
260
261 void PluginLV2UI::run_buffer(int shmid)
262 {
263         if( !shm_buffer(shmid) ) return;
264         map_buffer();
265         int samples = shm_bfr->samples;
266         connect_ports(config);
267         lilv_instance_run(inst, samples);
268         shm_bfr->done = 1;
269 }
270
271 int PluginLV2ChildUI::handle_child()
272 {
273         switch( child_token ) {
274         case LV2_OPEN: {
275                 open_bfr_t *open_bfr = (open_bfr_t *)child_data;
276                 if( init_ui(open_bfr->path, open_bfr->sample_rate) ) exit(1);
277                 break; }
278         case LV2_LOAD: {
279                 float *ctls = (float *)child_data;
280                 update_lv2(ctls, 1);
281                 break; }
282         case LV2_UPDATE: {
283                 float *ctls = (float *)child_data;
284                 if( update_lv2(ctls, 0) > 0 )
285                         ++updates;
286                 break; }
287         case LV2_SHOW: {
288                 start_gui();
289                 hidden = 0;  ++updates;
290                 break; }
291         case LV2_HIDE: {
292                 hidden = 1;  ++updates;
293                 break; }
294         case LV2_SET: {
295                 control_bfr_t *ctl_bfr = (control_bfr_t *)child_data;
296                 config.ctls[ctl_bfr->idx] = ctl_bfr->value;
297                 redraw = 1;
298                 break; }
299         case LV2_SHMID: {
300                 int shmid = *(int *)child_data;
301                 run_buffer(shmid);
302                 send_parent(LV2_SHMID, 0, 0);
303                 break; }
304         case EXIT_CODE:
305                 return -1;
306         }
307         return 1;
308 }
309
310 #endif /* HAVE_LV2UI */