aboutsummaryrefslogtreecommitdiff
path: root/sandbox_ui.lv2
diff options
context:
space:
mode:
authorHanspeter Portner <dev@open-music-kontrollers.ch>2016-04-03 22:52:48 +0200
committerHanspeter Portner <dev@open-music-kontrollers.ch>2016-04-03 22:56:50 +0200
commitf18ac20e55169a3e8583075ad273a11474a7a2a4 (patch)
treee1408b5138f064d042cf2dae59e34055cbdbc4a9 /sandbox_ui.lv2
parenta0a9843bdfcc3b3abe0d741f3aab6820bc47002a (diff)
downloadsherlock.lv2-f18ac20e55169a3e8583075ad273a11474a7a2a4.tar.xz
prototype sandbox_ui.
Diffstat (limited to 'sandbox_ui.lv2')
-rw-r--r--sandbox_ui.lv2/sandbox_efl.c192
-rw-r--r--sandbox_ui.lv2/sandbox_io.h552
-rw-r--r--sandbox_ui.lv2/sandbox_master.c100
-rw-r--r--sandbox_ui.lv2/sandbox_master.h71
-rw-r--r--sandbox_ui.lv2/sandbox_slave.c563
-rw-r--r--sandbox_ui.lv2/sandbox_slave.h77
6 files changed, 1555 insertions, 0 deletions
diff --git a/sandbox_ui.lv2/sandbox_efl.c b/sandbox_ui.lv2/sandbox_efl.c
new file mode 100644
index 0000000..90211de
--- /dev/null
+++ b/sandbox_ui.lv2/sandbox_efl.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <sandbox_slave.h>
+
+#include <Elementary.h>
+
+typedef struct _app_t app_t;
+
+struct _app_t {
+ sandbox_slave_t *sb;
+
+ Evas_Object *win;
+ Evas_Object *bg;
+ Evas_Object *widget;
+ Ecore_Fd_Handler *fd;
+};
+
+static Eina_Bool
+_recv(void *data, Ecore_Fd_Handler *fd_handler)
+{
+ sandbox_slave_t *sb = data;
+
+ sandbox_slave_recv(sb);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static void
+_del_request(void *data, Evas_Object *obj, void *event_info)
+{
+ app_t *app = data;
+
+ elm_exit();
+ app->bg = NULL;
+ app->win = NULL;
+}
+
+static inline int
+_init(sandbox_slave_t *sb, void *data)
+{
+ app_t *app= data;
+
+ int w = 640;
+ int h = 360;
+
+ const char *title = sandbox_slave_title_get(sb);
+ app->win = elm_win_add(NULL, title, ELM_WIN_BASIC);
+ if(!app->win)
+ {
+ fprintf(stderr, "elm_win_add failed\n");
+ goto fail;
+ }
+ elm_win_title_set(app->win, title);
+ elm_win_autodel_set(app->win, EINA_TRUE);
+ evas_object_smart_callback_add(app->win, "delete,request", _del_request, app);
+
+ app->bg = elm_bg_add(app->win);
+ if(!app->bg)
+ {
+ fprintf(stderr, "elm_bg_add failed\n");
+ goto fail;
+ }
+ elm_bg_color_set(app->bg, 64, 64, 64);
+ evas_object_size_hint_weight_set(app->bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(app->bg, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_show(app->bg);
+ elm_win_resize_object_add(app->win, app->bg);
+
+ if( sandbox_slave_instantiate(sb, (void *)app->win, (void *)&app->widget)
+ || !app->widget)
+ {
+ fprintf(stderr, "sandbox_slave_instantiate failed\n");
+ goto fail;
+ }
+ evas_object_size_hint_weight_set(app->widget, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(app->widget, EVAS_HINT_FILL, EVAS_HINT_FILL);
+
+ // get widget size hint
+ int W, H;
+ evas_object_size_hint_min_get(app->widget, &W, &H);
+ if(W != 0)
+ w = W;
+ if(H != 0)
+ h = H;
+ evas_object_show(app->widget);
+ elm_win_resize_object_add(app->win, app->widget);
+
+ evas_object_resize(app->win, w, h);
+ evas_object_show(app->win);
+
+ int fd;
+ sandbox_slave_fd_get(sb, &fd);
+ if(fd == -1)
+ {
+ fprintf(stderr, "sandbox_slave_instantiate failed\n");
+ goto fail;
+ }
+
+ app->fd= ecore_main_fd_handler_add(fd, ECORE_FD_READ,
+ _recv, sb, NULL, NULL);
+ if(!app->fd)
+ {
+ fprintf(stderr, "ecore_main_fd_handler_add failed\n");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ return -1;
+}
+
+static inline void
+_run(sandbox_slave_t *sb, void *data)
+{
+ app_t *app = data;
+
+ elm_run();
+}
+
+static inline void
+_deinit(void *data)
+{
+ app_t *app = data;
+
+ if(app->fd)
+ ecore_main_fd_handler_del(app->fd);
+
+ if(app->bg)
+ {
+ elm_win_resize_object_del(app->win, app->bg);
+ evas_object_hide(app->bg);
+ evas_object_del(app->bg);
+ }
+
+ if(app->win)
+ {
+ evas_object_hide(app->win);
+ evas_object_del(app->win);
+ }
+}
+
+static const sandbox_slave_driver_t driver = {
+ .init_cb = _init,
+ .run_cb = _run,
+ .deinit_cb = _deinit,
+ .resize_cb = NULL
+};
+
+static int
+elm_main(int argc, char **argv)
+{
+ static app_t app;
+
+#ifdef ELM_1_10
+ elm_config_accel_preference_set("gl");
+#endif
+
+ app.sb = sandbox_slave_new(argc, argv, &driver, &app);
+ if(app.sb)
+ {
+ sandbox_slave_run(app.sb);
+ sandbox_slave_free(app.sb);
+ printf("bye from %s\n", argv[0]);
+ return 0;
+ }
+
+ printf("fail from %s\n", argv[0]);
+ return -1;
+}
+
+ELM_MAIN();
diff --git a/sandbox_ui.lv2/sandbox_io.h b/sandbox_ui.lv2/sandbox_io.h
new file mode 100644
index 0000000..1fca98e
--- /dev/null
+++ b/sandbox_ui.lv2/sandbox_io.h
@@ -0,0 +1,552 @@
+/*
+ * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _SANDBOX_IO_H
+#define _SANDBOX_IO_H
+
+#include <sratom/sratom.h>
+
+#include <nanomsg/nn.h>
+#include <nanomsg/pair.h>
+
+#include <lv2/lv2plug.in/ns/lv2core/lv2.h>
+#include <lv2/lv2plug.in/ns/ext/urid/urid.h>
+#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
+#include <lv2/lv2plug.in/ns/extensions/ui/ui.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RDF_PREFIX "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+
+#undef LV2_ATOM_TUPLE_FOREACH // there is a bug in LV2 1.10.0
+#define LV2_ATOM_TUPLE_FOREACH(tuple, iter) \
+ for (LV2_Atom* (iter) = lv2_atom_tuple_begin(tuple); \
+ !lv2_atom_tuple_is_end(LV2_ATOM_BODY(tuple), (tuple)->atom.size, (iter)); \
+ (iter) = lv2_atom_tuple_next(iter))
+
+typedef struct _atom_ser_t atom_ser_t;
+typedef struct _sandbox_io_subscription_t sandbox_io_subscription_t;
+typedef struct _sandbox_io_t sandbox_io_t;
+
+typedef void (*_sandbox_io_recv_cb_t)(void *data, uint32_t index, uint32_t size,
+ uint32_t format, const void *buf);
+typedef void (*_sandbox_io_subscribe_cb_t)(void *data, uint32_t index,
+ uint32_t protocol, bool state);
+
+struct _atom_ser_t {
+ uint32_t size;
+ uint8_t *buf;
+ uint32_t offset;
+};
+
+struct _sandbox_io_subscription_t {
+ uint32_t protocol;
+ int32_t state;
+};
+
+struct _sandbox_io_t {
+ LV2_URID_Map *map;
+ LV2_URID_Unmap *unmap;
+
+ int sock;
+ int id;
+
+ Sratom *sratom;
+ atom_ser_t ser;
+ LV2_Atom_Forge_Frame frame;
+ LV2_Atom_Forge forge;
+
+ const char *base_uri;
+ SerdNode subject;
+ SerdNode predicate;
+
+ LV2_URID float_protocol;
+ LV2_URID peak_protocol;
+ LV2_URID event_transfer;
+ LV2_URID atom_transfer;
+ LV2_URID core_index;
+ LV2_URID rdf_value;
+ LV2_URID ui_protocol;
+ LV2_URID ui_period_start;
+ LV2_URID ui_period_size;
+ LV2_URID ui_peak;
+ LV2_URID ui_window_title;
+ LV2_URID ui_port_subscribe;
+};
+
+static inline LV2_Atom_Forge_Ref
+_sink(LV2_Atom_Forge_Sink_Handle handle, const void *buf, uint32_t size)
+{
+ atom_ser_t *ser = handle;
+
+ const LV2_Atom_Forge_Ref ref = ser->offset + 1;
+
+ const uint32_t new_offset = ser->offset + size;
+ if(new_offset > ser->size)
+ {
+ uint32_t new_size = ser->size << 1;
+ while(new_offset > new_size)
+ new_size <<= 1;
+
+ if(!(ser->buf = realloc(ser->buf, new_size)))
+ return 0; // realloc failed
+
+ ser->size = new_size;
+ }
+
+ memcpy(ser->buf + ser->offset, buf, size);
+ ser->offset = new_offset;
+
+ return ref;
+}
+
+static inline LV2_Atom *
+_deref(LV2_Atom_Forge_Sink_Handle handle, LV2_Atom_Forge_Ref ref)
+{
+ atom_ser_t *ser = handle;
+
+ const uint32_t offset = ref - 1;
+
+ return (LV2_Atom *)(ser->buf + offset);
+}
+
+static inline void
+_sandbox_io_reset(sandbox_io_t *io)
+{
+ atom_ser_t *ser = &io->ser;
+ ser->offset = 0; //TODO free?
+
+ lv2_atom_forge_set_sink(&io->forge, _sink, _deref, ser);
+ lv2_atom_forge_tuple(&io->forge, &io->frame);
+}
+
+static inline void
+_sandbox_io_recv(sandbox_io_t *io, _sandbox_io_recv_cb_t recv_cb,
+ _sandbox_io_subscribe_cb_t subscribe_cb, void *data)
+{
+ char *ttl = NULL;
+ int res;
+
+ while((res = nn_recv(io->sock, &ttl, NN_MSG, NN_DONTWAIT)) != -1)
+ {
+ //printf("%s\n\n", ttl);
+ LV2_Atom *atom = sratom_from_turtle(io->sratom, io->base_uri,
+ &io->subject, &io->predicate, ttl);
+ if(atom)
+ {
+ LV2_ATOM_TUPLE_FOREACH((LV2_Atom_Tuple *)atom, itm)
+ {
+ LV2_Atom_Object *obj = (LV2_Atom_Object *)itm;
+
+ if(!lv2_atom_forge_is_object_type(&io->forge, obj->atom.type))
+ continue;
+
+ if(obj->body.otype == io->float_protocol)
+ {
+ const LV2_Atom_Int *index = NULL;
+ const LV2_Atom_Float *value = NULL;
+ LV2_Atom_Object_Query q [] = {
+ {io->core_index, (const LV2_Atom **)&index},
+ {io->rdf_value, (const LV2_Atom **)&value},
+ {0, NULL}
+ };
+ lv2_atom_object_query(obj, q);
+
+ if( index && (index->atom.type == io->forge.Int)
+ && value && (value->atom.type == io->forge.Float)
+ && (value->atom.size == sizeof(float)) )
+ {
+ recv_cb(data, index->body,
+ sizeof(float), io->float_protocol, &value->body);
+ recv_cb(data, index->body,
+ sizeof(float), 0, &value->body);
+ }
+ }
+ else if(obj->body.otype == io->peak_protocol)
+ {
+ const LV2_Atom_Int *index = NULL;
+ const LV2_Atom_Int *period_start = NULL;
+ const LV2_Atom_Int *period_size = NULL;
+ const LV2_Atom_Float *peak= NULL;
+ LV2_Atom_Object_Query q [] = {
+ {io->core_index, (const LV2_Atom **)&index},
+ {io->ui_period_start, (const LV2_Atom **)&period_start},
+ {io->ui_period_size, (const LV2_Atom **)&period_size},
+ {io->ui_peak, (const LV2_Atom **)&peak},
+ {0, NULL}
+ };
+ lv2_atom_object_query(obj, q);
+
+ if( index && (index->atom.type == io->forge.Int)
+ && period_start && (period_start->atom.type == io->forge.Int)
+ && (period_start->atom.size == sizeof(int32_t))
+ && period_size && (period_size->atom.type == io->forge.Int)
+ && (period_size->atom.size == sizeof(int32_t))
+ && peak && (peak->atom.type == io->forge.Float)
+ && (peak->atom.size == sizeof(float)) )
+ {
+ const LV2UI_Peak_Data peak_data = {
+ .period_start = period_start->body,
+ .period_size = period_size->body,
+ .peak = peak->body
+ };
+ recv_cb(data, index->body,
+ sizeof(LV2UI_Peak_Data), io->peak_protocol, &peak_data);
+ }
+ }
+ else if(obj->body.otype == io->event_transfer)
+ {
+ const LV2_Atom_Int *index = NULL;
+ const LV2_Atom *value = NULL;
+ LV2_Atom_Object_Query q [] = {
+ {io->core_index, (const LV2_Atom **)&index},
+ {io->rdf_value, (const LV2_Atom **)&value},
+ {0, NULL}
+ };
+ lv2_atom_object_query(obj, q);
+
+ if( index && (index->atom.type == io->forge.Int)
+ && value)
+ {
+ recv_cb(data, index->body,
+ lv2_atom_total_size(value), io->event_transfer, value);
+ }
+ }
+ else if(obj->body.otype == io->atom_transfer)
+ {
+ const LV2_Atom_Int *index = NULL;
+ const LV2_Atom *value = NULL;
+ LV2_Atom_Object_Query q [] = {
+ {io->core_index, (const LV2_Atom **)&index},
+ {io->rdf_value, (const LV2_Atom **)&value},
+ {0, NULL}
+ };
+ lv2_atom_object_query(obj, q);
+
+ if( index && (index->atom.type == io->forge.Int)
+ && value)
+ {
+ recv_cb(data, index->body,
+ lv2_atom_total_size(value), io->atom_transfer, value);
+ }
+ }
+ else if(obj->body.otype == io->ui_port_subscribe)
+ {
+ const LV2_Atom_Int *index = NULL;
+ const LV2_Atom_URID *protocol = NULL;
+ const LV2_Atom_Bool *value = NULL;
+ LV2_Atom_Object_Query q [] = {
+ {io->core_index, (const LV2_Atom **)&index},
+ {io->ui_protocol, (const LV2_Atom **)&protocol},
+ {io->rdf_value, (const LV2_Atom **)&value},
+ {0, NULL}
+ };
+ lv2_atom_object_query(obj, q);
+
+ if( index && (index->atom.type == io->forge.Int)
+ && value && (value->atom.type == io->forge.Bool)
+ && protocol && (protocol->atom.type == io->forge.URID))
+ {
+ if(subscribe_cb)
+ subscribe_cb(data, index->body, protocol->body, value->body);
+ }
+ }
+ }
+
+ free(atom);
+ }
+
+ nn_freemsg(ttl);
+ }
+
+ switch(nn_errno())
+ {
+ case EAGAIN:
+ // do nothing
+ break;
+ case ETERM:
+ //FIXME done
+ // fall-through
+ default:
+ fprintf(stderr, "nn_recv: %s\n", nn_strerror(nn_errno()));
+ break;
+ }
+}
+
+static inline bool
+_sandbox_io_flush(sandbox_io_t *io)
+{
+ const LV2_Atom *atom = (const LV2_Atom *)io->ser.buf;
+
+ bool more = false;
+ if( (io->ser.offset == 0) || (atom->size == 0) )
+ return more; // empty tuple
+
+ char *ttl = sratom_to_turtle(io->sratom, io->unmap,
+ io->base_uri, &io->subject, &io->predicate,
+ atom->type, atom->size, LV2_ATOM_BODY_CONST(atom));
+
+ if(ttl)
+ {
+ const size_t len = strlen(ttl) + 1;
+ //printf("sending: %zu\n\n%s\n\n", len, ttl);
+
+ if(nn_send(io->sock, ttl, len, NN_DONTWAIT) == -1)
+ {
+ switch(nn_errno())
+ {
+ case EAGAIN:
+ more = true;
+ break;
+ case ETERM:
+ //FIXME done
+ // fall-through
+ default:
+ fprintf(stderr, "nn_send: %s\n", nn_strerror(nn_errno()));
+ break;
+ }
+ }
+ else
+ {
+ _sandbox_io_reset(io);
+ }
+
+ free(ttl);
+ }
+
+ return more;
+}
+
+static inline void
+_sandbox_io_clean(LV2_Atom_Forge *forge, LV2_Atom *atom)
+{
+ if(atom->type == forge->Object)
+ {
+ LV2_Atom_Object *obj = (LV2_Atom_Object *)atom;
+
+ if(obj->body.id != 0)
+ obj->body.id = 0; // if not, sratom will fail
+
+ LV2_ATOM_OBJECT_FOREACH(obj, prop)
+ {
+ _sandbox_io_clean(forge, &prop->value);
+ }
+ }
+ else if(atom->type == forge->Tuple)
+ {
+ LV2_Atom_Tuple *tup = (LV2_Atom_Tuple *)atom;
+
+ LV2_ATOM_TUPLE_FOREACH(tup, itm)
+ {
+ _sandbox_io_clean(forge, itm);
+ }
+ }
+ else if(atom->type == forge->Sequence)
+ {
+ LV2_Atom_Sequence *seq = (LV2_Atom_Sequence *)atom;
+
+ LV2_ATOM_SEQUENCE_FOREACH(seq, ev)
+ {
+ _sandbox_io_clean(forge, &ev->body);
+ }
+ }
+}
+
+static inline bool
+_sandbox_io_send(sandbox_io_t *io, uint32_t index,
+ uint32_t size, uint32_t protocol, const void *buf)
+{
+ LV2_Atom_Forge_Frame frame;
+
+ if(protocol == 0)
+ protocol = io->float_protocol;
+
+ lv2_atom_forge_object(&io->forge, &frame, 0, protocol);
+
+ lv2_atom_forge_key(&io->forge, io->core_index);
+ lv2_atom_forge_int(&io->forge, index);
+
+ if(protocol == io->float_protocol)
+ {
+ const float *value = buf;
+
+ lv2_atom_forge_key(&io->forge, io->rdf_value);
+ lv2_atom_forge_float(&io->forge, *value);
+ }
+ else if(protocol == io->peak_protocol)
+ {
+ const LV2UI_Peak_Data *peak_data = buf;
+
+ lv2_atom_forge_key(&io->forge, io->ui_period_start);
+ lv2_atom_forge_int(&io->forge, peak_data->period_start);
+
+ lv2_atom_forge_key(&io->forge, io->ui_period_size);
+ lv2_atom_forge_int(&io->forge, peak_data->period_size);
+
+ lv2_atom_forge_key(&io->forge, io->ui_peak);
+ lv2_atom_forge_float(&io->forge, peak_data->peak);
+ }
+ else if(protocol == io->event_transfer)
+ {
+ const LV2_Atom *atom = buf;
+ LV2_Atom_Forge_Ref ref;
+
+ lv2_atom_forge_key(&io->forge, io->rdf_value);
+ ref = lv2_atom_forge_atom(&io->forge, atom->size, atom->type);
+ lv2_atom_forge_write(&io->forge, LV2_ATOM_BODY_CONST(atom), atom->size);
+
+ LV2_Atom *src= lv2_atom_forge_deref(&io->forge, ref);
+ _sandbox_io_clean(&io->forge, src);
+ }
+ else if(protocol == io->atom_transfer)
+ {
+ const LV2_Atom *atom = buf;
+ LV2_Atom_Forge_Ref ref;
+
+ lv2_atom_forge_key(&io->forge, io->rdf_value);
+ ref = lv2_atom_forge_atom(&io->forge, atom->size, atom->type);
+ lv2_atom_forge_write(&io->forge, LV2_ATOM_BODY_CONST(atom), atom->size);
+
+ LV2_Atom *src= lv2_atom_forge_deref(&io->forge, ref);
+ _sandbox_io_clean(&io->forge, src);
+ }
+ else if(protocol == io->ui_port_subscribe)
+ {
+ const sandbox_io_subscription_t *sub = buf;
+
+ lv2_atom_forge_key(&io->forge, io->rdf_value);
+ lv2_atom_forge_bool(&io->forge, sub->state);
+
+ lv2_atom_forge_key(&io->forge, io->ui_protocol);
+ lv2_atom_forge_urid(&io->forge, protocol);
+ }
+
+ lv2_atom_forge_pop(&io->forge, &frame);
+
+ //lv2_atom_forge_pop(&io->forge, &io->frame);
+
+ return _sandbox_io_flush(io);
+}
+
+static inline int
+_sandbox_io_fd_get(sandbox_io_t *io)
+{
+ int fd = -1;
+ size_t sz = sizeof(int);
+
+ if(io->sock == -1)
+ return -1;
+
+ if(nn_getsockopt(io->sock, NN_SOL_SOCKET, NN_RCVFD, &fd, &sz) < 0)
+ return -1;
+
+ //printf("_sandbox_io_fd_get: %i\n", fd);
+
+ return fd;
+}
+
+static inline int
+_sandbox_io_init(sandbox_io_t *io, LV2_URID_Map *map, LV2_URID_Unmap *unmap,
+ const char *socket_path, bool is_master)
+{
+ io->base_uri = "file:///tmp/base";
+ io->subject = serd_node_from_string(SERD_URI, (const uint8_t *)(""));
+ io->predicate = serd_node_from_string(SERD_URI, (const uint8_t *)(LV2_ATOM__atomTransfer));
+
+ io->map = map;
+ io->unmap = unmap;
+
+ io->ser.offset = 0;
+ io->ser.size = 1024;
+ io->ser.buf = malloc(io->ser.size);
+ if(!io->ser.buf)
+ return -1;
+
+ if(!(io->sratom = sratom_new(map)))
+ return -1;
+ sratom_set_pretty_numbers(io->sratom, false);
+ sratom_set_object_mode(io->sratom, SRATOM_OBJECT_MODE_BLANK);
+
+ if((io->sock = nn_socket(AF_SP, NN_PAIR)) == -1)
+ return -1;
+
+ const int ms = 10000;
+ if(nn_setsockopt(io->sock, NN_SOL_SOCKET, NN_LINGER, &ms, sizeof(ms)) == -1)
+ return -1;
+
+ if(is_master)
+ {
+ if((io->id = nn_bind(io->sock, socket_path)) == -1)
+ return -1;
+ }
+ else // is_slave
+ {
+ if((io->id = nn_connect(io->sock, socket_path)) == -1)
+ return -1;
+ }
+
+ lv2_atom_forge_init(&io->forge, map);
+
+ io->float_protocol = map->map(map->handle, LV2_UI_PREFIX"floatProtocol");
+ io->peak_protocol = map->map(map->handle, LV2_UI_PREFIX"peakProtocol");
+ io->event_transfer = map->map(map->handle, LV2_ATOM__eventTransfer);
+ io->atom_transfer = map->map(map->handle, LV2_ATOM__atomTransfer);
+ io->core_index = map->map(map->handle, LV2_CORE__index);
+ io->rdf_value = map->map(map->handle, RDF_PREFIX"value");
+ io->ui_protocol = map->map(map->handle, LV2_UI_PREFIX"protocol");
+ io->ui_period_start = map->map(map->handle, LV2_UI_PREFIX"periodStart");
+ io->ui_period_size = map->map(map->handle, LV2_UI_PREFIX"periodSize");
+ io->ui_peak = map->map(map->handle, LV2_UI_PREFIX"peak");
+ io->ui_window_title = map->map(map->handle, LV2_UI__windowTitle);
+ io->ui_port_subscribe = map->map(map->handle, LV2_UI__portSubscribe);
+
+ _sandbox_io_reset(io);
+
+ return 0;
+}
+
+static inline void
+_sandbox_io_deinit(sandbox_io_t *io)
+{
+ if(io->id != -1)
+ {
+ int res = nn_shutdown(io->sock, io->id);
+ if(res < 0)
+ fprintf(stderr, "nn_shutdown failed: %s\n", nn_strerror(nn_errno()));
+ }
+
+ if(io->sock != -1)
+ {
+ int res = nn_close(io->sock);
+ if(res < 0)
+ fprintf(stderr, "nn_close failed: %s\n", nn_strerror(nn_errno()));
+ }
+
+ if(io->sratom)
+ sratom_free(io->sratom);
+
+ if(io->ser.buf)
+ free(io->ser.buf);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sandbox_ui.lv2/sandbox_master.c b/sandbox_ui.lv2/sandbox_master.c
new file mode 100644
index 0000000..c61e1d0
--- /dev/null
+++ b/sandbox_ui.lv2/sandbox_master.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <sandbox_master.h>
+#include <sandbox_io.h>
+
+#include <lv2/lv2plug.in/ns/ext/log/log.h>
+#include <lv2/lv2plug.in/ns/ext/options/options.h>
+#include <lv2/lv2plug.in/ns/extensions/ui/ui.h>
+
+struct _sandbox_master_t {
+ sandbox_io_t io;
+
+ sandbox_master_driver_t *driver;
+ void *data;
+};
+
+sandbox_master_t *
+sandbox_master_new(sandbox_master_driver_t *driver, void *data)
+{
+ sandbox_master_t *sb = calloc(1, sizeof(sandbox_master_t));
+ if(!sb)
+ goto fail;
+
+ sb->driver = driver;
+ sb->data = data;
+
+ if(_sandbox_io_init(&sb->io, driver->map, driver->unmap, driver->socket_path, true))
+ goto fail;
+
+ return sb;
+
+fail:
+ sandbox_master_free(sb);
+ return NULL;
+}
+
+void
+sandbox_master_free(sandbox_master_t *sb)
+{
+ if(sb)
+ {
+ _sandbox_io_deinit(&sb->io);
+ free(sb);
+ }
+}
+
+void
+sandbox_master_recv(sandbox_master_t *sb)
+{
+ if(sb)
+ _sandbox_io_recv(&sb->io, sb->driver->recv_cb, sb->driver->subscribe_cb, sb->data);
+}
+
+bool
+sandbox_master_send(sandbox_master_t *sb, uint32_t index, uint32_t size,
+ uint32_t format, const void *buf)
+{
+ if(sb)
+ return _sandbox_io_send(&sb->io, index, size, format, buf);
+
+ return false;
+}
+
+bool
+sandbox_master_flush(sandbox_master_t *sb)
+{
+ if(sb)
+ return _sandbox_io_flush(&sb->io);
+
+ return false;
+}
+
+void
+sandbox_master_fd_get(sandbox_master_t *sb, int *fd)
+{
+ if(sb && fd)
+ *fd = _sandbox_io_fd_get(&sb->io);
+ else if(fd)
+ *fd = -1;
+}
diff --git a/sandbox_ui.lv2/sandbox_master.h b/sandbox_ui.lv2/sandbox_master.h
new file mode 100644
index 0000000..44c6957
--- /dev/null
+++ b/sandbox_ui.lv2/sandbox_master.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _SANDBOX_MASTER_H
+#define _SANDBOX_MASTER_H
+
+#include <lv2/lv2plug.in/ns/ext/urid/urid.h>
+
+#ifdef _WIN32
+# define SANDBOX_SYMBOL_EXTERN __declspec(dllexport)
+#else
+# define SANDBOX_SYMBOL_EXTERN __attribute__((visibility("default")))
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _sandbox_master_t sandbox_master_t;
+typedef struct _sandbox_master_driver_t sandbox_master_driver_t;
+typedef void (*sandbox_master_recv_cb_t)(void *data, uint32_t index, uint32_t size,
+ uint32_t format, const void *buf);
+typedef void (*sandbox_master_subscribe_cb_t)(void *data, uint32_t index,
+ uint32_t protocol, bool state);
+
+struct _sandbox_master_driver_t {
+ const char *socket_path;
+ LV2_URID_Map *map;
+ LV2_URID_Unmap *unmap;
+ sandbox_master_recv_cb_t recv_cb;
+ sandbox_master_subscribe_cb_t subscribe_cb;
+};
+
+SANDBOX_SYMBOL_EXTERN sandbox_master_t *
+sandbox_master_new(sandbox_master_driver_t *driver, void *data);
+
+SANDBOX_SYMBOL_EXTERN void
+sandbox_master_free(sandbox_master_t *sb);
+
+SANDBOX_SYMBOL_EXTERN void
+sandbox_master_recv(sandbox_master_t *sb);
+
+SANDBOX_SYMBOL_EXTERN bool
+sandbox_master_send(sandbox_master_t *sb, uint32_t index, uint32_t size,
+ uint32_t format, const void *buf);
+
+SANDBOX_SYMBOL_EXTERN bool
+sandbox_master_flush(sandbox_master_t *sb);
+
+SANDBOX_SYMBOL_EXTERN void
+sandbox_master_fd_get(sandbox_master_t *sb, int *fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sandbox_ui.lv2/sandbox_slave.c b/sandbox_ui.lv2/sandbox_slave.c
new file mode 100644
index 0000000..9e1e665
--- /dev/null
+++ b/sandbox_ui.lv2/sandbox_slave.c
@@ -0,0 +1,563 @@
+/*
+ * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <sandbox_slave.h>
+#include <sandbox_io.h>
+
+#include <lv2/lv2plug.in/ns/ext/log/log.h>
+#include <lv2/lv2plug.in/ns/ext/options/options.h>
+#include <lv2/lv2plug.in/ns/extensions/ui/ui.h>
+
+#include <lilv/lilv.h>
+#include <symap.h>
+
+struct _sandbox_slave_t {
+ Symap *symap;
+ LV2_URID_Map map;
+ LV2_URID_Unmap unmap;
+
+ LV2_Log_Log log;
+
+ LV2UI_Port_Map port_map;
+ LV2UI_Port_Subscribe port_subscribe;
+
+ LV2UI_Resize host_resize;
+ const LV2UI_Resize *client_resize;
+
+ const LV2UI_Idle_Interface *idle_iface;
+
+ LilvWorld *world;
+ LilvNode *bundle_node;
+ LilvNode *plugin_node;
+ LilvNode *ui_node;
+
+ const LilvPlugin *plug;
+ const LilvUI *ui;
+
+ void *lib;
+ const LV2UI_Descriptor *desc;
+ void *handle;
+
+ sandbox_io_t io;
+
+ const sandbox_slave_driver_t *driver;
+ void *data;
+
+ const char *plugin_uri;
+ const char *bundle_path;
+ const char *ui_uri;
+ const char *socket_path;
+ const char *window_title;
+};
+
+static inline LV2_URID
+_map(void *data, const char *uri)
+{
+ sandbox_slave_t *sb= data;
+
+ return symap_map(sb->symap, uri);
+}
+
+static inline const char *
+_unmap(void *data, LV2_URID urid)
+{
+ sandbox_slave_t *sb= data;
+
+ return symap_unmap(sb->symap, urid);
+}
+
+static inline int
+_log_vprintf(LV2_Log_Handle handle, LV2_URID type, const char *fmt, va_list args)
+{
+ sandbox_slave_t *sb = handle;
+
+ vprintf(fmt, args);
+
+ return 0;
+}
+
+static inline int
+_log_printf(LV2_Log_Handle handle, LV2_URID type, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ const int ret = _log_vprintf(handle, type, fmt, args);
+ va_end(args);
+
+ return ret;
+}
+
+static inline uint32_t
+_port_index(LV2UI_Feature_Handle handle, const char *symbol)
+{
+ sandbox_slave_t *sb = handle;
+ uint32_t index = LV2UI_INVALID_PORT_INDEX;
+
+ LilvNode *symbol_uri = lilv_new_string(sb->world, symbol);
+ if(symbol_uri)
+ {
+ const LilvPort *port = lilv_plugin_get_port_by_symbol(sb->plug, symbol_uri);
+
+ if(port)
+ index = lilv_port_get_index(sb->plug, port);
+
+ lilv_node_free(symbol_uri);
+ }
+
+ return index;
+}
+
+static inline void
+_write_function(LV2UI_Controller controller, uint32_t index,
+ uint32_t size, uint32_t protocol, const void *buf)
+{
+ sandbox_slave_t *sb = controller;
+
+ const bool more = _sandbox_io_send(&sb->io, index, size, protocol, buf);
+ (void)more; //TODO
+}
+
+static inline uint32_t
+_port_subscribe(LV2UI_Feature_Handle handle, uint32_t index, uint32_t protocol,
+ const LV2_Feature *const *features)
+{
+ sandbox_slave_t *sb = handle;
+
+ const sandbox_io_subscription_t sub = {
+ .state = 1,
+ .protocol = protocol
+ };
+ _write_function(handle, index, sizeof(sandbox_io_subscription_t), sb->io.ui_port_subscribe, &sub);
+
+ return 0;
+}
+
+static inline uint32_t
+_port_unsubscribe(LV2UI_Feature_Handle handle, uint32_t index, uint32_t protocol,
+ const LV2_Feature *const *features)
+{
+ sandbox_slave_t *sb = handle;
+
+ const sandbox_io_subscription_t sub = {
+ .state = 0,
+ .protocol = protocol
+ };
+ _write_function(handle, index, sizeof(sandbox_io_subscription_t), sb->io.ui_port_subscribe, &sub);
+
+ return 0;
+}
+
+static inline void
+_sandbox_recv_cb(LV2UI_Handle handle, uint32_t index, uint32_t size,
+ uint32_t protocol, const void *buf)
+{
+ sandbox_slave_t *sb = handle;
+
+ if(sb->desc && sb->desc->port_event)
+ sb->desc->port_event(sb->handle, index, size, protocol, buf);
+}
+
+static inline int
+_sandbox_resize(sandbox_slave_t *sb, int w, int h)
+{
+ if(sb->client_resize)
+ return sb->client_resize->ui_resize(sb->data, w, h);
+
+ return 0;
+}
+
+sandbox_slave_t *
+sandbox_slave_new(int argc, char **argv, const sandbox_slave_driver_t *driver, void *data)
+{
+ sandbox_slave_t *sb = calloc(1, sizeof(sandbox_slave_t));
+ if(!sb)
+ {
+ fprintf(stderr, "allocation failed\n");
+ goto fail;
+ }
+
+ int c;
+ while((c = getopt(argc, argv, "p:b:u:s:w:")) != -1)
+ {
+ switch(c)
+ {
+ case 'p':
+ sb->plugin_uri = optarg;
+ break;
+ case 'b':
+ sb->bundle_path = optarg;
+ break;
+ case 'u':
+ sb->ui_uri = optarg;
+ break;
+ case 's':
+ sb->socket_path = optarg;
+ break;
+ case 'w':
+ sb->window_title = optarg;
+ break;
+ case '?':
+ if( (optopt == 'p') || (optopt == 'b') || (optopt == 'u') || (optopt == 's') || (optopt == 'w') )
+ fprintf(stderr, "Option `-%c' requires an argument.\n", optopt);
+ else if(isprint(optopt))
+ fprintf(stderr, "Unknown option `-%c'.\n", optopt);
+ else
+ fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
+ goto fail;
+ default:
+ goto fail;
+ }
+ }
+
+ if( !sb->plugin_uri
+ || !sb->bundle_path
+ || !sb->ui_uri
+ || !sb->socket_path
+ || !sb->window_title)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ goto fail;
+ }
+
+ sb->driver = driver;
+ sb->data = data;
+
+ sb->map.handle = sb;
+ sb->map.map = _map;
+
+ sb->unmap.handle = sb;
+ sb->unmap.unmap = _unmap;
+
+ sb->log.handle = sb;
+ sb->log.printf = _log_printf;
+ sb->log.vprintf = _log_vprintf;
+
+ sb->port_map.handle = sb;
+ sb->port_map.port_index = _port_index;
+
+ sb->port_subscribe.handle = sb;
+ sb->port_subscribe.subscribe = _port_subscribe;
+ sb->port_subscribe.unsubscribe = _port_unsubscribe;
+
+ sb->host_resize.handle = data;
+ sb->host_resize.ui_resize = driver->resize_cb;
+
+ if(!(sb->symap = symap_new()))
+ {
+ fprintf(stderr, "symap_new failed\n");
+ goto fail;
+ }
+
+ if(!(sb->world = lilv_world_new()))
+ {
+ fprintf(stderr, "lilv_world_new failed\n");
+ goto fail;
+ }
+
+ sb->bundle_node = lilv_new_file_uri(sb->world, NULL, sb->bundle_path);
+ sb->plugin_node = lilv_new_uri(sb->world, sb->plugin_uri);
+ sb->ui_node = lilv_new_uri(sb->world, sb->ui_uri);
+
+ if(!sb->bundle_node || !sb->plugin_node || !sb->ui_node)
+ {
+ fprintf(stderr, "lilv_new_uri failes\n");
+ goto fail;
+ }
+
+ lilv_world_load_bundle(sb->world, sb->bundle_node);
+ lilv_world_load_resource(sb->world, sb->ui_node);
+
+ const LilvPlugins *plugins = lilv_world_get_all_plugins(sb->world);
+ if(!plugins)
+ {
+ fprintf(stderr, "lilv_world_get_all_plugins failed\n");
+ goto fail;
+ }
+
+ if(!(sb->plug = lilv_plugins_get_by_uri(plugins, sb->plugin_node)))
+ {
+ fprintf(stderr, "lilv_plugins_get_by_uri failed\n");
+ goto fail;
+ }
+
+ LilvUIs *uis = lilv_plugin_get_uis(sb->plug);
+ if(!uis)
+ {
+ fprintf(stderr, "lilv_plugin_get_uis failed\n");
+ goto fail;
+ }
+
+ if(!(sb->ui = lilv_uis_get_by_uri(uis, sb->ui_node)))
+ {
+ fprintf(stderr, "lilv_uis_get_by_uri failed\n");
+ goto fail;
+ }
+
+ const LilvNode *ui_path = lilv_ui_get_binary_uri(sb->ui);
+ if(!ui_path)
+ {
+ fprintf(stderr, "lilv_ui_get_binary_uri failed\n");
+ goto fail;
+ }
+
+#if defined(LILV_0_22)
+ char *binary_path = lilv_file_uri_parse(lilv_node_as_string(ui_path), NULL);
+#else
+ const char *binary_path = lilv_uri_to_path(lilv_node_as_string(ui_path));
+#endif
+ if(!(sb->lib = dlopen(binary_path, RTLD_LAZY)))
+ {
+ fprintf(stderr, "dlopen failed: %s\n", dlerror());
+ goto fail;
+ }
+
+#if defined(LILV_0_22)
+ lilv_free(binary_path);
+#endif
+
+ LV2UI_DescriptorFunction desc_func = dlsym(sb->lib, "lv2ui_descriptor");
+ if(!desc_func)
+ {
+ fprintf(stderr, "dlsym failed\n");
+ goto fail;
+ }
+
+ for(int i=0; true; i++)
+ {
+ const LV2UI_Descriptor *desc = desc_func(i);
+ if(!desc) // sentinel
+ break;
+
+ if(!strcmp(desc->URI, sb->ui_uri))
+ {
+ sb->desc = desc;
+ break;
+ }
+ }
+
+ if(!sb->desc)
+ {
+ fprintf(stderr, "LV2UI_Descriptor lookup failed\n");
+ goto fail;
+ }
+
+ if(sb->desc->extension_data)
+ {
+ sb->idle_iface = sb->desc->extension_data(LV2_UI__idleInterface);
+ sb->client_resize= sb->desc->extension_data(LV2_UI__resize);
+ }
+
+ if(_sandbox_io_init(&sb->io, &sb->map, &sb->unmap, sb->socket_path, false))
+ {
+ fprintf(stderr, "_sandbox_io_init failed\n");
+ goto fail;
+ }
+
+ if(driver->init_cb && (driver->init_cb(sb, data) != 0) )
+ {
+ fprintf(stderr, "driver->init_cb failed\n");
+ goto fail;
+ }
+
+ return sb; // success
+
+fail:
+ sandbox_slave_free(sb);
+ return NULL;
+}
+
+void
+sandbox_slave_free(sandbox_slave_t *sb)
+{
+ if(!sb)
+ return;
+
+ if(sb->desc && sb->desc->cleanup)
+ sb->desc->cleanup(sb->handle);
+
+ if(sb->driver && sb->driver->deinit_cb)
+ sb->driver->deinit_cb(sb->data);
+
+ _sandbox_io_deinit(&sb->io);
+
+ if(sb->lib)
+ dlclose(sb->lib);
+
+ if(sb->world)
+ {
+ if(sb->ui_node)
+ {
+ lilv_world_unload_resource(sb->world, sb->ui_node);
+ lilv_node_free(sb->ui_node);
+ }
+
+ if(sb->bundle_node)
+ {
+ lilv_world_unload_bundle(sb->world, sb->bundle_node);
+ lilv_node_free(sb->bundle_node);
+ }
+
+ if(sb->plugin_node)
+ lilv_node_free(sb->plugin_node);
+
+ lilv_world_free(sb->world);
+ }
+
+ if(sb->symap)
+ symap_free(sb->symap);
+
+ free(sb);
+}
+
+int
+sandbox_slave_instantiate(sandbox_slave_t *sb, void *parent, void *widget)
+{
+ LV2_Options_Option options [] = {
+ [0] = {
+ .context = LV2_OPTIONS_INSTANCE,
+ .subject = 0,
+ .key = sb->io.ui_window_title,
+ .size = strlen(sb->plugin_uri) + 1,
+ .type = sb->io.forge.String,
+ .value = sb->plugin_uri
+ },
+ [1] = {
+ .key = 0,
+ .value = NULL
+ }
+ };
+
+ const LV2_Feature map_feature = {
+ .URI = LV2_URID__map,
+ .data = &sb->map
+ };
+ const LV2_Feature unmap_feature = {
+ .URI = LV2_URID__unmap,
+ .data = &sb->unmap
+ };
+ const LV2_Feature parent_feature = {
+ .URI = LV2_UI__parent,
+ .data = parent
+ };
+ const LV2_Feature log_feature = {
+ .URI = LV2_LOG__log,
+ .data = &sb->log
+ };
+ const LV2_Feature port_map_feature = {
+ .URI = LV2_UI__portMap,
+ .data = &sb->port_map
+ };
+ const LV2_Feature port_subscribe_feature = {
+ .URI = LV2_UI__portSubscribe,
+ .data = &sb->port_subscribe
+ };
+ const LV2_Feature idle_feature = {
+ .URI = LV2_UI__idleInterface,
+ .data = NULL
+ };
+ const LV2_Feature resize_feature = {
+ .URI = LV2_UI__resize,
+ .data = &sb->host_resize
+ };
+ const LV2_Feature options_feature = {
+ .URI = LV2_OPTIONS__options,
+ .data = options
+ };
+
+ const LV2_Feature *const features [] = {
+ &map_feature,
+ &unmap_feature,
+ &parent_feature,
+ &log_feature,
+ &port_map_feature,
+ &port_subscribe_feature,
+ &idle_feature,
+ &options_feature,
+ sb->host_resize.ui_resize ? &resize_feature : NULL,
+ NULL
+ };
+
+ if(sb->desc && sb->desc->instantiate)
+ {
+ sb->handle = sb->desc->instantiate(sb->desc, sb->plugin_uri,
+ sb->bundle_path, _write_function, sb, widget, features);
+ }
+
+ if(sb->handle)
+ return 0; // success
+
+ return -1;
+}
+
+void
+sandbox_slave_recv(sandbox_slave_t *sb)
+{
+ if(sb)
+ _sandbox_io_recv(&sb->io, _sandbox_recv_cb, NULL, sb);
+}
+
+bool
+sandbox_slave_flush(sandbox_slave_t *sb)
+{
+ if(sb)
+ return _sandbox_io_flush(&sb->io);
+
+ return false;
+}
+
+int
+sandbox_slave_idle(sandbox_slave_t *sb)
+{
+ if(sb && sb->idle_iface)
+ return sb->idle_iface->idle(sb->handle);
+
+ return 0;
+}
+
+void
+sandbox_slave_run(sandbox_slave_t *sb)
+{
+ if(sb && sb->driver && sb->driver->run_cb)
+ sb->driver->run_cb(sb, sb->data);
+}
+
+void
+sandbox_slave_fd_get(sandbox_slave_t *sb, int *fd)
+{
+ if(sb && fd)
+ *fd = _sandbox_io_fd_get(&sb->io);
+ else if(fd)
+ *fd = -1;
+}
+
+const char *
+sandbox_slave_title_get(sandbox_slave_t *sb)
+{
+ if(sb)
+ return sb->window_title;
+
+ return NULL;
+}
diff --git a/sandbox_ui.lv2/sandbox_slave.h b/sandbox_ui.lv2/sandbox_slave.h
new file mode 100644
index 0000000..bea0b85
--- /dev/null
+++ b/sandbox_ui.lv2/sandbox_slave.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _SANDBOX_SLAVE_H
+#define _SANDBOX_SLAVE_H
+
+#ifdef _WIN32
+# define SANDBOX_SYMBOL_EXTERN __declspec(dllexport)
+#else
+# define SANDBOX_SYMBOL_EXTERN __attribute__((visibility("default")))
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _sandbox_slave_t sandbox_slave_t;
+typedef struct _sandbox_slave_driver_t sandbox_slave_driver_t;
+
+typedef int (*sandbox_slave_driver_init_t)(sandbox_slave_t *sb, void *data);
+typedef void (*sandbox_slave_driver_run_t)(sandbox_slave_t *sb, void *data);
+typedef void (*sandbox_slave_driver_deinit_t)(void *data);
+typedef int (*sandbox_slave_driver_resize_t)(void *data, int width, int height);
+
+struct _sandbox_slave_driver_t {
+ sandbox_slave_driver_init_t init_cb;
+ sandbox_slave_driver_run_t run_cb;
+ sandbox_slave_driver_deinit_t deinit_cb;
+ sandbox_slave_driver_resize_t resize_cb;
+};
+
+SANDBOX_SYMBOL_EXTERN sandbox_slave_t *
+sandbox_slave_new(int argc, char **argv, const sandbox_slave_driver_t *driver, void *data);
+
+SANDBOX_SYMBOL_EXTERN void
+sandbox_slave_free(sandbox_slave_t *sb);
+
+SANDBOX_SYMBOL_EXTERN int
+sandbox_slave_instantiate(sandbox_slave_t *sb, void *parent, void *widget);
+
+SANDBOX_SYMBOL_EXTERN void
+sandbox_slave_recv(sandbox_slave_t *sb);
+
+SANDBOX_SYMBOL_EXTERN bool
+sandbox_slave_flush(sandbox_slave_t *sb);
+
+SANDBOX_SYMBOL_EXTERN int
+sandbox_slave_idle(sandbox_slave_t *sb);
+
+SANDBOX_SYMBOL_EXTERN void
+sandbox_slave_run(sandbox_slave_t *sb);
+
+SANDBOX_SYMBOL_EXTERN void
+sandbox_slave_fd_get(sandbox_slave_t *sb, int *fd);
+
+SANDBOX_SYMBOL_EXTERN const char *
+sandbox_slave_title_get(sandbox_slave_t *sb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif