From a5c734a2ad41ad3f97c9f9550f705baab57a7eaa Mon Sep 17 00:00:00 2001 From: David Robillard Date: Tue, 20 Nov 2018 23:26:49 +0100 Subject: WIP: Add feature to request parameter from from UI --- lv2/core/meta.ttl | 2 +- lv2/ui/ui.h | 90 +++++++++++++++++++++++++++++++++++++ lv2/ui/ui.ttl | 13 ++++++ plugins/eg-sampler.lv2/sampler.ttl | 1 + plugins/eg-sampler.lv2/sampler_ui.c | 34 +++++++++++--- wscript | 2 +- 6 files changed, 133 insertions(+), 9 deletions(-) diff --git a/lv2/core/meta.ttl b/lv2/core/meta.ttl index 22aa976..836e7db 100644 --- a/lv2/core/meta.ttl +++ b/lv2/core/meta.ttl @@ -36,7 +36,7 @@ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH R meta:kfoltman , meta:paniq ; doap:release [ - doap:revision "1.17.0" ; + doap:revision "1.17.2" ; doap:created "2019-03-27" ; dcs:blame ; dcs:changeset [ diff --git a/lv2/ui/ui.h b/lv2/ui/ui.h index 8e67a36..aba3d5a 100644 --- a/lv2/ui/ui.h +++ b/lv2/ui/ui.h @@ -30,6 +30,7 @@ #define LV2_UI_H #include "lv2/core/lv2.h" +#include "lv2/urid/urid.h" #include #include @@ -59,6 +60,7 @@ #define LV2_UI__portNotification LV2_UI_PREFIX "portNotification" ///< http://lv2plug.in/ns/extensions/ui#portNotification #define LV2_UI__portSubscribe LV2_UI_PREFIX "portSubscribe" ///< http://lv2plug.in/ns/extensions/ui#portSubscribe #define LV2_UI__protocol LV2_UI_PREFIX "protocol" ///< http://lv2plug.in/ns/extensions/ui#protocol +#define LV2_UI__requestValue LV2_UI_PREFIX "requestValue" ///< http://lv2plug.in/ns/extensions/ui#requestValue #define LV2_UI__floatProtocol LV2_UI_PREFIX "floatProtocol" ///< http://lv2plug.in/ns/extensions/ui#floatProtocol #define LV2_UI__peakProtocol LV2_UI_PREFIX "peakProtocol" ///< http://lv2plug.in/ns/extensions/ui#peakProtocol #define LV2_UI__resize LV2_UI_PREFIX "resize" ///< http://lv2plug.in/ns/extensions/ui#resize @@ -343,6 +345,94 @@ typedef struct { bool grabbed); } LV2UI_Touch; +/** + A status code for LV2UI_Request_Value::request(). +*/ +typedef enum { + /** + Completed successfully. + + The host will set the parameter later if the user choses a new value. + */ + LV2UI_REQUEST_VALUE_SUCCESS, + + /** + Parameter already being requested. + + The host is already requesting a parameter from the user (for example, a + dialog is visible), or the UI is otherwise busy and can not make this + request. + */ + LV2UI_REQUEST_VALUE_BUSY, + + /** + Unknown parameter. + + The host is not aware of this parameter, and is not able to set a new + value for it. + */ + LV2UI_REQUEST_VALUE_ERR_UNKNOWN, + + /** + Unsupported parameter. + + The host knows about this parameter, but does not support requesting a + new value for it from the user. This is likely because the host does + not have UI support for choosing a value with the appropriate type. + */ + LV2UI_REQUEST_VALUE_ERR_UNSUPPORTED +} LV2UI_Request_Value_Status; + +/** + A feature to request a new parameter value from the host. +*/ +typedef struct { + /** + Pointer to opaque data which must be passed to request(). + */ + LV2UI_Feature_Handle handle; + + /** + Request a value for a parameter from the host. + + This is mainly used by UIs to request values for complex parameters that + don't change often, such as file paths, but it may be used to request + any parameter value. + + This function returns immediately, and the return value indicates + whether the host can fulfill the request. The host may notify the + plugin about the new parameter value, for example when a file is + selected by the user, via the usual mechanism. Typically, the host will + send a message to the plugin that sets the new parameter value, and the + plugin will notify the UI via a message as usual for any other parameter + change. + + To provide an appropriate UI, the host can determine details about the + parameter from the plugin data as usual. The additional parameters of + this function provide support for more advanced use cases, but in the + simple common case, the plugin will simply pass the key of the desired + parameter and zero for everything else. + + @param handle The handle field of this struct. + + @param key The URID of the parameter. + + @param type The optional type of the value to request. This can be used + to request a specific value type for parameters that support several. + If non-zero, it must be the URID of an instance of rdfs:Class or + rdfs:Datatype. + + @param features Additional features for this request, or NULL. + + @return A status code which is 0 on success. + */ + LV2UI_Request_Value_Status (*request)(LV2UI_Feature_Handle handle, + LV2_URID key, + LV2_URID type, + const LV2_Feature* const* features); + +} LV2UI_Request_Value; + /** UI Idle Interface (LV2_UI__idleInterface) diff --git a/lv2/ui/ui.ttl b/lv2/ui/ui.ttl index ecebbf4..387e80e 100644 --- a/lv2/ui/ui.ttl +++ b/lv2/ui/ui.ttl @@ -309,6 +309,19 @@ plugin UI. This feature corresponds to the LV2UI_Touch struct, which should be passed with the URI LV2_UI__touch.

""" . +ui:requestValue + a lv2:Feature ; + lv2:documentation """ +

A feature to request a parameter value from the user via the host. This +allows a plugin UI to request a new parameter value using the host's UI, for +example by showing a dialog or integrating with the host's built-in content +browser. This should be used only for complex parameter types where the plugin +UI is not capable of showing the expected native platform or host interface to +choose a value, such as file path parameters. This feature corresponds to the +LV2UI_Request_Value struct, which should be passed with the URI +LV2_UI__requestValue.

+ """ . + ui:idleInterface a lv2:Feature , lv2:ExtensionData ; diff --git a/plugins/eg-sampler.lv2/sampler.ttl b/plugins/eg-sampler.lv2/sampler.ttl index 960276a..f5088f3 100644 --- a/plugins/eg-sampler.lv2/sampler.ttl +++ b/plugins/eg-sampler.lv2/sampler.ttl @@ -64,6 +64,7 @@ a ui:GtkUI ; lv2:requiredFeature urid:map ; + lv2:optionalFeature ui:requestValue ; lv2:extensionData ui:showInterface ; ui:portNotification [ ui:plugin ; diff --git a/plugins/eg-sampler.lv2/sampler_ui.c b/plugins/eg-sampler.lv2/sampler_ui.c index 89bca2f..1cb7553 100644 --- a/plugins/eg-sampler.lv2/sampler_ui.c +++ b/plugins/eg-sampler.lv2/sampler_ui.c @@ -47,11 +47,12 @@ #define MIN_CANVAS_H 80 typedef struct { - LV2_Atom_Forge forge; - LV2_URID_Map* map; - LV2_Log_Logger logger; - SamplerURIs uris; - PeaksReceiver precv; + LV2_Atom_Forge forge; + LV2_URID_Map* map; + LV2UI_Request_Value* request_value; + LV2_Log_Logger logger; + SamplerURIs uris; + PeaksReceiver precv; LV2UI_Write_Function write; LV2UI_Controller controller; @@ -59,6 +60,7 @@ typedef struct { GtkWidget* box; GtkWidget* play_button; GtkWidget* file_button; + GtkWidget* request_file_button; GtkWidget* button_box; GtkWidget* canvas; GtkWidget* window; /* For optional show interface. */ @@ -90,6 +92,17 @@ on_file_set(GtkFileChooserButton* widget, void* handle) g_free(filename); } +static void +on_request_file(GtkButton* widget, void* handle) +{ + SamplerUI* ui = (SamplerUI*)handle; + + ui->request_value->request(ui->request_value->handle, + ui->uris.eg_sample, + 0, + NULL); +} + static void on_play_clicked(GtkFileChooserButton* widget, void* handle) { @@ -220,8 +233,9 @@ instantiate(const LV2UI_Descriptor* descriptor, // Get host features const char* missing = lv2_features_query( features, - LV2_LOG__log, &ui->logger.log, false, - LV2_URID__map, &ui->map, true, + LV2_LOG__log, &ui->logger.log , false, + LV2_URID__map, &ui->map, true, + LV2_UI__requestValue, &ui->request_value, false, NULL); lv2_log_logger_set_map(&ui->logger, ui->map); if (missing) { @@ -242,16 +256,21 @@ instantiate(const LV2UI_Descriptor* descriptor, ui->button_box = gtk_hbox_new(FALSE, 4); ui->file_button = gtk_file_chooser_button_new( "Load Sample", GTK_FILE_CHOOSER_ACTION_OPEN); + ui->request_file_button = gtk_button_new_with_label("Request Sample"); gtk_widget_set_size_request(ui->canvas, MIN_CANVAS_W, MIN_CANVAS_H); gtk_container_set_border_width(GTK_CONTAINER(ui->box), 4); gtk_box_pack_start(GTK_BOX(ui->box), ui->canvas, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(ui->box), ui->button_box, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(ui->button_box), ui->play_button, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(ui->button_box), ui->request_file_button, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(ui->button_box), ui->file_button, TRUE, TRUE, 0); g_signal_connect(ui->file_button, "file-set", G_CALLBACK(on_file_set), ui); + g_signal_connect(ui->request_file_button, "clicked", + G_CALLBACK(on_request_file), ui); + g_signal_connect(ui->play_button, "clicked", G_CALLBACK(on_play_clicked), ui); @@ -283,6 +302,7 @@ cleanup(LV2UI_Handle handle) gtk_widget_destroy(ui->canvas); gtk_widget_destroy(ui->button_box); gtk_widget_destroy(ui->file_button); + gtk_widget_destroy(ui->request_file_button); free(ui); } diff --git a/wscript b/wscript index 9dcfbe1..b30239a 100644 --- a/wscript +++ b/wscript @@ -9,7 +9,7 @@ from waflib.extras import autowaf as autowaf # Mandatory waf variables APPNAME = 'lv2' # Package name for waf dist -VERSION = '1.17.0' # Package version for waf dist +VERSION = '1.17.2' # Package version for waf dist top = '.' # Source directory out = 'build' # Build directory -- cgit v1.2.1