|
|
@@ -0,0 +1,388 @@ |
|
|
|
commit 109070a71a1e780385b803731fbdd2689752c644 |
|
|
|
Author: Robin Gareus <robin@gareus.org> |
|
|
|
Date: Tue Jan 7 17:48:52 2014 +0100 |
|
|
|
|
|
|
|
add support for externalUI |
|
|
|
|
|
|
|
diff --git a/jalv/src/jalv.c b/jalv/src/jalv.c |
|
|
|
index e80f9f9..e0aa1a0 100644 |
|
|
|
--- a/jalv/src/jalv.c |
|
|
|
+++ b/jalv/src/jalv.c |
|
|
|
@@ -649,32 +649,73 @@ void |
|
|
|
jalv_ui_instantiate(Jalv* jalv, const char* native_ui_type, void* parent) |
|
|
|
{ |
|
|
|
jalv->ui_host = suil_host_new(jalv_ui_write, jalv_ui_port_index, NULL, NULL); |
|
|
|
+ jalv->extuiptr = NULL; |
|
|
|
+ |
|
|
|
+ if (jalv->externalui) { |
|
|
|
+ |
|
|
|
+ const LV2_Feature external_lv_feature = { |
|
|
|
+ LV2_EXTERNAL_UI_DEPRECATED_URI, parent |
|
|
|
+ }; |
|
|
|
+ const LV2_Feature external_kx_feature = { |
|
|
|
+ LV2_EXTERNAL_UI__Host, parent |
|
|
|
+ }; |
|
|
|
+ const LV2_Feature instance_feature = { |
|
|
|
+ NS_EXT "instance-access", lilv_instance_get_handle(jalv->instance) |
|
|
|
+ }; |
|
|
|
+ const LV2_Feature* ui_features[] = { |
|
|
|
+ &uri_map_feature, &map_feature, &unmap_feature, |
|
|
|
+ &instance_feature, |
|
|
|
+ &log_feature, |
|
|
|
+ &external_lv_feature, |
|
|
|
+ &external_kx_feature, |
|
|
|
+ &options_feature, |
|
|
|
+ NULL |
|
|
|
+ }; |
|
|
|
+ |
|
|
|
+ jalv->ui_instance = suil_instance_new( |
|
|
|
+ jalv->ui_host, |
|
|
|
+ jalv, |
|
|
|
+ native_ui_type, |
|
|
|
+ lilv_node_as_uri(lilv_plugin_get_uri(jalv->plugin)), |
|
|
|
+ lilv_node_as_uri(lilv_ui_get_uri(jalv->ui)), |
|
|
|
+ lilv_node_as_uri(jalv->ui_type), |
|
|
|
+ lilv_uri_to_path(lilv_node_as_uri(lilv_ui_get_bundle_uri(jalv->ui))), |
|
|
|
+ lilv_uri_to_path(lilv_node_as_uri(lilv_ui_get_binary_uri(jalv->ui))), |
|
|
|
+ ui_features); |
|
|
|
|
|
|
|
- const LV2_Feature parent_feature = { |
|
|
|
- LV2_UI__parent, parent |
|
|
|
- }; |
|
|
|
- const LV2_Feature instance_feature = { |
|
|
|
- NS_EXT "instance-access", lilv_instance_get_handle(jalv->instance) |
|
|
|
- }; |
|
|
|
- const LV2_Feature* ui_features[] = { |
|
|
|
- &uri_map_feature, &map_feature, &unmap_feature, |
|
|
|
- &instance_feature, |
|
|
|
- &log_feature, |
|
|
|
- &parent_feature, |
|
|
|
- &options_feature, |
|
|
|
- NULL |
|
|
|
- }; |
|
|
|
+ if (jalv->ui_instance) { |
|
|
|
+ jalv->extuiptr = suil_instance_get_widget((SuilInstance*)jalv->ui_instance); |
|
|
|
+ } else { |
|
|
|
+ jalv->externalui = false; |
|
|
|
+ } |
|
|
|
+ } else { |
|
|
|
|
|
|
|
- jalv->ui_instance = suil_instance_new( |
|
|
|
- jalv->ui_host, |
|
|
|
- jalv, |
|
|
|
- native_ui_type, |
|
|
|
- lilv_node_as_uri(lilv_plugin_get_uri(jalv->plugin)), |
|
|
|
- lilv_node_as_uri(lilv_ui_get_uri(jalv->ui)), |
|
|
|
- lilv_node_as_uri(jalv->ui_type), |
|
|
|
- lilv_uri_to_path(lilv_node_as_uri(lilv_ui_get_bundle_uri(jalv->ui))), |
|
|
|
- lilv_uri_to_path(lilv_node_as_uri(lilv_ui_get_binary_uri(jalv->ui))), |
|
|
|
- ui_features); |
|
|
|
+ const LV2_Feature parent_feature = { |
|
|
|
+ LV2_UI__parent, parent |
|
|
|
+ }; |
|
|
|
+ const LV2_Feature instance_feature = { |
|
|
|
+ NS_EXT "instance-access", lilv_instance_get_handle(jalv->instance) |
|
|
|
+ }; |
|
|
|
+ const LV2_Feature* ui_features[] = { |
|
|
|
+ &uri_map_feature, &map_feature, &unmap_feature, |
|
|
|
+ &instance_feature, |
|
|
|
+ &log_feature, |
|
|
|
+ &parent_feature, |
|
|
|
+ &options_feature, |
|
|
|
+ NULL |
|
|
|
+ }; |
|
|
|
+ |
|
|
|
+ jalv->ui_instance = suil_instance_new( |
|
|
|
+ jalv->ui_host, |
|
|
|
+ jalv, |
|
|
|
+ native_ui_type, |
|
|
|
+ lilv_node_as_uri(lilv_plugin_get_uri(jalv->plugin)), |
|
|
|
+ lilv_node_as_uri(lilv_ui_get_uri(jalv->ui)), |
|
|
|
+ lilv_node_as_uri(jalv->ui_type), |
|
|
|
+ lilv_uri_to_path(lilv_node_as_uri(lilv_ui_get_bundle_uri(jalv->ui))), |
|
|
|
+ lilv_uri_to_path(lilv_node_as_uri(lilv_ui_get_binary_uri(jalv->ui))), |
|
|
|
+ ui_features); |
|
|
|
+ } |
|
|
|
|
|
|
|
/* Set initial control values on UI */ |
|
|
|
if (jalv->ui_instance) { |
|
|
|
@@ -688,6 +729,7 @@ jalv_ui_instantiate(Jalv* jalv, const char* native_ui_type, void* parent) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
+ |
|
|
|
bool |
|
|
|
jalv_ui_is_resizable(Jalv* jalv) |
|
|
|
{ |
|
|
|
@@ -795,6 +837,9 @@ jalv_emit_ui_events(Jalv* jalv) |
|
|
|
jalv_ui_port_event(jalv, ev.index, ev.size, ev.protocol, buf); |
|
|
|
} |
|
|
|
} |
|
|
|
+ if (jalv->externalui && jalv->extuiptr) { |
|
|
|
+ LV2_EXTERNAL_UI_RUN(jalv->extuiptr); |
|
|
|
+ } |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
@@ -938,6 +983,8 @@ main(int argc, char** argv) |
|
|
|
jalv.nodes.rsz_minimumSize = lilv_new_uri(world, LV2_RESIZE_PORT__minimumSize); |
|
|
|
jalv.nodes.work_interface = lilv_new_uri(world, LV2_WORKER__interface); |
|
|
|
jalv.nodes.work_schedule = lilv_new_uri(world, LV2_WORKER__schedule); |
|
|
|
+ jalv.nodes.ui_externallv = lilv_new_uri(world, "http://lv2plug.in/ns/extensions/ui#external"); |
|
|
|
+ jalv.nodes.ui_externalkx = lilv_new_uri(world, "http://kxstudio.sf.net/ns/lv2ext/external-ui#Widget"); |
|
|
|
jalv.nodes.end = NULL; |
|
|
|
|
|
|
|
/* Get plugin URI from loaded state or command line */ |
|
|
|
@@ -1013,11 +1060,31 @@ main(int argc, char** argv) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
+ if (!jalv.ui) { |
|
|
|
+ LILV_FOREACH(uis, u, jalv.uis) { |
|
|
|
+ const LilvUI* ui = lilv_uis_get(jalv.uis, u); |
|
|
|
+ const LilvNodes* types = lilv_ui_get_classes(ui); |
|
|
|
+ LILV_FOREACH(nodes, t, types) { |
|
|
|
+ const char * pt = lilv_node_as_uri(lilv_nodes_get(types, t)); |
|
|
|
+ if (!strcmp(pt, "http://kxstudio.sf.net/ns/lv2ext/external-ui#Widget")) { |
|
|
|
+ jalv.externalui = true; |
|
|
|
+ jalv.ui = ui; |
|
|
|
+ jalv.ui_type = jalv.nodes.ui_externalkx; |
|
|
|
+ } else if (!strcmp(pt, "http://lv2plug.in/ns/extensions/ui#external")) { |
|
|
|
+ jalv.externalui = true; |
|
|
|
+ jalv.ui_type = jalv.nodes.ui_externallv; |
|
|
|
+ jalv.ui = ui; |
|
|
|
+ } |
|
|
|
+ } |
|
|
|
+ } |
|
|
|
+ } |
|
|
|
|
|
|
|
/* Create ringbuffers for UI if necessary */ |
|
|
|
if (jalv.ui) { |
|
|
|
fprintf(stderr, "UI: %s\n", |
|
|
|
lilv_node_as_uri(lilv_ui_get_uri(jalv.ui))); |
|
|
|
+ fprintf(stderr, "UI Type: %s\n", |
|
|
|
+ lilv_node_as_uri(jalv.ui_type)); |
|
|
|
} else { |
|
|
|
fprintf(stderr, "No appropriate UI found\n"); |
|
|
|
} |
|
|
|
diff --git a/jalv/src/jalv_gtk.c b/jalv/src/jalv_gtk.c |
|
|
|
index bb92dbb..5599c0c 100644 |
|
|
|
--- a/jalv/src/jalv_gtk.c |
|
|
|
+++ b/jalv/src/jalv_gtk.c |
|
|
|
@@ -803,18 +803,28 @@ build_menu(Jalv* jalv, GtkWidget* window, GtkWidget* vbox) |
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 0); |
|
|
|
} |
|
|
|
|
|
|
|
+void |
|
|
|
+on_external_ui_closed(void* controller) |
|
|
|
+{ |
|
|
|
+ Jalv* jalv = (Jalv*) controller; |
|
|
|
+ jalv_close_ui(jalv); |
|
|
|
+} |
|
|
|
+ |
|
|
|
int |
|
|
|
jalv_open_ui(Jalv* jalv) |
|
|
|
{ |
|
|
|
+ LV2_External_UI_Host extui; |
|
|
|
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
|
|
|
jalv->window = window; |
|
|
|
jalv->has_ui = TRUE; |
|
|
|
+ extui.ui_closed = on_external_ui_closed; |
|
|
|
|
|
|
|
g_signal_connect(window, "destroy", |
|
|
|
G_CALLBACK(on_window_destroy), jalv); |
|
|
|
|
|
|
|
LilvNode* name = lilv_plugin_get_name(jalv->plugin); |
|
|
|
gtk_window_set_title(GTK_WINDOW(window), lilv_node_as_string(name)); |
|
|
|
+ extui.plugin_human_id = jalv_strdup(lilv_node_as_string(name)); |
|
|
|
lilv_node_free(name); |
|
|
|
|
|
|
|
GtkWidget* vbox = new_box(false, 0); |
|
|
|
@@ -832,10 +842,16 @@ jalv_open_ui(Jalv* jalv) |
|
|
|
|
|
|
|
/* Attempt to instantiate custom UI if necessary */ |
|
|
|
if (jalv->ui && !jalv->opts.generic_ui) { |
|
|
|
- jalv_ui_instantiate(jalv, jalv_native_ui_type(jalv), alignment); |
|
|
|
+ if (jalv->externalui) { |
|
|
|
+ jalv_ui_instantiate(jalv, lilv_node_as_uri(jalv->ui_type), &extui); |
|
|
|
+ } else { |
|
|
|
+ jalv_ui_instantiate(jalv, jalv_native_ui_type(jalv), alignment); |
|
|
|
+ } |
|
|
|
} |
|
|
|
|
|
|
|
- if (jalv->ui_instance) { |
|
|
|
+ if (jalv->externalui && jalv->extuiptr) { |
|
|
|
+ LV2_EXTERNAL_UI_SHOW(jalv->extuiptr); |
|
|
|
+ } else if (jalv->ui_instance) { |
|
|
|
GtkWidget* widget = (GtkWidget*)suil_instance_get_widget( |
|
|
|
jalv->ui_instance); |
|
|
|
|
|
|
|
@@ -843,6 +859,7 @@ jalv_open_ui(Jalv* jalv) |
|
|
|
gtk_window_set_resizable(GTK_WINDOW(window), jalv_ui_is_resizable(jalv)); |
|
|
|
gtk_widget_show_all(vbox); |
|
|
|
gtk_widget_grab_focus(widget); |
|
|
|
+ gtk_window_present(GTK_WINDOW(window)); |
|
|
|
} else { |
|
|
|
GtkWidget* controls = build_control_widget(jalv, window); |
|
|
|
GtkWidget* scroll_win = gtk_scrolled_window_new(NULL, NULL); |
|
|
|
@@ -862,13 +879,12 @@ jalv_open_ui(Jalv* jalv) |
|
|
|
GTK_WINDOW(window), |
|
|
|
MAX(MAX(box_size.width, controls_size.width) + 24, 640), |
|
|
|
box_size.height + controls_size.height); |
|
|
|
+ gtk_window_present(GTK_WINDOW(window)); |
|
|
|
} |
|
|
|
|
|
|
|
g_timeout_add(1000 / jalv->ui_update_hz, |
|
|
|
(GSourceFunc)jalv_emit_ui_events, jalv); |
|
|
|
|
|
|
|
- gtk_window_present(GTK_WINDOW(window)); |
|
|
|
- |
|
|
|
gtk_main(); |
|
|
|
zix_sem_post(jalv->done); |
|
|
|
return 0; |
|
|
|
diff --git a/jalv/src/jalv_internal.h b/jalv/src/jalv_internal.h |
|
|
|
index 4531ee6..f24c05a 100644 |
|
|
|
--- a/jalv/src/jalv_internal.h |
|
|
|
+++ b/jalv/src/jalv_internal.h |
|
|
|
@@ -44,6 +44,8 @@ |
|
|
|
#include "lv2_evbuf.h" |
|
|
|
#include "symap.h" |
|
|
|
|
|
|
|
+#include "lv2_external_ui.h" |
|
|
|
+ |
|
|
|
#ifdef __cplusplus |
|
|
|
extern "C" { |
|
|
|
#endif |
|
|
|
@@ -139,6 +141,8 @@ typedef struct { |
|
|
|
LilvNode* rsz_minimumSize; |
|
|
|
LilvNode* work_interface; |
|
|
|
LilvNode* work_schedule; |
|
|
|
+ LilvNode* ui_externallv; |
|
|
|
+ LilvNode* ui_externalkx; |
|
|
|
LilvNode* end; ///< NULL terminator for easy freeing of entire structure |
|
|
|
} JalvNodes; |
|
|
|
|
|
|
|
@@ -204,6 +208,8 @@ typedef struct { |
|
|
|
bool buf_size_set; ///< True iff buffer size callback fired |
|
|
|
bool exit; ///< True iff execution is finished |
|
|
|
bool has_ui; ///< True iff a control UI is present |
|
|
|
+ bool externalui; ///< True iff plugin has an external-ui |
|
|
|
+ LV2_External_UI_Widget* extuiptr; ///< data structure used for external-ui |
|
|
|
} Jalv; |
|
|
|
|
|
|
|
int |
|
|
|
diff --git a/jalv/src/lv2_external_ui.h b/jalv/src/lv2_external_ui.h |
|
|
|
new file mode 100644 |
|
|
|
index 0000000..2c9e6ee |
|
|
|
--- /dev/null |
|
|
|
+++ b/jalv/src/lv2_external_ui.h |
|
|
|
@@ -0,0 +1,109 @@ |
|
|
|
+/* |
|
|
|
+ LV2 External UI extension |
|
|
|
+ This work is in public domain. |
|
|
|
+ |
|
|
|
+ This file 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. |
|
|
|
+ |
|
|
|
+ If you have questions, contact Filipe Coelho (aka falkTX) <falktx@falktx.com> |
|
|
|
+ or ask in #lad channel, FreeNode IRC network. |
|
|
|
+*/ |
|
|
|
+ |
|
|
|
+/** |
|
|
|
+ @file lv2_external_ui.h |
|
|
|
+ C header for the LV2 External UI extension <http://kxstudio.sf.net/ns/lv2ext/external-ui>. |
|
|
|
+*/ |
|
|
|
+ |
|
|
|
+#ifndef LV2_EXTERNAL_UI_H |
|
|
|
+#define LV2_EXTERNAL_UI_H |
|
|
|
+ |
|
|
|
+#include "lv2/lv2plug.in/ns/extensions/ui/ui.h" |
|
|
|
+ |
|
|
|
+#define LV2_EXTERNAL_UI_URI "http://kxstudio.sf.net/ns/lv2ext/external-ui" |
|
|
|
+#define LV2_EXTERNAL_UI_PREFIX LV2_EXTERNAL_UI_URI "#" |
|
|
|
+ |
|
|
|
+#define LV2_EXTERNAL_UI__Host LV2_EXTERNAL_UI_PREFIX "Host" |
|
|
|
+#define LV2_EXTERNAL_UI__Widget LV2_EXTERNAL_UI_PREFIX "Widget" |
|
|
|
+ |
|
|
|
+/** This extension used to be defined by a lv2plug.in URI */ |
|
|
|
+#define LV2_EXTERNAL_UI_DEPRECATED_URI "http://lv2plug.in/ns/extensions/ui#external" |
|
|
|
+ |
|
|
|
+#ifdef __cplusplus |
|
|
|
+extern "C" { |
|
|
|
+#endif |
|
|
|
+ |
|
|
|
+/** |
|
|
|
+ * When LV2_EXTERNAL_UI__Widget UI is instantiated, the returned |
|
|
|
+ * LV2UI_Widget handle must be cast to pointer to LV2_External_UI_Widget. |
|
|
|
+ * UI is created in invisible state. |
|
|
|
+ */ |
|
|
|
+typedef struct _LV2_External_UI_Widget { |
|
|
|
+ /** |
|
|
|
+ * Host calls this function regulary. UI library implementing the |
|
|
|
+ * callback may do IPC or redraw the UI. |
|
|
|
+ * |
|
|
|
+ * @param _this_ the UI context |
|
|
|
+ */ |
|
|
|
+ void (*run)(struct _LV2_External_UI_Widget * _this_); |
|
|
|
+ |
|
|
|
+ /** |
|
|
|
+ * Host calls this function to make the plugin UI visible. |
|
|
|
+ * |
|
|
|
+ * @param _this_ the UI context |
|
|
|
+ */ |
|
|
|
+ void (*show)(struct _LV2_External_UI_Widget * _this_); |
|
|
|
+ |
|
|
|
+ /** |
|
|
|
+ * Host calls this function to make the plugin UI invisible again. |
|
|
|
+ * |
|
|
|
+ * @param _this_ the UI context |
|
|
|
+ */ |
|
|
|
+ void (*hide)(struct _LV2_External_UI_Widget * _this_); |
|
|
|
+ |
|
|
|
+} LV2_External_UI_Widget; |
|
|
|
+ |
|
|
|
+#define LV2_EXTERNAL_UI_RUN(ptr) (ptr)->run(ptr) |
|
|
|
+#define LV2_EXTERNAL_UI_SHOW(ptr) (ptr)->show(ptr) |
|
|
|
+#define LV2_EXTERNAL_UI_HIDE(ptr) (ptr)->hide(ptr) |
|
|
|
+ |
|
|
|
+/** |
|
|
|
+ * On UI instantiation, host must supply LV2_EXTERNAL_UI__Host feature. |
|
|
|
+ * LV2_Feature::data must be pointer to LV2_External_UI_Host. |
|
|
|
+ */ |
|
|
|
+typedef struct _LV2_External_UI_Host { |
|
|
|
+ /** |
|
|
|
+ * Callback that plugin UI will call when UI (GUI window) is closed by user. |
|
|
|
+ * This callback will be called during execution of LV2_External_UI_Widget::run() |
|
|
|
+ * (i.e. not from background thread). |
|
|
|
+ * |
|
|
|
+ * After this callback is called, UI is defunct. Host must call LV2UI_Descriptor::cleanup(). |
|
|
|
+ * If host wants to make the UI visible again, the UI must be reinstantiated. |
|
|
|
+ * |
|
|
|
+ * @note When using the depreated URI LV2_EXTERNAL_UI_DEPRECATED_URI, |
|
|
|
+ * some hosts will not call LV2UI_Descriptor::cleanup() as they should, |
|
|
|
+ * and may call show() again without re-initialization. |
|
|
|
+ * |
|
|
|
+ * @param controller Host context associated with plugin UI, as |
|
|
|
+ * supplied to LV2UI_Descriptor::instantiate(). |
|
|
|
+ */ |
|
|
|
+ void (*ui_closed)(LV2UI_Controller controller); |
|
|
|
+ |
|
|
|
+ /** |
|
|
|
+ * Optional (may be NULL) "user friendly" identifier which the UI |
|
|
|
+ * may display to allow a user to easily associate this particular |
|
|
|
+ * UI instance with the correct plugin instance as it is represented |
|
|
|
+ * by the host (e.g. "track 1" or "channel 4"). |
|
|
|
+ * |
|
|
|
+ * If supplied by host, the string will be referenced only during |
|
|
|
+ * LV2UI_Descriptor::instantiate() |
|
|
|
+ */ |
|
|
|
+ const char * plugin_human_id; |
|
|
|
+ |
|
|
|
+} LV2_External_UI_Host; |
|
|
|
+ |
|
|
|
+#ifdef __cplusplus |
|
|
|
+} /* extern "C" */ |
|
|
|
+#endif |
|
|
|
+ |
|
|
|
+#endif /* LV2_EXTERNAL_UI_H */ |