aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/jalv.c96
-rw-r--r--src/jalv_console.c31
-rw-r--r--src/jalv_gtk.c42
-rw-r--r--src/jalv_gtkmm2.cpp2
-rw-r--r--src/jalv_internal.h8
-rw-r--r--src/jalv_qt.cpp2
-rw-r--r--src/worker.c37
7 files changed, 184 insertions, 34 deletions
diff --git a/src/jalv.c b/src/jalv.c
index adee072..09b15e3 100644
--- a/src/jalv.c
+++ b/src/jalv.c
@@ -411,6 +411,7 @@ jalv_ui_instantiate(Jalv* jalv, const char* native_ui_type, void* parent)
&parent_feature,
&jalv->features.options_feature,
&idle_feature,
+ &jalv->features.request_value_feature,
NULL
};
@@ -731,6 +732,65 @@ setup_signals(Jalv* const jalv)
#endif
}
+static const LilvUI*
+jalv_select_custom_ui(const Jalv* const jalv)
+{
+ const char* const native_ui_type_uri = jalv_native_ui_type();
+
+ if (jalv->opts.ui_uri) {
+ // Specific UI explicitly requested by user
+ LilvNode* uri = lilv_new_uri(jalv->world, jalv->opts.ui_uri);
+ const LilvUI* ui = lilv_uis_get_by_uri(jalv->uis, uri);
+
+ lilv_node_free(uri);
+ return ui;
+ }
+
+#ifdef HAVE_SUIL
+ if (native_ui_type_uri) {
+ // Try to find an embeddable UI
+ LilvNode* native_type = lilv_new_uri(jalv->world, native_ui_type_uri);
+
+ LILV_FOREACH (uis, u, jalv->uis) {
+ const LilvUI* ui = lilv_uis_get(jalv->uis, u);
+ const LilvNode* type = NULL;
+ const bool supported = lilv_ui_is_supported(
+ ui, suil_ui_supported, native_type, &type);
+
+ if (supported) {
+ lilv_node_free(native_type);
+ return ui;
+ }
+ }
+
+ lilv_node_free(native_type);
+ }
+#endif
+
+ if (!native_ui_type_uri && jalv->opts.show_ui) {
+ // Try to find a UI with ui:showInterface
+ LILV_FOREACH (uis, u, jalv->uis) {
+ const LilvUI* ui = lilv_uis_get(jalv->uis, u);
+ const LilvNode* ui_node = lilv_ui_get_uri(ui);
+
+ lilv_world_load_resource(jalv->world, ui_node);
+
+ const bool supported = lilv_world_ask(jalv->world,
+ ui_node,
+ jalv->nodes.lv2_extensionData,
+ jalv->nodes.ui_showInterface);
+
+ lilv_world_unload_resource(jalv->world, ui_node);
+
+ if (supported) {
+ return ui;
+ }
+ }
+ }
+
+ return NULL;
+}
+
int
jalv_open(Jalv* const jalv, int* argc, char*** argv)
{
@@ -844,6 +904,10 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
init_feature(&jalv->features.log_feature,
LV2_LOG__log, &jalv->features.llog);
+ jalv->features.request_value.handle = jalv;
+ init_feature(&jalv->features.request_value_feature,
+ LV2_UI__requestValue, &jalv->features.request_value);
+
zix_sem_init(&jalv->done, 0);
zix_sem_init(&jalv->paused, 0);
@@ -870,6 +934,7 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
jalv->nodes.lv2_control = lilv_new_uri(world, LV2_CORE__control);
jalv->nodes.lv2_default = lilv_new_uri(world, LV2_CORE__default);
jalv->nodes.lv2_enumeration = lilv_new_uri(world, LV2_CORE__enumeration);
+ jalv->nodes.lv2_extensionData = lilv_new_uri(world, LV2_CORE__extensionData);
jalv->nodes.lv2_integer = lilv_new_uri(world, LV2_CORE__integer);
jalv->nodes.lv2_maximum = lilv_new_uri(world, LV2_CORE__maximum);
jalv->nodes.lv2_minimum = lilv_new_uri(world, LV2_CORE__minimum);
@@ -889,6 +954,7 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
jalv->nodes.rdfs_label = lilv_new_uri(world, LILV_NS_RDFS "label");
jalv->nodes.rdfs_range = lilv_new_uri(world, LILV_NS_RDFS "range");
jalv->nodes.rsz_minimumSize = lilv_new_uri(world, LV2_RESIZE_PORT__minimumSize);
+ jalv->nodes.ui_showInterface = lilv_new_uri(world, LV2_UI__showInterface);
jalv->nodes.work_interface = lilv_new_uri(world, LV2_WORKER__interface);
jalv->nodes.work_schedule = lilv_new_uri(world, LV2_WORKER__schedule);
jalv->nodes.end = NULL;
@@ -963,25 +1029,23 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
}
/* Get a plugin UI */
- const char* native_ui_type_uri = jalv_native_ui_type();
jalv->uis = lilv_plugin_get_uis(jalv->plugin);
- if (!jalv->opts.generic_ui && native_ui_type_uri) {
-#ifdef HAVE_SUIL
- const LilvNode* native_ui_type = lilv_new_uri(jalv->world, native_ui_type_uri);
- LILV_FOREACH(uis, u, jalv->uis) {
- const LilvUI* this_ui = lilv_uis_get(jalv->uis, u);
- if (lilv_ui_is_supported(this_ui,
- suil_ui_supported,
- native_ui_type,
- &jalv->ui_type)) {
- /* TODO: Multiple UI support */
- jalv->ui = this_ui;
- break;
+ if (!jalv->opts.generic_ui) {
+ if ((jalv->ui = jalv_select_custom_ui(jalv))) {
+ const char* host_type_uri = jalv_native_ui_type();
+ if (host_type_uri) {
+ LilvNode* host_type = lilv_new_uri(jalv->world, host_type_uri);
+
+ if (!lilv_ui_is_supported(jalv->ui,
+ suil_ui_supported,
+ host_type,
+ &jalv->ui_type)) {
+ jalv->ui = NULL;
+ }
+
+ lilv_node_free(host_type);
}
}
-#endif
- } else if (!jalv->opts.generic_ui && jalv->opts.show_ui) {
- jalv->ui = lilv_uis_get(jalv->uis, lilv_uis_begin(jalv->uis));
}
/* Create ringbuffers for UI if necessary */
diff --git a/src/jalv_console.c b/src/jalv_console.c
index 7d11714..2d4a197 100644
--- a/src/jalv_console.c
+++ b/src/jalv_console.c
@@ -38,6 +38,7 @@ print_usage(const char* name, bool error)
fprintf(os, " -b SIZE Buffer size for plugin <=> UI communication\n");
fprintf(os, " -c SYM=VAL Set control value (e.g. \"vol=1.4\")\n");
fprintf(os, " -d Dump plugin <=> UI communication\n");
+ fprintf(os, " -U URI Load the UI with the given URI\n");
fprintf(os, " -h Display this help and exit\n");
fprintf(os, " -l DIR Load state from save directory\n");
fprintf(os, " -n NAME JACK client name\n");
@@ -50,12 +51,16 @@ print_usage(const char* name, bool error)
}
void
-jalv_ui_port_event(ZIX_UNUSED Jalv* jalv,
- ZIX_UNUSED uint32_t port_index,
- ZIX_UNUSED uint32_t buffer_size,
- ZIX_UNUSED uint32_t protocol,
- ZIX_UNUSED const void* buffer)
+jalv_ui_port_event(Jalv* jalv,
+ uint32_t port_index,
+ uint32_t buffer_size,
+ uint32_t protocol,
+ const void* buffer)
{
+ if (jalv->ui_instance) {
+ suil_instance_port_event(jalv->ui_instance, port_index,
+ buffer_size, protocol, buffer);
+ }
}
int
@@ -70,6 +75,12 @@ jalv_init(int* argc, char*** argv, JalvOptions* opts)
opts->show_ui = true;
} else if ((*argv)[a][1] == 'p') {
opts->print_controls = true;
+ } else if ((*argv)[a][1] == 'U') {
+ if (++a == *argc) {
+ fprintf(stderr, "Missing argument for -U\n");
+ return 1;
+ }
+ opts->ui_uri = jalv_strdup((*argv)[a]);
} else if ((*argv)[a][1] == 'u') {
if (++a == *argc) {
fprintf(stderr, "Missing argument for -u\n");
@@ -155,7 +166,7 @@ jalv_print_preset(Jalv* jalv,
static void
jalv_process_command(Jalv* jalv, const char* cmd)
{
- char sym[64];
+ char sym[1024];
uint32_t index;
float value;
if (!strncmp(cmd, "help", 4)) {
@@ -172,7 +183,7 @@ jalv_process_command(Jalv* jalv, const char* cmd)
} else if (strcmp(cmd, "presets\n") == 0) {
jalv_unload_presets(jalv);
jalv_load_presets(jalv, jalv_print_preset, NULL);
- } else if (sscanf(cmd, "preset %[a-zA-Z0-9_:/-.#]\n", sym) == 1) {
+ } else if (sscanf(cmd, "preset %1023[a-zA-Z0-9_:/-.#]\n", sym) == 1) {
LilvNode* preset = lilv_new_uri(jalv->world, sym);
jalv_apply_preset(jalv, preset);
lilv_node_free(preset);
@@ -188,8 +199,8 @@ jalv_process_command(Jalv* jalv, const char* cmd)
} else {
fprintf(stderr, "error: port index out of range\n");
}
- } else if (sscanf(cmd, "set %[a-zA-Z0-9_] %f", sym, &value) == 2 ||
- sscanf(cmd, "%[a-zA-Z0-9_] = %f", sym, &value) == 2) {
+ } else if (sscanf(cmd, "set %1023[a-zA-Z0-9_] %f", sym, &value) == 2 ||
+ sscanf(cmd, "%1023[a-zA-Z0-9_] = %f", sym, &value) == 2) {
struct Port* port = NULL;
for (uint32_t i = 0; i < jalv->num_ports; ++i) {
struct Port* p = &jalv->ports[i];
@@ -262,7 +273,7 @@ jalv_open_ui(Jalv* jalv)
if (!jalv_run_custom_ui(jalv) && !jalv->opts.non_interactive) {
// Primitive command prompt for setting control values
while (!zix_sem_try_wait(&jalv->done)) {
- char line[128];
+ char line[1024];
printf("> ");
if (fgets(line, sizeof(line), stdin)) {
jalv_process_command(jalv, line);
diff --git a/src/jalv_gtk.c b/src/jalv_gtk.c
index 71259bd..375ea76 100644
--- a/src/jalv_gtk.c
+++ b/src/jalv_gtk.c
@@ -97,6 +97,8 @@ jalv_init(int* argc, char*** argv, JalvOptions* opts)
"Load state from preset", "URI" },
{ "dump", 'd', 0, G_OPTION_ARG_NONE, &opts->dump,
"Dump plugin <=> UI communication", NULL },
+ { "ui-uri", 'U', 0, G_OPTION_ARG_STRING, &opts->ui_uri,
+ "Load the UI with the given URI", "URI" },
{ "trace", 't', 0, G_OPTION_ARG_NONE, &opts->trace,
"Print trace messages from plugin", NULL },
{ "show-hidden", 's', 0, G_OPTION_ARG_NONE, &opts->show_hidden,
@@ -630,6 +632,42 @@ patch_put_get(Jalv* jalv,
return 0;
}
+static LV2UI_Request_Value_Status
+on_request_value(LV2UI_Feature_Handle handle,
+ const LV2_URID key,
+ const LV2_URID type,
+ const LV2_Feature* const* features)
+{
+ Jalv* jalv = (Jalv*)handle;
+ ControlID* control = get_property_control(&jalv->controls, key);
+
+ if (!control) {
+ return LV2UI_REQUEST_VALUE_ERR_UNKNOWN;
+ } else if (control->value_type != jalv->forge.Path) {
+ return LV2UI_REQUEST_VALUE_ERR_UNSUPPORTED;
+ }
+
+ GtkWidget* dialog =
+ gtk_file_chooser_dialog_new("Choose file",
+ GTK_WINDOW(jalv->window),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) {
+ char* path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+
+ set_control(control, strlen(path) + 1, jalv->forge.Path, path);
+
+ g_free(path);
+ }
+
+ gtk_widget_destroy(dialog);
+
+ return 0;
+}
+
static void
property_changed(Jalv* jalv, LV2_URID key, const LV2_Atom* value)
{
@@ -776,7 +814,7 @@ file_changed(GtkFileChooserButton* widget,
const char* filename = gtk_file_chooser_get_filename(
GTK_FILE_CHOOSER(widget));
- set_control(control, strlen(filename), jalv->forge.Path, filename);
+ set_control(control, strlen(filename) + 1, jalv->forge.Path, filename);
}
static Controller*
@@ -1197,6 +1235,8 @@ jalv_open_ui(Jalv* jalv)
jalv_ui_instantiate(jalv, jalv_native_ui_type(), alignment);
}
+ jalv->features.request_value.request = on_request_value;
+
if (jalv->ui_instance) {
GtkWidget* widget = (GtkWidget*)suil_instance_get_widget(
jalv->ui_instance);
diff --git a/src/jalv_gtkmm2.cpp b/src/jalv_gtkmm2.cpp
index 3f5a0e9..3db2157 100644
--- a/src/jalv_gtkmm2.cpp
+++ b/src/jalv_gtkmm2.cpp
@@ -87,6 +87,8 @@ jalv_open_ui(Jalv* jalv)
window->add(*Gtk::manage(button));
}
+ jalv_init_ui(jalv);
+
window->set_resizable(jalv_ui_is_resizable(jalv));
window->show_all();
diff --git a/src/jalv_internal.h b/src/jalv_internal.h
index 4d25109..3bfc102 100644
--- a/src/jalv_internal.h
+++ b/src/jalv_internal.h
@@ -17,6 +17,8 @@
#ifndef JALV_INTERNAL_H
#define JALV_INTERNAL_H
+#define _POSIX_C_SOURCE 200809L
+
#include "lv2_evbuf.h"
#include "symap.h"
@@ -39,6 +41,7 @@
#include "lv2/options/options.h"
#include "lv2/resize-port/resize-port.h"
#include "lv2/state/state.h"
+#include "lv2/ui/ui.h"
#include "lv2/urid/urid.h"
#include "lv2/worker/worker.h"
@@ -174,6 +177,7 @@ typedef struct {
int show_ui; ///< Show non-embedded UI
int print_controls; ///< Print control changes to stdout
int non_interactive; ///< Do not listen for commands on stdin
+ char* ui_uri; ///< URI of UI to load
} JalvOptions;
typedef struct {
@@ -223,6 +227,7 @@ typedef struct {
LilvNode* lv2_control;
LilvNode* lv2_default;
LilvNode* lv2_enumeration;
+ LilvNode* lv2_extensionData;
LilvNode* lv2_integer;
LilvNode* lv2_maximum;
LilvNode* lv2_minimum;
@@ -242,6 +247,7 @@ typedef struct {
LilvNode* rdfs_label;
LilvNode* rdfs_range;
LilvNode* rsz_minimumSize;
+ LilvNode* ui_showInterface;
LilvNode* work_interface;
LilvNode* work_schedule;
LilvNode* end; ///< NULL terminator for easy freeing of entire structure
@@ -278,6 +284,8 @@ typedef struct {
LV2_Options_Option options[6];
LV2_Feature options_feature;
LV2_Feature safe_restore_feature;
+ LV2UI_Request_Value request_value;
+ LV2_Feature request_value_feature;
LV2_Extension_Data_Feature ext_data;
} JalvFeatures;
diff --git a/src/jalv_qt.cpp b/src/jalv_qt.cpp
index f23a005..46f9a92 100644
--- a/src/jalv_qt.cpp
+++ b/src/jalv_qt.cpp
@@ -728,6 +728,8 @@ jalv_open_ui(Jalv* jalv)
win->setCentralWidget(widget);
app->connect(app, SIGNAL(lastWindowClosed()), app, SLOT(quit()));
+ jalv_init_ui(jalv);
+
win->show();
if (jalv->ui_instance && !jalv_ui_is_resizable(jalv)) {
widget->setMinimumSize(widget->width(), widget->height());
diff --git a/src/worker.c b/src/worker.c
index 79bb7b7..8cb09d1 100644
--- a/src/worker.c
+++ b/src/worker.c
@@ -22,6 +22,10 @@ jalv_worker_respond(LV2_Worker_Respond_Handle handle,
const void* data)
{
JalvWorker* worker = (JalvWorker*)handle;
+ if (zix_ring_write_space(worker->responses) < (sizeof(size) + size)) {
+ return LV2_WORKER_ERR_NO_SPACE;
+ }
+
zix_ring_write(worker->responses, (const char*)&size, sizeof(size));
zix_ring_write(worker->responses, (const char*)data, size);
return LV2_WORKER_SUCCESS;
@@ -125,17 +129,36 @@ void
jalv_worker_emit_responses(JalvWorker* worker, LilvInstance* instance)
{
if (worker->responses) {
- uint32_t read_space = zix_ring_read_space(worker->responses);
- while (read_space) {
+ uint32_t read_space = 0;
+ while (read_space = zix_ring_read_space(worker->responses)) {
uint32_t size = 0;
- zix_ring_read(worker->responses, (char*)&size, sizeof(size));
-
- zix_ring_read(worker->responses, (char*)worker->response, size);
+ if (zix_ring_peek(worker->responses, (char*)&size, sizeof(size)) <= 0) {
+ fprintf(stderr, "error: Response buffer corrupted (req %lu avail %u)\n",
+ sizeof(size), read_space);
+ break;
+ }
+
+ const uint32_t packet_size = sizeof(size) + size;
+ if (read_space < packet_size) {
+ fprintf(stderr, "warning: Try to read bigger response (%u) than data available (%u). Retry later.\n",
+ packet_size, read_space);
+ break;
+ }
+
+ if (zix_ring_skip(worker->responses, sizeof(size)) <= 0) {
+ fprintf(stderr, "error: Response buffer corrupted on skip (req %lu avail %u)\n",
+ sizeof(size), read_space);
+ break;
+ }
+
+ if (zix_ring_read(worker->responses, (char*)worker->response, size) <= 0) {
+ fprintf(stderr, "error: Response buffer corrupted on read response (req %u avail %u)\n",
+ size, zix_ring_read_space(worker->responses));
+ break;
+ }
worker->iface->work_response(
instance->lv2_handle, size, worker->response);
-
- read_space -= sizeof(size) + size;
}
}
}