summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/c/overview.rst189
-rw-r--r--include/suil/suil.h50
-rw-r--r--meson.build43
3 files changed, 253 insertions, 29 deletions
diff --git a/doc/c/overview.rst b/doc/c/overview.rst
index e8d61b1..e6d4e63 100644
--- a/doc/c/overview.rst
+++ b/doc/c/overview.rst
@@ -1,6 +1,6 @@
-########
-Overview
-########
+##########
+Using Suil
+##########
.. default-domain:: c
.. highlight:: c
@@ -10,3 +10,186 @@ The complete API is declared in ``suil.h``:
.. code-block:: c
#include <suil/suil.h>
+
+*************
+Library Setup
+*************
+
+In order to work properly on certain platforms,
+suil must be set up by the application before any GUI toolkits are loaded.
+This is done with :func:`suil_init`,
+which takes command line arguments and an optional list of arguments,
+terminated by :enumerator:`SUIL_ARG_NONE`:
+
+.. code-block:: c
+
+ int main(int argc, char** argv)
+ {
+ suil_init(&argc, &argv, SUIL_ARG_NONE);
+ return 0;
+ }
+
+There are currently no defined Suil arguments,
+the facility exists for future extensibility.
+
+**********
+Host Setup
+**********
+
+After the library is prepared,
+a :struct:`SuilHost` must be created.
+This represents the plugin UI host,
+in particular the callbacks that will fire when there is UI activity.
+There are five types of host callback:
+
+- :type:`SuilPortWriteFunc`
+- :type:`SuilPortIndexFunc`
+- :type:`SuilPortSubscribeFunc`
+- :type:`SuilPortUnsubscribeFunc`
+- :type:`SuilTouchFunc`
+
+These callbacks all take a :type:`SuilController`,
+which is an opaque pointer that can be used to describe the context
+(including which UI is responsible for the call),
+as well as extra arguments specific to the callback.
+
+Assuming these are implemented in the host as ``my_write_func``, ``my_port_index_func``, ``my_port_subscribe_func``, ``my_port_unsubscribe_func``, and ``my_touch_func``, respectively,
+the host can be allocated with :func:`suil_host_new`:
+
+.. code-block:: c
+
+ SuilHost* host = suil_host_new(my_write_func,
+ my_index_func,
+ my_subscribe_func,
+ my_unsubscribe_func);
+
+Touch functionality was added to the API later,
+so the touch function is set with the separate :func:`suil_host_set_touch_func`:
+
+.. code-block:: c
+
+ suil_host_set_touch_func(host, my_touch_func);
+
+*********************
+Finding Supported UIs
+*********************
+
+Suil does not access data directly itself,
+so UI discovery is the responsibility of the application.
+Most hosts will use :func:`lilv_plugin_get_uis` to discover the available UIs for a particular plugin.
+
+Suil provides :func:`suil_ui_supported`,
+which can be used to determine if a particular UI can be embedded in the host.
+It requires two URIs,
+one that describes the host UI type,
+and one that describes the plugin UI type.
+They are typically URIs defined in the LV2 UI extension,
+such as https://lv2plug.in/ns/extensions/ui#X11UI,
+which are defined for convenience with names like :var:`LV2_UI__X11UI`.
+
+If a specific UI type is already known,
+this can be used directly,
+for example an X11 host might do something like this:
+
+.. code-block:: c
+
+ if (suil_ui_supported(LV2_UI__X11UI, plugin_ui_type) > 0) {
+ // Use this UI...
+ }
+
+More commonly,
+this function is used as a utility along with :func:`lilv_ui_is_supported`:
+
+.. code-block:: c
+
+ LilvUI* ui = this_plugin_ui();
+ LilvNode* host_type = lilv_new_uri(LV2_UI__X11UI);
+ LilvNode* ui_type = NULL;
+
+ if (lilv_ui_is_supported(ui, suil_ui_supported, host_type, &ui_type)) {
+ // Use this UI, which has type ui_type...
+ }
+
+*********************
+Loading a UI Instance
+*********************
+
+An instance of a plugin UI is represented by a :struct:`SuilInstance`,
+which can be created with :func:`suil_instance_new`.
+For example,
+given a :struct:`LilvPlugin` ``plugin``,
+and a :struct:`LilvUI` ``ui``:
+
+.. code-block:: c
+
+ SuilController* controller = my_controller_for(ui);
+ const LV2_Feature** features = my_features_for(ui);
+
+ const char* bundle_uri = lilv_node_as_uri(lilv_ui_get_bundle_uri(ui));
+ const char* binary_uri = lilv_node_as_uri(lilv_ui_get_binary_uri(ui));
+ char* bundle_path = serd_parse_file_uri(bundle_uri, NULL);
+ char* binary_path = serd_parse_file_uri(binary_uri, NULL);
+
+ SuilInstance* instance = suil_instance_new(
+ host,
+ controller,
+ LV2_UI__X11UI,
+ lilv_node_as_uri(lilv_plugin_get_uri(plugin)),
+ lilv_node_as_uri(lilv_ui_get_uri(ui)),
+ lilv_node_as_uri(ui_type),
+ bundle_path,
+ binary_path,
+ ui_features);
+
+*********************
+Retrieving the Widget
+*********************
+
+With the UI instantiated,
+the actual widget can be retrieved with :func:`suil_instance_get_widget`.
+This returns a :type:`SuilWidget` handle which has the container type passed to :func:`suil_instance_new`.
+In this example,
+the host is a LV2_UI__X11UI,
+so the returned widget is a ``Window``:
+
+.. code-block:: c
+
+ Window window = (Window)suil_instance_get_widget(instance);
+
+The host may be responsible for displaying or embedding the widget,
+depending on the windowing system or toolkit used.
+
+***************
+Updating the UI
+***************
+
+Updates can be sent to a UI instance with :func:`suil_instance_port_event`.
+This takes a port index, a value size and format, and a pointer to a value.
+For example, to update a control port, the special format ``0`` can be used:
+
+.. code-block:: c
+
+ const float value = 42.0f;
+
+ suil_instance_port_event(instance, 2, sizeof(float), 0, &value);
+
+*******
+Cleanup
+*******
+
+When the host is finished with the UI,
+and has removed any references to it in the windowing system or toolkit,
+it must destroy the UI with :func:`suil_instance_free`:
+
+.. code-block:: c
+
+ suil_instance_free(instance);
+
+When all UIs have been destroyed and the host is ready to shut down,
+it must free its description with :func:`suil_host_free`:
+
+.. code-block:: c
+
+ suil_host_free(host);
+
+Note that the host must outlive any UI instances that were created for it.
diff --git a/include/suil/suil.h b/include/suil/suil.h
index ec2ea5e..779a169 100644
--- a/include/suil/suil.h
+++ b/include/suil/suil.h
@@ -1,5 +1,5 @@
/*
- Copyright 2011-2017 David Robillard <d@drobilla.net>
+ Copyright 2011-2021 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -47,27 +47,14 @@ extern "C" {
#endif
/**
- @defgroup suil Suil
+ @defgroup suil Suil C API
@{
*/
/**
- UI host descriptor.
-
- This contains the various functions that a plugin UI may use to communicate
- with the plugin. It is passed to suil_instance_new() to provide these
- functions to the UI.
+ @defgroup suil_host UI Host
+ @{
*/
-typedef struct SuilHostImpl SuilHost;
-
-/// An instance of an LV2 plugin UI
-typedef struct SuilInstanceImpl SuilInstance;
-
-/// Opaque pointer to a UI handle
-typedef void* SuilHandle;
-
-/// Opaque pointer to a UI widget
-typedef void* SuilWidget;
/**
UI controller.
@@ -112,6 +99,15 @@ typedef void (*SuilTouchFunc)( //
uint32_t port_index,
bool grabbed);
+/**
+ UI host descriptor.
+
+ This contains the various functions that a plugin UI may use to communicate
+ with the plugin. It is passed to suil_instance_new() to provide these
+ functions to the UI.
+*/
+typedef struct SuilHostImpl SuilHost;
+
/// Initialization argument
typedef enum { SUIL_ARG_NONE } SuilArg;
@@ -159,6 +155,21 @@ void
suil_host_free(SuilHost* host);
/**
+ @}
+ @defgroup suil_ui UI Instances
+ @{
+*/
+
+/// An instance of an LV2 plugin UI
+typedef struct SuilInstanceImpl SuilInstance;
+
+/// Opaque pointer to a UI handle
+typedef void* SuilHandle;
+
+/// Opaque pointer to a UI widget
+typedef void* SuilWidget;
+
+/**
Check if suil can wrap a UI type.
@param host_type_uri The URI of the desired widget type of the host,
@@ -224,8 +235,8 @@ suil_instance_free(SuilInstance* instance);
Get the handle for a UI instance.
Returns the handle to the UI instance. The returned handle has opaque type
- to insulate the Suil API from LV2 extensions, but in pactice it is currently
- of type `LV2UI_Handle`. This should not normally be needed.
+ to insulate the Suil API from LV2 extensions, but in practice it is
+ currently of type `LV2UI_Handle`. This should not normally be needed.
The returned handle is shared and must not be deleted.
*/
@@ -277,6 +288,7 @@ suil_instance_extension_data(SuilInstance* instance, const char* uri);
/**
@}
+ @}
*/
#ifdef __cplusplus
diff --git a/meson.build b/meson.build
index 7a413ff..02e83b8 100644
--- a/meson.build
+++ b/meson.build
@@ -43,6 +43,7 @@ if get_option('strict')
elif cc.get_id() == 'gcc'
c_warnings += [
'-Wno-padded',
+ '-Wno-pragmas',
'-Wno-suggest-attribute=const',
'-Wno-suggest-attribute=pure',
'-Wno-extra-semi', # Qt headers
@@ -56,9 +57,6 @@ if get_option('strict')
]
endif
- add_project_arguments(cc.get_supported_arguments(c_warnings),
- language: ['c'])
-
cpp_warnings = all_cpp_warnings
if cpp.get_id() == 'clang'
cpp_warnings += [
@@ -75,6 +73,7 @@ if get_option('strict')
cpp_warnings += [
'-Wno-cast-qual',
'-Wno-padded',
+ '-Wno-pragmas',
'-Wno-suggest-attribute=const',
'-Wno-suggest-attribute=pure',
'-Wno-useless-cast',
@@ -82,10 +81,36 @@ if get_option('strict')
]
endif
- add_project_arguments(cpp.get_supported_arguments(cpp_warnings),
- language: ['cpp'])
+else
+ c_warnings = []
+ if cc.get_id() == 'clang'
+ c_warnings += [
+ '-Wno-pragmas',
+ ]
+ elif cc.get_id() == 'gcc'
+ c_warnings += [
+ '-Wno-pragmas',
+ ]
+ endif
+
+ cpp_warnings = []
+ if cpp.get_id() == 'clang'
+ cpp_warnings += [
+ '-Wno-pragmas',
+ ]
+ elif cpp.get_id() == 'gcc'
+ cpp_warnings += [
+ '-Wno-pragmas',
+ ]
+ endif
endif
+add_project_arguments(cc.get_supported_arguments(c_warnings),
+ language: ['c'])
+
+add_project_arguments(cpp.get_supported_arguments(cpp_warnings),
+ language: ['cpp'])
+
# Add special arguments for MSVC
if cc.get_id() == 'msvc'
msvc_args = [
@@ -112,6 +137,8 @@ dl_dep = cc.find_library('dl', required: false)
# Dependencies
+gtk2_quartz_dep = disabler()
+
lv2_dep = dependency('lv2',
version: '>= 1.18.3',
fallback: ['lv2', 'lv2_dep'])
@@ -119,7 +146,9 @@ lv2_dep = dependency('lv2',
x11_dep = dependency('x11', required: false)
gtk2_dep = dependency('gtk+-2.0', version: '>=2.18.0', required: false)#, include_type: 'system')
gtk2_x11_dep = dependency('gtk+-x11-2.0', required: false)#, include_type: 'system')
-gtk2_quartz_dep = dependency('gtk+-quartz-2.0', required: false)#, include_type: 'system')
+if host_machine.system() == 'darwin'
+ gtk2_quartz_dep = dependency('gtk+-quartz-2.0', required: false)#, include_type: 'system')
+endif
gtk3_dep = dependency('gtk+-3.0', version: '>=3.14.0', required: false)#, include_type: 'system')
gtk3_x11_dep = dependency('gtk+-x11-3.0', version: '>=3.14.0', required: false)#, include_type: 'system')
qt5_dep = dependency('Qt5Widgets', version: '>=5.1.0', required: false)#, include_type: 'system')
@@ -252,7 +281,7 @@ if qt5_dep.found() and qt5_x11_dep.found()
endif
-if meson.version().version_compare('>=0.53.0')
+if not meson.is_subproject() and meson.version().version_compare('>=0.53.0')
# summary('Tests', get_option('tests'), bool_yn: true)
summary('Install prefix', get_option('prefix'))