1 /*
2  * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
3  *
4  * This is free software: you can redistribute it and/or modify
5  * it under the terms of the Artistic License 2.0 as published by
6  * The Perl Foundation.
7  *
8  * This source is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * Artistic License 2.0 for more details.
12  *
13  * You should have received a copy of the Artistic License 2.0
14  * along the source as a COPYING file. If not, obtain it from
15  * http://www.perlfoundation.org/artistic_license_2_0.
16  */
17 
18 #include <stdbool.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <dlfcn.h>
23 #include <string.h>
24 #include <ctype.h>
25 
26 #include <sandbox_slave.h>
27 #include <sandbox_io.h>
28 
29 #include <xpress.lv2/xpress.h>
30 
31 #include <lv2/lv2plug.in/ns/ext/log/log.h>
32 #include <lv2/lv2plug.in/ns/ext/options/options.h>
33 #include <lv2/lv2plug.in/ns/ext/uri-map/uri-map.h>
34 #include <lv2/lv2plug.in/ns/extensions/ui/ui.h>
35 
36 #include <lilv/lilv.h>
37 
38 #define MAPPER_IMPLEMENTATION
39 #include <mapper.lv2/mapper.h>
40 
41 struct _sandbox_slave_t {
42 	mapper_t *mapper;
43 
44 	LV2_URID_Map *map;
45 	LV2_URID_Unmap *unmap;
46 #pragma GCC diagnostic push
47 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
48 	LV2_URI_Map_Feature uri_id;
49 #pragma GCC diagnostic pop
50 
51 	LV2_URID log_trace;
52 	LV2_URID log_error;
53 	LV2_URID log_warning;
54 	LV2_URID log_note;
55 
56 	LV2_Log_Log log;
57 
58 	LV2UI_Port_Map port_map;
59 	LV2UI_Port_Subscribe port_subscribe;
60 	xpress_map_t xmap;
61 	xpress_t xpress;
62 
63 	LV2UI_Resize host_resize;
64 
65 	LilvWorld *world;
66 	LilvNode *plugin_bundle_node;
67 	LilvNode *ui_bundle_node;
68 	LilvNode *plugin_node;
69 	LilvNode *ui_node;
70 
71 	const LilvPlugin *plug;
72 	LilvUIs *uis;
73 	const LilvUI *ui;
74 
75 	bool no_user_resize;
76 	void *lib;
77 	const LV2UI_Descriptor *desc;
78 	void *handle;
79 
80 	sandbox_io_t io;
81 
82 	const sandbox_slave_driver_t *driver;
83 	void *data;
84 
85 	bool initialized;
86 
87 	const char *plugin_urn;
88 	const char *plugin_uri;
89 	const char *plugin_bundle_path;
90 	const char *ui_uri;
91 	const char *ui_bundle_path;
92 	const char *socket_path;
93 	const char *window_title;
94 	uint32_t minimum;
95 	float sample_rate;
96 	float update_rate;
97 };
98 
99 #define ANSI_COLOR_BOLD    "\x1b[1m"
100 #define ANSI_COLOR_RED     "\x1b[31m"
101 #define ANSI_COLOR_GREEN   "\x1b[32m"
102 #define ANSI_COLOR_YELLOW  "\x1b[33m"
103 #define ANSI_COLOR_BLUE    "\x1b[34m"
104 #define ANSI_COLOR_MAGENTA "\x1b[35m"
105 #define ANSI_COLOR_CYAN    "\x1b[36m"
106 #define ANSI_COLOR_RESET   "\x1b[0m"
107 
108 enum {
109 	COLOR_TRACE = 0,
110 	COLOR_LOG,
111 	COLOR_ERROR,
112 	COLOR_NOTE,
113 	COLOR_WARNING,
114 
115 	COLOR_UI,
116 	COLOR_URN1,
117 	COLOR_URN2
118 };
119 
120 static const char *prefix [2][8] = {
121 	[0] = {
122 		[COLOR_TRACE]   = "[Trace]",
123 		[COLOR_LOG]     = "[Log]  ",
124 		[COLOR_ERROR]   = "[Error]",
125 		[COLOR_NOTE]    = "[Note] ",
126 		[COLOR_WARNING] = "[Warn] ",
127 
128 		[COLOR_UI]     = "(UI) ",
129 		[COLOR_URN1]   = "{",
130 		[COLOR_URN2]   = "}"
131 	},
132 	[1] = {
133 		[COLOR_TRACE]   = "["ANSI_COLOR_BLUE   "Trace"ANSI_COLOR_RESET"]",
134 		[COLOR_LOG]     = "["ANSI_COLOR_MAGENTA"Log"ANSI_COLOR_RESET"]  ",
135 		[COLOR_ERROR]   = "["ANSI_COLOR_RED    "Error"ANSI_COLOR_RESET"]",
136 		[COLOR_NOTE]    = "["ANSI_COLOR_GREEN  "Note"ANSI_COLOR_RESET"] ",
137 		[COLOR_WARNING] = "["ANSI_COLOR_YELLOW "Warn"ANSI_COLOR_RESET"] ",
138 
139 		[COLOR_UI]      = "("ANSI_COLOR_MAGENTA"UI"ANSI_COLOR_RESET") ",
140 		[COLOR_URN1]    = "{"ANSI_COLOR_BOLD,
141 		[COLOR_URN2]    = ANSI_COLOR_RESET"}",
142 	}
143 };
144 
145 static inline int
_log_vprintf(LV2_Log_Handle handle,LV2_URID type,const char * fmt,va_list args)146 _log_vprintf(LV2_Log_Handle handle, LV2_URID type, const char *fmt, va_list args)
147 {
148 	sandbox_slave_t *sb = handle;
149 
150 	int idx = COLOR_LOG;
151 	if(type == sb->log_trace)
152 		idx = COLOR_TRACE;
153 	else if(type == sb->log_error)
154 		idx = COLOR_ERROR;
155 	else if(type == sb->log_note)
156 		idx = COLOR_NOTE;
157 	else if(type == sb->log_warning)
158 		idx = COLOR_WARNING;
159 
160 	char *buf;
161 	if(vasprintf(&buf, fmt, args) == -1)
162 		buf = NULL;
163 
164 	if(buf)
165 	{
166 		const int istty = isatty(STDERR_FILENO);
167 		const char *sep = "\n";
168 		for(char *bufp = buf, *pch = strsep(&bufp, sep);
169 			pch;
170 			pch = strsep(&bufp, sep) )
171 		{
172 			if(strlen(pch))
173 			{
174 				fprintf(stderr, "%s %s ", prefix[istty][COLOR_UI], prefix[istty][idx]);
175 				if(sb->plugin_urn)
176 					fprintf(stderr, "%s%s%s ", prefix[istty][COLOR_URN1], sb->plugin_urn, prefix[istty][COLOR_URN2]);
177 				fprintf(stderr, "%s\n", pch);
178 			}
179 		}
180 
181 		free(buf);
182 	}
183 
184 	return 0;
185 }
186 
187 static inline int __attribute__((format(printf, 3, 4)))
_log_printf(LV2_Log_Handle handle,LV2_URID type,const char * fmt,...)188 _log_printf(LV2_Log_Handle handle, LV2_URID type, const char *fmt, ...)
189 {
190   va_list args;
191 
192   va_start (args, fmt);
193 	const int ret = _log_vprintf(handle, type, fmt, args);
194   va_end(args);
195 
196 	return ret;
197 }
198 
199 static inline uint32_t
_port_index(LV2UI_Feature_Handle handle,const char * symbol)200 _port_index(LV2UI_Feature_Handle handle, const char *symbol)
201 {
202 	sandbox_slave_t *sb = handle;
203 	uint32_t index = LV2UI_INVALID_PORT_INDEX;
204 
205 	LilvNode *symbol_uri = lilv_new_string(sb->world, symbol);
206 	if(symbol_uri)
207 	{
208 		const LilvPort *port = lilv_plugin_get_port_by_symbol(sb->plug, symbol_uri);
209 
210 		if(port)
211 			index = lilv_port_get_index(sb->plug, port);
212 
213 		lilv_node_free(symbol_uri);
214 	}
215 
216 	return index;
217 }
218 
219 static inline void
_write_function(LV2UI_Controller controller,uint32_t index,uint32_t size,uint32_t protocol,const void * buf)220 _write_function(LV2UI_Controller controller, uint32_t index,
221 	uint32_t size, uint32_t protocol, const void *buf)
222 {
223 	sandbox_slave_t *sb = controller;
224 
225 	const int status = _sandbox_io_send(&sb->io, index, size, protocol, buf);
226 	(void)status; //TODO
227 }
228 
229 static inline uint32_t
_port_subscribe(LV2UI_Feature_Handle handle,uint32_t index,uint32_t protocol,const LV2_Feature * const * features)230 _port_subscribe(LV2UI_Feature_Handle handle, uint32_t index, uint32_t protocol,
231 	const LV2_Feature *const *features)
232 {
233 	sandbox_slave_t *sb = handle;
234 
235 	const sandbox_io_subscription_t sub = {
236 		.state = 1,
237 		.protocol = protocol
238 	};
239 	_write_function(handle, index, sizeof(sandbox_io_subscription_t), sb->io.ui_port_subscribe, &sub);
240 
241 	return 0;
242 }
243 
244 static inline uint32_t
_port_unsubscribe(LV2UI_Feature_Handle handle,uint32_t index,uint32_t protocol,const LV2_Feature * const * features)245 _port_unsubscribe(LV2UI_Feature_Handle handle, uint32_t index, uint32_t protocol,
246 	const LV2_Feature *const *features)
247 {
248 	sandbox_slave_t *sb = handle;
249 
250 	const sandbox_io_subscription_t sub = {
251 		.state = 0,
252 		.protocol = protocol
253 	};
254 	_write_function(handle, index, sizeof(sandbox_io_subscription_t), sb->io.ui_port_subscribe, &sub);
255 
256 	return 0;
257 }
258 
259 static inline bool
_sandbox_recv_cb(LV2UI_Handle handle,uint32_t index,uint32_t size,uint32_t protocol,const void * buf)260 _sandbox_recv_cb(LV2UI_Handle handle, uint32_t index, uint32_t size,
261 	uint32_t protocol, const void *buf)
262 {
263 	sandbox_slave_t *sb = handle;
264 
265 	if(sb->desc && sb->desc->port_event)
266 		sb->desc->port_event(sb->handle, index, size, protocol, buf);
267 
268 	return true; // continue handling messages
269 }
270 
271 #pragma GCC diagnostic push
272 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
273 static uint32_t
_sb_uri_to_id(LV2_URI_Map_Callback_Data handle,const char * map,const char * uri)274 _sb_uri_to_id(LV2_URI_Map_Callback_Data handle, const char *map, const char *uri)
275 {
276 	sandbox_slave_t *sb = handle;
277 	(void)map;
278 
279 	return sb->map->map(sb->map->handle, uri);
280 }
281 #pragma GCC diagnostic pop
282 
283 static uint32_t
_voice_map_new_uuid(void * data,uint32_t flags)284 _voice_map_new_uuid(void *data, uint32_t flags __attribute__((unused)))
285 {
286 	xpress_t *xpress = data;
287 
288 	return xpress_map(xpress);
289 }
290 
291 sandbox_slave_t *
sandbox_slave_new(int argc,char ** argv,const sandbox_slave_driver_t * driver,void * data,int * res)292 sandbox_slave_new(int argc, char **argv, const sandbox_slave_driver_t *driver,
293 	void *data, int *res)
294 {
295 	sandbox_slave_t *sb = calloc(1, sizeof(sandbox_slave_t));
296 	if(!sb)
297 	{
298 		fprintf(stderr, "allocation failed\n");
299 		goto fail;
300 	}
301 
302 	sb->plugin_urn = NULL;
303 	sb->window_title = "Untitled"; // fall-back
304 	sb->minimum = 0x100000; // fall-back
305 	sb->sample_rate = 44100.f; // fall-back
306 	sb->update_rate = 25.f; // fall-back
307 
308 	fprintf(stderr,
309 		"Synthpod "SYNTHPOD_VERSION"\n"
310 		"Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch)\n"
311 		"Released under Artistic License 2.0 by Open Music Kontrollers\n");
312 
313 	int c;
314 	while((c = getopt(argc, argv, "vhn:p:P:u:U:s:w:m:r:f:")) != -1)
315 	{
316 		switch(c)
317 		{
318 			case 'v':
319 				fprintf(stderr,
320 					"--------------------------------------------------------------------\n"
321 					"This is free software: you can redistribute it and/or modify\n"
322 					"it under the terms of the Artistic License 2.0 as published by\n"
323 					"The Perl Foundation.\n"
324 					"\n"
325 					"This source is distributed in the hope that it will be useful,\n"
326 					"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
327 					"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
328 					"Artistic License 2.0 for more details.\n"
329 					"\n"
330 					"You should have received a copy of the Artistic License 2.0\n"
331 					"along the source as a COPYING file. If not, obtain it from\n"
332 					"http://www.perlfoundation.org/artistic_license_2_0.\n\n");
333 				*res = EXIT_SUCCESS;
334 				return NULL;
335 			case 'h':
336 				fprintf(stderr,
337 					"--------------------------------------------------------------------\n"
338 					"USAGE\n"
339 					"   %s [OPTIONS]\n"
340 					"\n"
341 					"OPTIONS\n"
342 					"   [-v]                 print version and full license information\n"
343 					"   [-h]                 print usage information\n"
344 					"   [-n] plugin-urn      Plugin URN\n"
345 					"   [-p] plugin-uri      Plugin URI\n"
346 					"   [-P] plugin-bundle   Plugin bundle path\n"
347 					"   [-u] ui-uri          Plugin UI URI\n"
348 					"   [-U] ui-bundle       Plugin UI bundle path\n"
349 					"   [-s] socket-path     Socket path\n"
350 					"   [-w] window-title    Window title\n"
351 					"   [-m] minimum-size    Minimum ringbuffer size\n"
352 					"   [-r] sample-rate     Sample rate (44100)\n"
353 					"   [-f] update-rate     GUI update rate (25)\n\n"
354 					, argv[0]);
355 				*res = EXIT_SUCCESS;
356 				return NULL;
357 			case 'n':
358 				sb->plugin_urn = optarg;
359 				break;
360 			case 'p':
361 				sb->plugin_uri = optarg;
362 				break;
363 			case 'P':
364 				sb->plugin_bundle_path = optarg;
365 				break;
366 			case 'u':
367 				sb->ui_uri = optarg;
368 				break;
369 			case 'U':
370 				sb->ui_bundle_path = optarg;
371 				break;
372 			case 's':
373 				sb->socket_path = optarg;
374 				break;
375 			case 'w':
376 				sb->window_title = optarg;
377 				break;
378 			case 'm':
379 				sb->minimum = atoi(optarg);
380 				break;
381 			case 'r':
382 				sb->sample_rate = atof(optarg);
383 				break;
384 			case 'f':
385 				sb->update_rate = atof(optarg);
386 				break;
387 			case '?':
388 				if( (optopt == 'n') || (optopt == 'p') || (optopt == 'P') || (optopt == 'u') || (optopt == 'U') || (optopt == 's') || (optopt == 'w') || (optopt == 'm') || (optopt == 'r') || (optopt == 'f') )
389 					fprintf(stderr, "Option `-%c' requires an argument.\n", optopt);
390 				else if(isprint(optopt))
391 					fprintf(stderr, "Unknown option `-%c'.\n", optopt);
392 				else
393 					fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
394 				goto fail;
395 			default:
396 				goto fail;
397 		}
398 	}
399 
400 	if(  !sb->plugin_uri
401 		|| !sb->plugin_bundle_path
402 		|| !sb->ui_uri
403 		|| !sb->ui_bundle_path
404 		|| !sb->socket_path)
405 	{
406 		fprintf(stderr, "not enough arguments\n");
407 		goto fail;
408 	}
409 
410 	sb->driver = driver;
411 	sb->data = data;
412 
413 	sb->log.handle = sb;
414 	sb->log.printf = _log_printf;
415 	sb->log.vprintf = _log_vprintf;
416 
417 	sb->port_map.handle = sb;
418 	sb->port_map.port_index = _port_index;
419 
420 	sb->port_subscribe.handle = sb;
421 	sb->port_subscribe.subscribe = _port_subscribe;
422 	sb->port_subscribe.unsubscribe = _port_unsubscribe;
423 
424 	sb->host_resize.handle = data;
425 	sb->host_resize.ui_resize = driver->resize_cb;
426 
427 	if(!(sb->mapper = mapper_new(0x1000000, 0, NULL, NULL, NULL, NULL))) // 16M
428 	{
429 		fprintf(stderr, "mapper_new failed\n");
430 		goto fail;
431 	}
432 
433 	sb->map = mapper_get_map(sb->mapper);
434 	sb->unmap = mapper_get_unmap(sb->mapper);
435 	sb->uri_id.callback_data = sb;
436 	sb->uri_id.uri_to_id = _sb_uri_to_id;
437 
438 	sb->log_trace = sb->map->map(sb->map->handle, LV2_LOG__Trace);
439 	sb->log_error = sb->map->map(sb->map->handle, LV2_LOG__Error);
440 	sb->log_warning = sb->map->map(sb->map->handle, LV2_LOG__Warning);
441 	sb->log_note = sb->map->map(sb->map->handle, LV2_LOG__Note);
442 
443 	xpress_init(&sb->xpress, 0, sb->map, NULL,
444 		XPRESS_EVENT_NONE, NULL, NULL, NULL);
445 	sb->xmap.new_uuid = _voice_map_new_uuid;
446 	sb->xmap.handle = &sb->xpress;
447 
448 	if(!(sb->world = lilv_world_new()))
449 	{
450 		fprintf(stderr, "lilv_world_new failed\n");
451 		goto fail;
452 	}
453 
454 	sb->plugin_bundle_node = lilv_new_file_uri(sb->world, NULL, sb->plugin_bundle_path);
455 	if(strcmp(sb->plugin_bundle_path, sb->ui_bundle_path))
456 	{
457 		sb->ui_bundle_node = lilv_new_file_uri(sb->world, NULL, sb->ui_bundle_path);
458 	}
459 
460 	sb->plugin_node = lilv_new_uri(sb->world, sb->plugin_uri);
461 	sb->ui_node = lilv_new_uri(sb->world, sb->ui_uri);
462 
463 	if(!sb->plugin_bundle_node || !sb->plugin_node || !sb->ui_node)
464 	{
465 		fprintf(stderr, "lilv_new_uri failed\n");
466 		goto fail;
467 	}
468 
469 	lilv_world_load_bundle(sb->world, sb->plugin_bundle_node);
470 	if(sb->ui_bundle_node)
471 	{
472 		lilv_world_load_bundle(sb->world, sb->ui_bundle_node);
473 	}
474 
475 	lilv_world_load_resource(sb->world, sb->plugin_node);
476 	lilv_world_load_resource(sb->world, sb->ui_node);
477 
478 	const LilvPlugins *plugins = lilv_world_get_all_plugins(sb->world);
479 	if(!plugins)
480 	{
481 		fprintf(stderr, "lilv_world_get_all_plugins failed\n");
482 		goto fail;
483 	}
484 
485 	if(!(sb->plug = lilv_plugins_get_by_uri(plugins, sb->plugin_node)))
486 	{
487 		fprintf(stderr, "lilv_plugins_get_by_uri failed\n");
488 		goto fail;
489 	}
490 
491 	sb->uis = lilv_plugin_get_uis(sb->plug);
492 	if(!sb->uis)
493 	{
494 		fprintf(stderr, "lilv_plugin_get_uis failed\n");
495 		goto fail;
496 	}
497 
498 	if(!(sb->ui = lilv_uis_get_by_uri(sb->uis, sb->ui_node)))
499 	{
500 		fprintf(stderr, "lilv_uis_get_by_uri failed\n");
501 		goto fail;
502 	}
503 
504 	const LilvNode *ui_path = lilv_ui_get_binary_uri(sb->ui);
505 	if(!ui_path)
506 	{
507 		fprintf(stderr, "lilv_ui_get_binary_uri failed\n");
508 		goto fail;
509 	}
510 
511 	LilvNode *no_user_resize_uri = lilv_new_uri(sb->world, LV2_UI__noUserResize);
512 	if(no_user_resize_uri)
513 	{
514 		sb->no_user_resize = lilv_world_ask(sb->world, sb->ui_node, no_user_resize_uri, NULL);
515 		lilv_node_free(no_user_resize_uri);
516 	}
517 
518 #if defined(LILV_0_22)
519 	char *binary_path = lilv_file_uri_parse(lilv_node_as_string(ui_path), NULL);
520 #else
521 	const char *binary_path = lilv_uri_to_path(lilv_node_as_string(ui_path));
522 #endif
523 	if(!(sb->lib = dlopen(binary_path, RTLD_LAZY | RTLD_LOCAL)))
524 	{
525 		fprintf(stderr, "dlopen failed: %s\n", dlerror());
526 		goto fail;
527 	}
528 
529 #if defined(LILV_0_22)
530 	lilv_free(binary_path);
531 #endif
532 
533 	LV2UI_DescriptorFunction desc_func = dlsym(sb->lib, "lv2ui_descriptor");
534 	if(!desc_func)
535 	{
536 		fprintf(stderr, "dlsym failed\n");
537 		goto fail;
538 	}
539 
540 	for(int i=0; true; i++)
541 	{
542 		const LV2UI_Descriptor *desc = desc_func(i);
543 		if(!desc) // sentinel
544 			break;
545 
546 		if(!strcmp(desc->URI, sb->ui_uri))
547 		{
548 			sb->desc = desc;
549 			break;
550 		}
551 	}
552 
553 	if(!sb->desc)
554 	{
555 		fprintf(stderr, "LV2UI_Descriptor lookup failed\n");
556 		goto fail;
557 	}
558 
559 	if(_sandbox_io_init(&sb->io, sb->map, sb->unmap, sb->socket_path, false, false, sb->minimum))
560 	{
561 		fprintf(stderr, "_sandbox_io_init failed: are you sure that the host is running?\n");
562 		goto fail;
563 	}
564 
565 	if(driver->init_cb && (driver->init_cb(sb, data) != 0) )
566 	{
567 		fprintf(stderr, "driver->init_cb failed\n");
568 		goto fail;
569 	}
570 
571 	sb->initialized = true;
572 	*res = EXIT_SUCCESS;
573 	return sb; // success
574 
575 fail:
576 	sandbox_slave_free(sb);
577 	*res = EXIT_FAILURE;
578 	return NULL;
579 }
580 
581 void
sandbox_slave_free(sandbox_slave_t * sb)582 sandbox_slave_free(sandbox_slave_t *sb)
583 {
584 	if(!sb)
585 		return;
586 
587 	xpress_deinit(&sb->xpress);
588 
589 	if(sb->desc && sb->desc->cleanup && sb->handle)
590 		sb->desc->cleanup(sb->handle);
591 
592 	if(sb->driver && sb->driver->deinit_cb && sb->initialized)
593 		sb->driver->deinit_cb(sb->data);
594 
595 	_sandbox_io_deinit(&sb->io, false);
596 
597 	if(sb->lib)
598 		dlclose(sb->lib);
599 
600 	if(sb->world)
601 	{
602 		if(sb->uis)
603 		{
604 			lilv_uis_free(sb->uis);
605 		}
606 
607 		if(sb->ui_node)
608 		{
609 			lilv_world_unload_resource(sb->world, sb->ui_node);
610 			lilv_node_free(sb->ui_node);
611 		}
612 
613 		if(sb->plugin_node)
614 		{
615 			lilv_world_unload_resource(sb->world, sb->plugin_node);
616 			lilv_node_free(sb->plugin_node);
617 		}
618 
619 		if(sb->ui_bundle_node)
620 		{
621 			lilv_world_unload_bundle(sb->world, sb->ui_bundle_node);
622 			lilv_node_free(sb->ui_bundle_node);
623 		}
624 
625 		if(sb->plugin_bundle_node)
626 		{
627 			lilv_world_unload_bundle(sb->world, sb->plugin_bundle_node);
628 			lilv_node_free(sb->plugin_bundle_node);
629 		}
630 
631 		lilv_world_free(sb->world);
632 	}
633 
634 	if(sb->mapper)
635 	{
636 		mapper_free(sb->mapper);
637 	}
638 
639 	free(sb);
640 }
641 
642 void *
sandbox_slave_instantiate(sandbox_slave_t * sb,const LV2_Feature * parent_feature,void * widget)643 sandbox_slave_instantiate(sandbox_slave_t *sb, const LV2_Feature *parent_feature, void *widget)
644 {
645 	LV2_Options_Option options [] = {
646 		[0] = {
647 			.context = LV2_OPTIONS_INSTANCE,
648 			.subject = 0,
649 			.key = sb->io.ui_window_title,
650 			.size = strlen(sb->plugin_uri) + 1,
651 			.type = sb->io.forge.String,
652 			.value = sb->plugin_uri
653 		},
654 		[1] = {
655 			.context = LV2_OPTIONS_INSTANCE,
656 			.subject = 0,
657 			.key = sb->io.params_sample_rate,
658 			.size = sizeof(float),
659 			.type = sb->io.forge.Float,
660 			.value = &sb->sample_rate
661 		},
662 		[2] = {
663 			.context = LV2_OPTIONS_INSTANCE,
664 			.subject = 0,
665 			.key = sb->io.ui_update_rate,
666 			.size = sizeof(float),
667 			.type = sb->io.forge.Float,
668 			.value = &sb->update_rate
669 		},
670 		[3] = {
671 			.key = 0,
672 			.value = NULL
673 		}
674 	};
675 
676 	const LV2_Feature map_feature = {
677 		.URI = LV2_URID__map,
678 		.data = sb->map
679 	};
680 	const LV2_Feature unmap_feature = {
681 		.URI = LV2_URID__unmap,
682 		.data = sb->unmap
683 	};
684 	const LV2_Feature uri_id_feature= {
685 		.URI = LV2_URI_MAP_URI,
686 		.data = &sb->uri_id
687 	};
688 	const LV2_Feature log_feature = {
689 		.URI = LV2_LOG__log,
690 		.data = &sb->log
691 	};
692 	const LV2_Feature port_map_feature = {
693 		.URI = LV2_UI__portMap,
694 		.data = &sb->port_map
695 	};
696 	const LV2_Feature port_subscribe_feature = {
697 		.URI = LV2_UI__portSubscribe,
698 		.data = &sb->port_subscribe
699 	};
700 	const LV2_Feature options_feature = {
701 		.URI = LV2_OPTIONS__options,
702 		.data = options
703 	};
704 	const LV2_Feature voice_map_feature = {
705 		.URI = XPRESS__voiceMap,
706 		.data = &sb->xmap
707 	};
708 	const LV2_Feature resize_feature = {
709 		.URI = LV2_UI__resize,
710 		.data = &sb->host_resize
711 	};
712 
713 	const LV2_Feature *const features [] = {
714 		&map_feature,
715 		&unmap_feature,
716 		&uri_id_feature,
717 		&log_feature,
718 		&port_map_feature,
719 		&port_subscribe_feature,
720 		&options_feature,
721 		&voice_map_feature,
722 		sb->host_resize.ui_resize ? &resize_feature : parent_feature,
723 		sb->host_resize.ui_resize && parent_feature ? parent_feature : NULL,
724 		NULL
725 	};
726 
727 	//FIXME check features
728 
729 	const LilvNode *ui_bundle_uri = lilv_ui_get_bundle_uri(sb->ui);
730 #if defined(LILV_0_22)
731 	char *ui_plugin_bundle_path = lilv_file_uri_parse(lilv_node_as_string(ui_bundle_uri), NULL);
732 #else
733 	const char *ui_plugin_bundle_path = lilv_uri_to_path(lilv_node_as_string(ui_bundle_uri));
734 #endif
735 
736 	if(sb->desc && sb->desc->instantiate)
737 	{
738 		sb->handle = sb->desc->instantiate(sb->desc, sb->plugin_uri,
739 			ui_plugin_bundle_path, _write_function, sb, widget, features);
740 	}
741 
742 #if defined(LILV_0_22)
743 	lilv_free(ui_plugin_bundle_path);
744 #endif
745 
746 	if(sb->handle)
747 		return sb->handle; // success
748 
749 	return NULL;
750 }
751 
752 int
sandbox_slave_recv(sandbox_slave_t * sb)753 sandbox_slave_recv(sandbox_slave_t *sb)
754 {
755 	if(sb)
756 		return _sandbox_io_recv(&sb->io, _sandbox_recv_cb, NULL, sb);
757 
758 	return -1;
759 }
760 
761 void
sandbox_slave_wait(sandbox_slave_t * sb)762 sandbox_slave_wait(sandbox_slave_t *sb)
763 {
764 	_sandbox_io_wait(&sb->io);
765 }
766 
767 bool
sandbox_slave_timedwait(sandbox_slave_t * sb,const struct timespec * abs_timeout)768 sandbox_slave_timedwait(sandbox_slave_t *sb, const struct timespec *abs_timeout)
769 {
770 	return _sandbox_io_timedwait(&sb->io, abs_timeout);
771 }
772 
773 const void *
sandbox_slave_extension_data(sandbox_slave_t * sb,const char * URI)774 sandbox_slave_extension_data(sandbox_slave_t *sb, const char *URI)
775 {
776 	if(sb && sb->desc && sb->desc->extension_data)
777 		return sb->desc->extension_data(URI);
778 
779 	return NULL;
780 }
781 
782 void
sandbox_slave_run(sandbox_slave_t * sb)783 sandbox_slave_run(sandbox_slave_t *sb)
784 {
785 	if(sb && sb->driver && sb->driver->run_cb)
786 		sb->driver->run_cb(sb, sb->update_rate, sb->data);
787 }
788 
789 const char *
sandbox_slave_title_get(sandbox_slave_t * sb)790 sandbox_slave_title_get(sandbox_slave_t *sb)
791 {
792 	if(sb)
793 		return sb->window_title;
794 
795 	return NULL;
796 }
797 
798 bool
sandbox_slave_no_user_resize_get(sandbox_slave_t * sb)799 sandbox_slave_no_user_resize_get(sandbox_slave_t *sb)
800 {
801 	if(sb)
802 		return sb->no_user_resize;
803 
804 	return false;
805 }
806