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