diff options
-rw-r--r-- | src/instance.c | 147 | ||||
-rw-r--r-- | src/suil_internal.h | 38 | ||||
-rw-r--r-- | src/uis.c | 45 | ||||
-rw-r--r-- | suil/suil.h | 86 | ||||
-rw-r--r-- | wscript | 9 |
5 files changed, 315 insertions, 10 deletions
diff --git a/src/instance.c b/src/instance.c new file mode 100644 index 0000000..82ace11 --- /dev/null +++ b/src/instance.c @@ -0,0 +1,147 @@ +/* Suil, a lightweight RDF syntax library. + * Copyright 2011 David Robillard <d@drobilla.net> + * + * Suil is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Suil 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 GNU Lesser General Public + * License for details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define _XOPEN_SOURCE 500 + +#include <assert.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <dlfcn.h> + +#include "suil_internal.h" + +SUIL_API +bool +suil_ui_type_supported(const char* uri) +{ + return !strcmp(uri, "http://lv2plug.in/ns/extensions/ui#GtkUI"); +} + +SUIL_API +SuilInstance +suil_instance_new(SuilUIs uis, + const char* type_uri, + const char* ui_uri, + LV2UI_Write_Function write_function, + LV2UI_Controller controller, + const LV2_Feature* const* features) +{ + struct _SuilInstance* instance = NULL; + + const bool local_features = (features == NULL); + if (local_features) { + features = malloc(sizeof(LV2_Feature)); + ((LV2_Feature**)features)[0] = NULL; + } + + SuilUI ui = uis->uis[0]; // FIXME + + dlerror(); + void* lib = dlopen(ui->binary_path, RTLD_NOW); + if (!lib) { + SUIL_ERRORF("Unable to open UI library %s (%s)\n", + ui->binary_path, dlerror()); + return NULL; + } + + LV2UI_DescriptorFunction df = (LV2UI_DescriptorFunction) + suil_dlfunc(lib, "lv2ui_descriptor"); + + if (!df) { + SUIL_ERRORF("Broken LV2 UI %s (no lv2ui_descriptor symbol found)\n", + ui->binary_path); + dlclose(lib); + return NULL; + } else { + for (uint32_t i = 0; true; ++i) { + const LV2UI_Descriptor* ld = df(i); + if (!ld) { + SUIL_ERRORF("No UI %s in %s\n", ui->uri, ui->binary_path); + dlclose(lib); + break; // return NULL + } else if ((ui_uri && !strcmp(ld->URI, ui_uri)) + || !strcmp(ui->type_uri, type_uri)) { + instance = malloc(sizeof(struct _SuilInstance)); + instance->descriptor = ld; + instance->handle = ld->instantiate( + ld, + uis->plugin_uri, + ui->bundle_path, + write_function, + controller, + &instance->widget, + features); + instance->lib_handle = lib; + break; + } + } + } + + if (local_features) { + free((LV2_Feature**)features); + } + + // Failed to find or instantiate UI + if (!instance || !instance->handle) { + free(instance); + return NULL; + } + + // Failed to create a widget, but still got a handle (buggy UI) + if (!instance->widget) { + suil_instance_free(instance); + return NULL; + } + + return instance; +} + +SUIL_API +void +suil_instance_free(SuilInstance instance) +{ + if (instance) { + instance->descriptor->cleanup(instance->handle); + dlclose(instance->lib_handle); + free(instance); + } +} + +SUIL_API +LV2UI_Widget +suil_instance_get_widget(SuilInstance instance) +{ + return instance->widget; +} + +SUIL_API +void +suil_instance_port_event(SuilInstance instance, + uint32_t port_index, + uint32_t buffer_size, + uint32_t format, + const void* buffer) +{ + instance->descriptor->port_event(instance->handle, + port_index, + buffer_size, + format, + buffer); +} diff --git a/src/suil_internal.h b/src/suil_internal.h index a755187..4940b69 100644 --- a/src/suil_internal.h +++ b/src/suil_internal.h @@ -21,6 +21,44 @@ #include <assert.h> #include <stdlib.h> +#include <dlfcn.h> + #include "suil/suil.h" +#define SUIL_ERRORF(fmt, ...) fprintf(stderr, "error: %s: " fmt, \ + __func__, __VA_ARGS__) + +struct _SuilUI { + char* uri; + char* type_uri; + char* bundle_path; + char* binary_path; +}; + +typedef struct _SuilUI* SuilUI; + +struct _SuilUIs { + char* plugin_uri; + SuilUI* uis; + size_t n_uis; +}; + +struct _SuilInstance { + void* lib_handle; + const LV2UI_Descriptor* descriptor; + LV2UI_Handle handle; + LV2UI_Widget widget; +}; + +typedef void (*SuilVoidFunc)(); + +/** dlsym wrapper to return a function pointer (without annoying warning) */ +static inline SuilVoidFunc +suil_dlfunc(void* handle, const char* symbol) +{ + typedef SuilVoidFunc (*VoidFuncGetter)(void*, const char*); + VoidFuncGetter dlfunc = (VoidFuncGetter)dlsym; + return dlfunc(handle, symbol); +} + #endif // SUIL_INTERNAL_H @@ -15,4 +15,49 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#define _XOPEN_SOURCE 500 + +#include <string.h> + #include "suil_internal.h" + +SUIL_API +SuilUIs +suil_uis_new(const char* plugin_uri) +{ + SuilUIs uis = (SuilUIs)malloc(sizeof(struct _SuilUIs)); + uis->plugin_uri = strdup(plugin_uri); + uis->uis = malloc(sizeof(SuilUI)); + uis->uis[0] = (SuilUI)NULL; + uis->n_uis = 0; + return uis; +} + +SUIL_API +void +suil_uis_free(SuilUIs uis) +{ + free(uis->plugin_uri); + free(uis); +} + +SUIL_API +void +suil_uis_add(SuilUIs uis, + const char* uri, + const char* type_uri, + const char* bundle_path, + const char* binary_path) +{ + SuilUI ui = (SuilUI)malloc(sizeof(struct _SuilUI)); + ui->uri = strdup(uri); + ui->type_uri = strdup(type_uri); + ui->bundle_path = strdup(bundle_path); + ui->binary_path = strdup(binary_path); + + ++uis->n_uis; + uis->uis = realloc(uis->uis, sizeof(SuilUI) * (uis->n_uis + 1)); + assert(uis->uis[uis->n_uis - 1] == NULL); + uis->uis[uis->n_uis - 1] = ui; + uis->uis[uis->n_uis] = NULL; +} diff --git a/suil/suil.h b/suil/suil.h index ffe0069..7e0dbec 100644 --- a/suil/suil.h +++ b/suil/suil.h @@ -19,13 +19,13 @@ * Public Suil API. */ -#ifndef SUIL_SUIL_H -#define SUIL_SUIL_H +#ifndef SUIL_SUIL_H__ +#define SUIL_SUIL_H__ #include <stdbool.h> -#include <stddef.h> #include <stdint.h> -#include <stdio.h> + +#include "lv2/lv2plug.in/ns/extensions/ui/ui.h" #ifdef SUIL_SHARED #if defined _WIN32 || defined __CYGWIN__ @@ -53,6 +53,82 @@ extern "C" { * @{ */ +/** A set of UIs for a particular LV2 plugin. */ +typedef struct _SuilUIs* SuilUIs; + +/** An instance of an LV2 plugin UI. */ +typedef struct _SuilInstance* SuilInstance; + +/** Return true iff UIs of the given type are supported. */ +SUIL_API +bool +suil_ui_type_supported(const char* uri); + +/** Create a new empty set of UIs for a particular LV2 plugin. */ +SUIL_API +SuilUIs +suil_uis_new(const char* plugin_uri); + +/** Free @a uis. */ +SUIL_API +void +suil_uis_free(SuilUIs uis); + +/** Add a discovered UI to @a uis. */ +SUIL_API +void +suil_uis_add(SuilUIs uis, + const char* uri, + const char* type_uri, + const char* bundle_path, + const char* binary_path); + +/** Instantiate a UI for an LV2 plugin. + * @param uis Set of available UIs for the plugin. + * @param type_uri URI of the desired widget type. + * @param ui_uri URI of a specifically desired UI, or NULL to use the + * best choice given @a type_uri. + * @param write_function Write function as defined by the LV2 UI extension. + * @param controller Opaque controller to be passed to @a write_function. + * @param features NULL-terminated array of supported features, or NULL. + * @return A new UI instance, or NULL if instantiation failed. + */ +SUIL_API +SuilInstance +suil_instance_new(SuilUIs uis, + const char* type_uri, + const char* ui_uri, + LV2UI_Write_Function write_function, + LV2UI_Controller controller, + const LV2_Feature* const* features); + +/** Free a plugin UI instance. + * The caller must ensure all references to the UI have been dropped before + * calling this function (e.g. it has been removed from its parent). + */ +SUIL_API +void +suil_instance_free(SuilInstance instance); + +/** Get the widget for a UI instance. + * Returns an opaque pointer to a widget, the type of which is defined by the + * corresponding parameter to suil_instantiate. Note this may be a wrapper + * widget created by Suil, and not necessarily an LV2UI_Widget implemented + * in an LV2 bundle. + */ +SUIL_API +LV2UI_Widget +suil_instance_get_widget(SuilInstance instance); + +/** Notify the UI about a change in a plugin port. + */ +SUIL_API +void +suil_instance_port_event(SuilInstance instance, + uint32_t port_index, + uint32_t buffer_size, + uint32_t format, + const void* buffer); /** @} */ @@ -60,4 +136,4 @@ extern "C" { } /* extern "C" */ #endif -#endif /* SUIL_SUIL_H */ +#endif /* SUIL_SUIL_H__ */ @@ -30,13 +30,15 @@ def options(opt): autowaf.set_options(opt) def configure(conf): - conf.line_just = max(conf.line_just, 40) + conf.line_just = max(conf.line_just, 56) autowaf.configure(conf) autowaf.display_header('Suil Configuration') conf.check_tool('compiler_cc') conf.env.append_value('CFLAGS', '-std=c99') + autowaf.check_header(conf, 'lv2/lv2plug.in/ns/extensions/ui/ui.h') + autowaf.define(conf, 'SUIL_VERSION', SUIL_VERSION) conf.write_config_header('suil-config.h', remove=False) @@ -52,7 +54,7 @@ def build(bld): # Library obj = bld(features = 'c cshlib') obj.export_includes = ['.'] - obj.source = 'src/uis.c' + obj.source = 'src/instance.c src/uis.c' obj.includes = ['.', './src'] obj.name = 'libsuil' obj.target = 'suil' @@ -63,9 +65,6 @@ def build(bld): # Documentation autowaf.build_dox(bld, 'SUIL', SUIL_VERSION, top, out) - # Man page - bld.install_files('${MANDIR}/man1', 'doc/suili.1') - bld.add_post_fun(autowaf.run_ldconfig) def lint(ctx): |