1 /*
2 Copyright 2011-2017 David Robillard <http://drobilla.net>
3
4 Permission to use, copy, modify, and/or distribute this software for any
5 purpose with or without fee is hereby granted, provided that the above
6 copyright notice and this permission notice appear in all copies.
7
8 THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 /**
18 @file suil.h API for Suil, an LV2 UI wrapper library.
19 */
20
21 #include "zrythm-config.h"
22
23 #ifndef HAVE_SUIL
24
25 #ifndef SUIL_SUIL_H
26 #define SUIL_SUIL_H
27
28 #include <assert.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdbool.h>
33 #include <stdint.h>
34
35 #include "lv2/core/lv2.h"
36 #include "lv2/ui/ui.h"
37
38 #ifdef _WOE32
39 #include <windows.h>
40 #endif
41 #include <dlfcn.h>
42
43 #include <gtk/gtk.h>
44
45 #define GTK2_UI_URI LV2_UI__GtkUI
46 #define GTK3_UI_URI LV2_UI__Gtk3UI
47 #define QT4_UI_URI LV2_UI__Qt4UI
48 #define QT5_UI_URI LV2_UI__Qt5UI
49 #define X11_UI_URI LV2_UI__X11UI
50 #define WIN_UI_URI LV2_UI_PREFIX "WindowsUI"
51 #define COCOA_UI_URI LV2_UI__CocoaUI
52
53 #ifdef __cplusplus
54 extern "C" {
55 #endif
56
57 /**
58 @defgroup suil Suil
59
60 Suil is a library for loading and wrapping LV2 plugin UIs. With Suil, a
61 host written in one supported toolkit can embed a plugin UI written in a
62 different supported toolkit. Suil insulates hosts from toolkit libraries
63 used by plugin UIs. For example, a Gtk host can embed a Qt UI without
64 linking against Qt at compile time.
65
66 Visit <http://drobilla.net/software/suil> for more information.
67
68 @{
69 */
70
71 /**
72 UI host descriptor.
73
74 This contains the various functions that a plugin UI may use to communicate
75 with the plugin. It is passed to suil_instance_new() to provide these
76 functions to the UI.
77 */
78 typedef struct SuilHostImpl SuilHost;
79
80 /** An instance of an LV2 plugin UI. */
81 typedef struct SuilInstanceImpl SuilInstance;
82
83 /** Opaque pointer to a UI handle. */
84 typedef void* SuilHandle;
85
86 /** Opaque pointer to a UI widget. */
87 typedef void* SuilWidget;
88
89 /**
90 UI controller.
91
92 This is an opaque pointer passed by the user which is passed to the various
93 UI control functions (e.g. SuilPortWriteFunc). It is typically used to pass
94 a pointer to some controller object the host uses to communicate with
95 plugins.
96 */
97 typedef void* SuilController;
98
99 /** Function to write/send a value to a port. */
100 typedef void (*SuilPortWriteFunc)(
101 SuilController controller,
102 uint32_t port_index,
103 uint32_t buffer_size,
104 uint32_t protocol,
105 void const* buffer);
106
107 /** Function to return the index for a port by symbol. */
108 typedef uint32_t (*SuilPortIndexFunc)(
109 SuilController controller,
110 const char* port_symbol);
111
112 /** Function to subscribe to notifications for a port. */
113 typedef uint32_t (*SuilPortSubscribeFunc)(
114 SuilController controller,
115 uint32_t port_index,
116 uint32_t protocol,
117 const LV2_Feature* const* features);
118
119 /** Function to unsubscribe from notifications for a port. */
120 typedef uint32_t (*SuilPortUnsubscribeFunc)(
121 SuilController controller,
122 uint32_t port_index,
123 uint32_t protocol,
124 const LV2_Feature* const* features);
125
126 /** Function called when a control is grabbed or released. */
127 typedef void (*SuilTouchFunc)(
128 SuilController controller,
129 uint32_t port_index,
130 bool grabbed);
131
132 /** Initialization argument. */
133 typedef enum {
134 SUIL_ARG_NONE
135 } SuilArg;
136
137 /**
138 Initialize suil.
139
140 This function should be called as early as possible, before any other GUI
141 toolkit functions. The variable argument list is a sequence of SuilArg keys
142 and corresponding value pairs for passing any necessary platform-specific
143 information. It must be terminated with SUIL_ARG_NONE.
144 */
145 void
146 suil_init(int* argc, char*** argv, SuilArg key, ...);
147
148 /**
149 Create a new UI host descriptor.
150 @param write_func Function to send a value to a plugin port.
151 @param index_func Function to get the index for a port by symbol.
152 @param subscribe_func Function to subscribe to port updates.
153 @param unsubscribe_func Function to unsubscribe from port updates.
154 */
155 SuilHost*
156 suil_host_new(SuilPortWriteFunc write_func,
157 SuilPortIndexFunc index_func,
158 SuilPortSubscribeFunc subscribe_func,
159 SuilPortUnsubscribeFunc unsubscribe_func);
160
161 /**
162 Set a touch function for a host descriptor.
163
164 Note this function will only be called if the UI supports it.
165 */
166 void
167 suil_host_set_touch_func(SuilHost* host,
168 SuilTouchFunc touch_func);
169
170 /**
171 Free `host`.
172 */
173 void
174 suil_host_free(SuilHost* host);
175
176 /**
177 Check if suil can wrap a UI type.
178 @param host_type_uri The URI of the desired widget type of the host,
179 corresponding to the `type_uri` parameter of suil_instance_new().
180 @param ui_type_uri The URI of the UI widget type.
181 @return 0 if wrapping is unsupported, otherwise the quality of the wrapping
182 where 1 is the highest quality (direct native embedding with no wrapping)
183 and increasing values are of a progressively lower quality and/or stability.
184 */
185 unsigned
186 suil_ui_supported(const char* host_type_uri,
187 const char* ui_type_uri);
188
189 /**
190 Instantiate a UI for an LV2 plugin.
191
192 This funcion may load a suil module to adapt the UI to the desired toolkit.
193 Suil is configured at compile time to load modules from the appropriate
194 place, but this can be changed at run-time via the environment variable
195 SUIL_MODULE_DIR. This makes it possible to bundle suil with an application.
196
197 Note that some situations (Gtk in Qt, Windows in Gtk) require a parent
198 container to be passed as a feature with URI LV2_UI__parent
199 (http://lv2plug.in/ns/extensions/ui#ui) in order to work correctly. The
200 data must point to a single child container of the host widget set.
201
202 @param host Host descriptor.
203 @param controller Opaque host controller pointer.
204 @param container_type_uri URI of the desired host container widget type.
205 @param plugin_uri URI of the plugin to instantiate this UI for.
206 @param ui_uri URI of the specifically desired UI.
207 @param ui_type_uri URI of the actual UI widget type.
208 @param ui_bundle_path Path of the UI bundle.
209 @param ui_binary_path Path of the UI binary.
210 @param features NULL-terminated array of supported features, or NULL.
211 @return A new UI instance, or NULL if instantiation failed.
212 */
213 SuilInstance*
214 suil_instance_new(SuilHost* host,
215 SuilController controller,
216 const char* container_type_uri,
217 const char* plugin_uri,
218 const char* ui_uri,
219 const char* ui_type_uri,
220 const char* ui_bundle_path,
221 const char* ui_binary_path,
222 const LV2_Feature* const* features);
223
224 /**
225 Free a plugin UI instance.
226
227 The caller must ensure all references to the UI have been dropped before
228 calling this function (e.g. it has been removed from its parent).
229 */
230 void
231 suil_instance_free(SuilInstance* instance);
232
233 /**
234 Get the handle for a UI instance.
235
236 Returns the handle to the UI instance. The returned handle has opaque type
237 to insulate the Suil API from LV2 extensions, but in pactice it is currently
238 of type `LV2UI_Handle`. This should not normally be needed.
239
240 The returned handle is shared and must not be deleted.
241 */
242 SuilHandle
243 suil_instance_get_handle(SuilInstance* instance);
244
245 /**
246 Get the widget for a UI instance.
247
248 Returns an opaque pointer to a widget, the type of which matches the
249 `container_type_uri` parameter of suil_instance_new(). Note this may be a
250 wrapper widget created by Suil, and not necessarily the widget directly
251 implemented by the UI.
252 */
253 SuilWidget
254 suil_instance_get_widget(SuilInstance* instance);
255
256 /**
257 Notify the UI about a change in a plugin port.
258 @param instance UI instance.
259 @param port_index Index of the port which has changed.
260 @param buffer_size Size of `buffer` in bytes.
261 @param format Format of `buffer` (mapped URI, or 0 for float).
262 @param buffer Change data, e.g. the new port value.
263
264 This function can be used to notify the UI about any port change, but in the
265 simplest case is used to set the value of lv2:ControlPort ports. For
266 simplicity, this is a special case where `format` is 0, `buffer_size` is 4,
267 and `buffer` should point to a single float.
268
269 The `buffer` must be valid only for the duration of this call, the UI must
270 not keep a reference to it.
271 */
272 void
273 suil_instance_port_event(SuilInstance* instance,
274 uint32_t port_index,
275 uint32_t buffer_size,
276 uint32_t format,
277 const void* buffer);
278
279 /**
280 Return a data structure defined by some LV2 extension URI.
281 */
282 const void*
283 suil_instance_extension_data(SuilInstance* instance,
284 const char* uri);
285
286 /**
287 @}
288 */
289 #ifdef __cplusplus
290 } /* extern "C" */
291 #endif
292
293 #include "lv2/ui/ui.h"
294
295 #define SUIL_ERRORF(fmt, ...) g_warning("suil error: " fmt, __VA_ARGS__)
296
297 struct SuilHostImpl {
298 SuilPortWriteFunc write_func;
299 SuilPortIndexFunc index_func;
300 SuilPortSubscribeFunc subscribe_func;
301 SuilPortUnsubscribeFunc unsubscribe_func;
302 SuilTouchFunc touch_func;
303 void* gtk_lib;
304 int argc;
305 char** argv;
306 };
307
308 struct _SuilWrapper;
309
310 typedef void (*SuilWrapperFreeFunc)(struct _SuilWrapper*);
311
312 typedef int (*SuilWrapperWrapFunc)(struct _SuilWrapper* wrapper,
313 SuilInstance* instance);
314
315 typedef struct _SuilWrapper {
316 SuilWrapperWrapFunc wrap;
317 SuilWrapperFreeFunc free;
318 void* impl;
319 LV2UI_Resize resize;
320 } SuilWrapper;
321
322 struct SuilInstanceImpl {
323 void* lib_handle;
324 const LV2UI_Descriptor* descriptor;
325 LV2UI_Handle handle;
326 SuilWrapper* wrapper;
327 LV2_Feature** features;
328 LV2UI_Port_Map port_map;
329 LV2UI_Port_Subscribe port_subscribe;
330 LV2UI_Touch touch;
331 SuilWidget ui_widget;
332 SuilWidget host_widget;
333 };
334
335 /**
336 The type of the suil_wrapper_new entry point in a wrapper module.
337
338 This constructs a SuilWrapper which contains everything necessary
339 to wrap a widget, including a possibly extended features array to
340 be used for instantiating the UI.
341 */
342 typedef SuilWrapper* (*SuilWrapperNewFunc)(SuilHost* host,
343 const char* host_type_uri,
344 const char* ui_type_uri,
345 LV2_Feature*** features,
346 unsigned n_features);
347
348 /** Prototype for suil_wrapper_new in each wrapper module. */
349 SuilWrapper*
350 suil_wrapper_new_x11 (SuilHost* host,
351 const char* host_type_uri,
352 const char* ui_type_uri,
353 LV2_Feature*** features,
354 unsigned n_features);
355
356 #ifdef __cplusplus
357 extern "C" {
358 #endif
359 /** Prototype for suil_wrapper_new in each wrapper module. */
360 SuilWrapper*
361 suil_wrapper_new_qt5 (SuilHost* host,
362 const char* host_type_uri,
363 const char* ui_type_uri,
364 LV2_Feature*** features,
365 unsigned n_features);
366
367 /** Prototype for suil_wrapper_new in each wrapper module. */
368 SuilWrapper*
369 suil_wrapper_new_woe (
370 SuilHost* host,
371 const char* host_type_uri,
372 const char* ui_type_uri,
373 LV2_Feature*** features,
374 unsigned n_features);
375
376 /** Prototype for suil_wrapper_new in each wrapper module. */
377 SuilWrapper*
378 suil_wrapper_new_cocoa (
379 SuilHost* host,
380 const char* host_type_uri,
381 const char* ui_type_uri,
382 LV2_Feature*** features,
383 unsigned n_features);
384 #ifdef __cplusplus
385 } // end extern "C"
386 #endif
387
388 /** Prototype for suil_host_init in each init module. */
389 void
390 suil_host_init(void);
391
392
393 typedef void (*SuilVoidFunc)(void);
394
395 /** dlsym wrapper to return a function pointer (without annoying warning) */
396 static inline SuilVoidFunc
suil_dlfunc(void * handle,const char * symbol)397 suil_dlfunc(void* handle, const char* symbol)
398 {
399 #ifdef _WOE32
400 return (SuilVoidFunc)GetProcAddress((HMODULE)handle, (LPCSTR) symbol);
401 #else
402 typedef SuilVoidFunc (*VoidFuncGetter)(void*, const char*);
403 VoidFuncGetter dlfunc = (VoidFuncGetter)dlsym;
404 return dlfunc(handle, symbol);
405 #endif
406 }
407
408 /** Add a feature to a (mutable) LV2 feature array. */
409 static inline void
suil_add_feature(LV2_Feature *** features,unsigned * n,const char * uri,void * data)410 suil_add_feature(LV2_Feature*** features,
411 unsigned* n,
412 const char* uri,
413 void* data)
414 {
415 for (unsigned i = 0; i < *n && (*features)[i]; ++i) {
416 if (!strcmp((*features)[i]->URI, uri)) {
417 (*features)[i]->data = data;
418 return;
419 }
420 }
421
422 *features = (LV2_Feature**)realloc(*features,
423 sizeof(LV2_Feature*) * (*n + 2));
424
425 (*features)[*n] = (LV2_Feature*)malloc(sizeof(LV2_Feature));
426 (*features)[*n]->URI = uri;
427 (*features)[*n]->data = data;
428 (*features)[*n + 1] = NULL;
429 *n += 1;
430 }
431
432 #endif /* SUIL_SUIL_H */
433
434 #endif /* ifndef HAVE_SUIL */
435