1 /*
2 * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
3 *
4 * This is free software: you can redistribute it and/or modify
5 * it under the terms of the Artistic License 2.0 as published by
6 * The Perl Foundation.
7 *
8 * This source is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * Artistic License 2.0 for more details.
12 *
13 * You should have received a copy of the Artistic License 2.0
14 * along the source as a COPYING file. If not, obtain it from
15 * http://www.perlfoundation.org/artistic_license_2_0.
16 */
17
18 #ifndef _LV2_PROPS_H_
19 #define _LV2_PROPS_H_
20
21 #ifdef __cplusplus
22 extern "C" {
23 #endif
24
25 #include <stdlib.h>
26 #include <stdatomic.h>
27 #include <stdio.h>
28
29 #include <lv2/lv2plug.in/ns/lv2core/lv2.h>
30 #include <lv2/lv2plug.in/ns/ext/urid/urid.h>
31 #include <lv2/lv2plug.in/ns/ext/atom/atom.h>
32 #include <lv2/lv2plug.in/ns/ext/atom/forge.h>
33 #include <lv2/lv2plug.in/ns/ext/patch/patch.h>
34 #include <lv2/lv2plug.in/ns/ext/state/state.h>
35
36 /*****************************************************************************
37 * API START
38 *****************************************************************************/
39
40 // structures
41 typedef struct _props_def_t props_def_t;
42 typedef struct _props_impl_t props_impl_t;
43 typedef struct _props_dyn_t props_dyn_t;
44 typedef struct _props_t props_t;
45
46 typedef enum _props_dyn_ev_t {
47 PROPS_DYN_EV_ADD,
48 PROPS_DYN_EV_REM,
49 PROPS_DYN_EV_SET
50 } props_dyn_ev_t;
51
52 // function callbacks
53 typedef void (*props_event_cb_t)(
54 void *data,
55 int64_t frames,
56 props_impl_t *impl);
57
58 typedef void (*props_dyn_prop_cb_t)(
59 void *data,
60 props_dyn_ev_t ev,
61 LV2_URID subj,
62 LV2_URID prop,
63 const LV2_Atom *body);
64
65 struct _props_def_t {
66 const char *property;
67 const char *type;
68 const char *access;
69 size_t offset;
70 bool hidden;
71
72 uint32_t max_size;
73 props_event_cb_t event_cb;
74 };
75
76 struct _props_impl_t {
77 LV2_URID property;
78 LV2_URID type;
79 LV2_URID access;
80
81 struct {
82 uint32_t size;
83 void *body;
84 } value;
85 struct {
86 uint32_t size;
87 void *body;
88 } stash;
89
90 const props_def_t *def;
91
92 atomic_int state;
93 bool stashing;
94 };
95
96 struct _props_dyn_t {
97 props_dyn_prop_cb_t prop;
98 };
99
100 struct _props_t {
101 struct {
102 LV2_URID subject;
103
104 LV2_URID patch_get;
105 LV2_URID patch_set;
106 LV2_URID patch_put;
107 LV2_URID patch_patch;
108 LV2_URID patch_wildcard;
109 LV2_URID patch_add;
110 LV2_URID patch_remove;
111 LV2_URID patch_subject;
112 LV2_URID patch_body;
113 LV2_URID patch_property;
114 LV2_URID patch_value;
115 LV2_URID patch_writable;
116 LV2_URID patch_readable;
117 LV2_URID patch_sequence;
118 LV2_URID patch_error;
119 LV2_URID patch_ack;
120
121 LV2_URID atom_int;
122 LV2_URID atom_long;
123 LV2_URID atom_float;
124 LV2_URID atom_double;
125 LV2_URID atom_bool;
126 LV2_URID atom_urid;
127 LV2_URID atom_path;
128 LV2_URID atom_literal;
129 LV2_URID atom_vector;
130 LV2_URID atom_object;
131 LV2_URID atom_sequence;
132
133 LV2_URID state_StateChanged;
134 } urid;
135
136 void *data;
137
138 bool stashing;
139 atomic_bool restoring;
140
141 uint32_t max_size;
142
143 const props_dyn_t *dyn;
144
145 unsigned nimpls;
146 props_impl_t impls [1];
147 };
148
149 #define PROPS_T(PROPS, MAX_NIMPLS) \
150 props_t (PROPS); \
151 props_impl_t _impls [MAX_NIMPLS]
152
153 // rt-safe
154 static inline int
155 props_init(props_t *props, const char *subject,
156 const props_def_t *defs, int nimpls,
157 void *value_base, void *stash_base,
158 LV2_URID_Map *map, void *data);
159
160 // rt-safe
161 static inline void
162 props_dyn(props_t *props, const props_dyn_t *dyn);
163
164 // rt-safe
165 static inline void
166 props_idle(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
167 LV2_Atom_Forge_Ref *ref);
168
169 // rt-safe
170 static inline int
171 props_advance(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
172 const LV2_Atom_Object *obj, LV2_Atom_Forge_Ref *ref);
173
174 // rt-safe
175 static inline void
176 props_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
177 LV2_URID property, LV2_Atom_Forge_Ref *ref);
178
179 // rt-safe
180 static inline void
181 props_get(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
182 LV2_URID property, LV2_Atom_Forge_Ref *ref);
183
184 // rt-safe
185 static inline void
186 props_stash(props_t *props, LV2_URID property);
187
188 // rt-safe
189 static inline LV2_URID
190 props_map(props_t *props, const char *property);
191
192 // rt-safe
193 static inline const char *
194 props_unmap(props_t *props, LV2_URID property);
195
196 // non-rt
197 static inline LV2_State_Status
198 props_save(props_t *props, LV2_State_Store_Function store,
199 LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features);
200
201 // non-rt
202 static inline LV2_State_Status
203 props_restore(props_t *props, LV2_State_Retrieve_Function retrieve,
204 LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features);
205
206 /*****************************************************************************
207 * API END
208 *****************************************************************************/
209
210 // enumerations
211 typedef enum _props_state_t {
212 PROP_STATE_NONE = 0,
213 PROP_STATE_LOCK = 1,
214 PROP_STATE_RESTORE = 2
215 } props_state_t;
216
217 static inline void
_props_impl_spin_lock(props_impl_t * impl,int from,int to)218 _props_impl_spin_lock(props_impl_t *impl, int from, int to)
219 {
220 int expected = from;
221 const int desired = to;
222
223 while(!atomic_compare_exchange_strong_explicit(&impl->state, &expected, desired,
224 memory_order_acquire, memory_order_acquire))
225 {
226 // spin
227 }
228 }
229
230 static inline bool
_props_impl_try_lock(props_impl_t * impl,int from,int to)231 _props_impl_try_lock(props_impl_t *impl, int from, int to)
232 {
233 int expected = from;
234 const int desired = to;
235
236 return atomic_compare_exchange_strong_explicit(&impl->state, &expected, desired,
237 memory_order_acquire, memory_order_acquire);
238 }
239
240 static inline void
_props_impl_unlock(props_impl_t * impl,int to)241 _props_impl_unlock(props_impl_t *impl, int to)
242 {
243 atomic_store_explicit(&impl->state, to, memory_order_release);
244 }
245
246 static inline bool
_props_restoring_get(props_t * props)247 _props_restoring_get(props_t *props)
248 {
249 return atomic_exchange_explicit(&props->restoring, false, memory_order_acquire);
250 }
251
252 static inline void
_props_restoring_set(props_t * props)253 _props_restoring_set(props_t *props)
254 {
255 atomic_store_explicit(&props->restoring, true, memory_order_release);
256 }
257
258 static inline void
_props_qsort(props_impl_t * A,int n)259 _props_qsort(props_impl_t *A, int n)
260 {
261 if(n < 2)
262 return;
263
264 const props_impl_t *p = A;
265
266 int i = -1;
267 int j = n;
268
269 while(true)
270 {
271 do {
272 i += 1;
273 } while(A[i].property < p->property);
274
275 do {
276 j -= 1;
277 } while(A[j].property > p->property);
278
279 if(i >= j)
280 break;
281
282 const props_impl_t tmp = A[i];
283 A[i] = A[j];
284 A[j] = tmp;
285 }
286
287 _props_qsort(A, j + 1);
288 _props_qsort(A + j + 1, n - j - 1);
289 }
290
291 static inline props_impl_t *
_props_impl_get(props_t * props,LV2_URID property)292 _props_impl_get(props_t *props, LV2_URID property)
293 {
294 props_impl_t *base = props->impls;
295
296 for(int N = props->nimpls, half; N > 1; N -= half)
297 {
298 half = N/2;
299 props_impl_t *dst = &base[half];
300 base = (dst->property > property) ? base : dst;
301 }
302
303 return (base->property == property) ? base : NULL;
304 }
305
306 static inline LV2_Atom_Forge_Ref
_props_patch_set(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,props_impl_t * impl,int32_t sequence_num)307 _props_patch_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
308 props_impl_t *impl, int32_t sequence_num)
309 {
310 LV2_Atom_Forge_Frame obj_frame;
311
312 LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
313
314 if(ref)
315 ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_set);
316 {
317 if(props->urid.subject) // is optional
318 {
319 if(ref)
320 ref = lv2_atom_forge_key(forge, props->urid.patch_subject);
321 if(ref)
322 ref = lv2_atom_forge_urid(forge, props->urid.subject);
323 }
324
325 if(sequence_num) // is optional
326 {
327 if(ref)
328 ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
329 if(ref)
330 ref = lv2_atom_forge_int(forge, sequence_num);
331 }
332
333 if(ref)
334 ref = lv2_atom_forge_key(forge, props->urid.patch_property);
335 if(ref)
336 ref = lv2_atom_forge_urid(forge, impl->property);
337
338 if(ref)
339 lv2_atom_forge_key(forge, props->urid.patch_value);
340 if(ref)
341 ref = lv2_atom_forge_atom(forge, impl->value.size, impl->type);
342 if(ref)
343 ref = lv2_atom_forge_write(forge, impl->value.body, impl->value.size);
344 }
345 if(ref)
346 lv2_atom_forge_pop(forge, &obj_frame);
347
348 if(ref)
349 ref = lv2_atom_forge_frame_time(forge, frames);
350 if(ref)
351 ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.state_StateChanged);
352 if(ref)
353 lv2_atom_forge_pop(forge, &obj_frame);
354
355 return ref;
356 }
357
358 static inline LV2_Atom_Forge_Ref
_props_patch_get(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,props_impl_t * impl,int32_t sequence_num)359 _props_patch_get(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
360 props_impl_t *impl, int32_t sequence_num)
361 {
362 LV2_Atom_Forge_Frame obj_frame;
363
364 LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
365
366 if(ref)
367 ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_get);
368 {
369 if(props->urid.subject) // is optional
370 {
371 if(ref)
372 ref = lv2_atom_forge_key(forge, props->urid.patch_subject);
373 if(ref)
374 ref = lv2_atom_forge_urid(forge, props->urid.subject);
375 }
376
377 if(sequence_num) // is optional
378 {
379 if(ref)
380 ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
381 if(ref)
382 ref = lv2_atom_forge_int(forge, sequence_num);
383 }
384
385 if(ref)
386 ref = lv2_atom_forge_key(forge, props->urid.patch_property);
387 if(ref)
388 ref = lv2_atom_forge_urid(forge, impl->property);
389 }
390 if(ref)
391 lv2_atom_forge_pop(forge, &obj_frame);
392
393 return ref;
394 }
395
396 static inline LV2_Atom_Forge_Ref
_props_patch_error(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,int32_t sequence_num)397 _props_patch_error(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
398 int32_t sequence_num)
399 {
400 LV2_Atom_Forge_Frame obj_frame;
401
402 LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
403
404 if(ref)
405 ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_error);
406 {
407 if(ref)
408 ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
409 if(ref)
410 ref = lv2_atom_forge_int(forge, sequence_num);
411 }
412 if(ref)
413 lv2_atom_forge_pop(forge, &obj_frame);
414
415 return ref;
416 }
417
418 static inline LV2_Atom_Forge_Ref
_props_patch_ack(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,int32_t sequence_num)419 _props_patch_ack(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
420 int32_t sequence_num)
421 {
422 LV2_Atom_Forge_Frame obj_frame;
423
424 LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
425
426 if(ref)
427 ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_ack);
428 {
429 if(ref)
430 ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
431 if(ref)
432 ref = lv2_atom_forge_int(forge, sequence_num);
433 }
434 if(ref)
435 lv2_atom_forge_pop(forge, &obj_frame);
436
437 return ref;
438 }
439
440 static inline void
_props_impl_stash(props_t * props,props_impl_t * impl)441 _props_impl_stash(props_t *props, props_impl_t *impl)
442 {
443 if(_props_impl_try_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK))
444 {
445 impl->stashing = false;
446 impl->stash.size = impl->value.size;
447 memcpy(impl->stash.body, impl->value.body, impl->value.size);
448
449 _props_impl_unlock(impl, PROP_STATE_NONE);
450 }
451 else
452 {
453 impl->stashing = true; // try again later
454 props->stashing = true;
455 }
456 }
457
458 static inline void
_props_impl_restore(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,props_impl_t * impl,LV2_Atom_Forge_Ref * ref)459 _props_impl_restore(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
460 props_impl_t *impl, LV2_Atom_Forge_Ref *ref)
461 {
462 if(_props_impl_try_lock(impl, PROP_STATE_RESTORE, PROP_STATE_LOCK))
463 {
464 impl->stashing = false; // makes no sense to stash a recently restored value
465 impl->value.size = impl->stash.size;
466 memcpy(impl->value.body, impl->stash.body, impl->stash.size);
467
468 _props_impl_unlock(impl, PROP_STATE_NONE);
469
470 if(*ref && !impl->def->hidden)
471 *ref = _props_patch_set(props, forge, frames, impl, 0);
472
473 const props_def_t *def = impl->def;
474 if(def->event_cb)
475 def->event_cb(props->data, 0, impl);
476 }
477 }
478
479 static inline void
_props_impl_set(props_t * props,props_impl_t * impl,LV2_URID type,uint32_t size,const void * body)480 _props_impl_set(props_t *props, props_impl_t *impl, LV2_URID type,
481 uint32_t size, const void *body)
482 {
483 if( (impl->type == type)
484 && ( (impl->def->max_size == 0) || (size <= impl->def->max_size)) )
485 {
486 impl->value.size = size;
487 memcpy(impl->value.body, body, size);
488
489 _props_impl_stash(props, impl);
490 }
491 }
492
493 static inline int
_props_impl_init(props_t * props,props_impl_t * impl,const props_def_t * def,void * value_base,void * stash_base,LV2_URID_Map * map)494 _props_impl_init(props_t *props, props_impl_t *impl, const props_def_t *def,
495 void *value_base, void *stash_base, LV2_URID_Map *map)
496 {
497 if(!def->property || !def->type)
498 return 0;
499
500 const LV2_URID type = map->map(map->handle, def->type);
501 const LV2_URID property = map->map(map->handle, def->property);
502 const LV2_URID access = def->access
503 ? map->map(map->handle, def->access)
504 : map->map(map->handle, LV2_PATCH__writable);
505
506 if(!type || !property || !access)
507 return 0;
508
509 impl->property = property;
510 impl->access = access;
511 impl->def = def;
512 impl->value.body = (uint8_t *)value_base + def->offset;
513 impl->stash.body = (uint8_t *)stash_base + def->offset;
514
515 uint32_t size;
516 if( (type == props->urid.atom_int)
517 || (type == props->urid.atom_float)
518 || (type == props->urid.atom_bool)
519 || (type == props->urid.atom_urid) )
520 {
521 size = 4;
522 }
523 else if((type == props->urid.atom_long)
524 || (type == props->urid.atom_double) )
525 {
526 size = 8;
527 }
528 else if(type == props->urid.atom_literal)
529 {
530 size = sizeof(LV2_Atom_Literal_Body);
531 }
532 else if(type == props->urid.atom_vector)
533 {
534 size = sizeof(LV2_Atom_Vector_Body);
535 }
536 else if(type == props->urid.atom_object)
537 {
538 size = sizeof(LV2_Atom_Object_Body);
539 }
540 else if(type == props->urid.atom_sequence)
541 {
542 size = sizeof(LV2_Atom_Sequence_Body);
543 }
544 else
545 {
546 size = 0; // assume everything else as having size 0
547 }
548
549 impl->type = type;
550 impl->value.size = size;
551 impl->stash.size = size;
552
553 atomic_init(&impl->state, PROP_STATE_NONE);
554
555 // update maximal value size
556 const uint32_t max_size = def->max_size
557 ? def->max_size
558 : size;
559
560 if(max_size > props->max_size)
561 {
562 props->max_size = max_size;
563 }
564
565 return 1;
566 }
567
568 static inline int
props_init(props_t * props,const char * subject,const props_def_t * defs,int nimpls,void * value_base,void * stash_base,LV2_URID_Map * map,void * data)569 props_init(props_t *props, const char *subject,
570 const props_def_t *defs, int nimpls,
571 void *value_base, void *stash_base,
572 LV2_URID_Map *map, void *data)
573 {
574 if(!props || !defs || !value_base || !stash_base || !map)
575 return 0;
576
577 props->nimpls = nimpls;
578 props->data = data;
579
580 props->urid.subject = subject ? map->map(map->handle, subject) : 0;
581
582 props->urid.patch_get = map->map(map->handle, LV2_PATCH__Get);
583 props->urid.patch_set = map->map(map->handle, LV2_PATCH__Set);
584 props->urid.patch_put = map->map(map->handle, LV2_PATCH__Put);
585 props->urid.patch_patch = map->map(map->handle, LV2_PATCH__Patch);
586 props->urid.patch_wildcard = map->map(map->handle, LV2_PATCH__wildcard);
587 props->urid.patch_add = map->map(map->handle, LV2_PATCH__add);
588 props->urid.patch_remove = map->map(map->handle, LV2_PATCH__remove);
589 props->urid.patch_subject = map->map(map->handle, LV2_PATCH__subject);
590 props->urid.patch_body = map->map(map->handle, LV2_PATCH__body);
591 props->urid.patch_property = map->map(map->handle, LV2_PATCH__property);
592 props->urid.patch_value = map->map(map->handle, LV2_PATCH__value);
593 props->urid.patch_writable = map->map(map->handle, LV2_PATCH__writable);
594 props->urid.patch_readable = map->map(map->handle, LV2_PATCH__readable);
595 props->urid.patch_sequence = map->map(map->handle, LV2_PATCH__sequenceNumber);
596 props->urid.patch_ack = map->map(map->handle, LV2_PATCH__Ack);
597 props->urid.patch_error = map->map(map->handle, LV2_PATCH__Error);
598
599 props->urid.atom_int = map->map(map->handle, LV2_ATOM__Int);
600 props->urid.atom_long = map->map(map->handle, LV2_ATOM__Long);
601 props->urid.atom_float = map->map(map->handle, LV2_ATOM__Float);
602 props->urid.atom_double = map->map(map->handle, LV2_ATOM__Double);
603 props->urid.atom_bool = map->map(map->handle, LV2_ATOM__Bool);
604 props->urid.atom_urid = map->map(map->handle, LV2_ATOM__URID);
605 props->urid.atom_path = map->map(map->handle, LV2_ATOM__Path);
606 props->urid.atom_literal = map->map(map->handle, LV2_ATOM__Literal);
607 props->urid.atom_vector = map->map(map->handle, LV2_ATOM__Vector);
608 props->urid.atom_object = map->map(map->handle, LV2_ATOM__Object);
609 props->urid.atom_sequence = map->map(map->handle, LV2_ATOM__Sequence);
610
611 props->urid.state_StateChanged = map->map(map->handle, LV2_STATE__StateChanged);
612
613 atomic_init(&props->restoring, false);
614
615 int status = 1;
616 for(unsigned i = 0; i < props->nimpls; i++)
617 {
618 props_impl_t *impl = &props->impls[i];
619
620 status = status
621 && _props_impl_init(props, impl, &defs[i], value_base, stash_base, map);
622 }
623
624 _props_qsort(props->impls, props->nimpls);
625
626 return status;
627 }
628
629 static inline void
props_dyn(props_t * props,const props_dyn_t * dyn)630 props_dyn(props_t *props, const props_dyn_t *dyn)
631 {
632 props->dyn = dyn;
633 }
634
635 static inline void
props_idle(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,LV2_Atom_Forge_Ref * ref)636 props_idle(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
637 LV2_Atom_Forge_Ref *ref)
638 {
639 if(_props_restoring_get(props))
640 {
641 for(unsigned i = 0; i < props->nimpls; i++)
642 {
643 props_impl_t *impl = &props->impls[i];
644
645 _props_impl_restore(props, forge, frames, impl, ref);
646 }
647 }
648
649 if(props->stashing)
650 {
651 props->stashing = false;
652
653 for(unsigned i = 0; i < props->nimpls; i++)
654 {
655 props_impl_t *impl = &props->impls[i];
656
657 if(impl->stashing)
658 _props_impl_stash(props, impl);
659 }
660 }
661 }
662
663 static inline int
props_advance(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,const LV2_Atom_Object * obj,LV2_Atom_Forge_Ref * ref)664 props_advance(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
665 const LV2_Atom_Object *obj, LV2_Atom_Forge_Ref *ref)
666 {
667 if(!lv2_atom_forge_is_object_type(forge, obj->atom.type))
668 {
669 return 0;
670 }
671
672 if(obj->body.otype == props->urid.patch_get)
673 {
674 const LV2_Atom_URID *subject = NULL;
675 const LV2_Atom_URID *property = NULL;
676 const LV2_Atom_Int *sequence = NULL;
677
678 lv2_atom_object_get(obj,
679 props->urid.patch_subject, &subject,
680 props->urid.patch_property, &property,
681 props->urid.patch_sequence, &sequence,
682 0);
683
684 // check for a matching optional subject
685 if( (subject && props->urid.subject)
686 && ( (subject->atom.type != props->urid.atom_urid)
687 || (subject->body != props->urid.subject) ) )
688 {
689 return 0;
690 }
691
692 int32_t sequence_num = 0;
693 if(sequence && (sequence->atom.type == props->urid.atom_int))
694 {
695 sequence_num = sequence->body;
696 }
697
698 if(!property)
699 {
700 for(unsigned i = 0; i < props->nimpls; i++)
701 {
702 props_impl_t *impl = &props->impls[i];
703
704 if(*ref && !impl->def->hidden)
705 *ref = _props_patch_set(props, forge, frames, impl, sequence_num);
706 }
707
708 return 1;
709 }
710 else if(property->atom.type == props->urid.atom_urid)
711 {
712 props_impl_t *impl = _props_impl_get(props, property->body);
713
714 if(impl)
715 {
716 if(*ref && !impl->def->hidden)
717 *ref = _props_patch_set(props, forge, frames, impl, sequence_num);
718
719 return 1;
720 }
721 else if(sequence_num)
722 {
723 if(*ref)
724 *ref = _props_patch_error(props, forge, frames, sequence_num);
725 }
726 }
727 else if(sequence_num)
728 {
729 if(*ref)
730 *ref = _props_patch_error(props, forge, frames, sequence_num);
731 }
732 }
733 else if(obj->body.otype == props->urid.patch_set)
734 {
735 const LV2_Atom_URID *subject = NULL;
736 const LV2_Atom_URID *property = NULL;
737 const LV2_Atom_Int *sequence = NULL;
738 const LV2_Atom *value = NULL;
739
740 lv2_atom_object_get(obj,
741 props->urid.patch_subject, &subject,
742 props->urid.patch_property, &property,
743 props->urid.patch_sequence, &sequence,
744 props->urid.patch_value, &value,
745 0);
746
747 // check for a matching optional subject
748 if( (subject && props->urid.subject)
749 && ( (subject->atom.type != props->urid.atom_urid)
750 || (subject->body != props->urid.subject) ) )
751 {
752 return 0;
753 }
754
755 int32_t sequence_num = 0;
756 if(sequence && (sequence->atom.type == props->urid.atom_int))
757 {
758 sequence_num = sequence->body;
759 }
760
761 if(!property || (property->atom.type != props->urid.atom_urid) || !value)
762 {
763 if(sequence_num)
764 {
765 if(ref)
766 *ref = _props_patch_error(props, forge, frames, sequence_num);
767 }
768
769 return 0;
770 }
771
772 props_impl_t *impl = _props_impl_get(props, property->body);
773 if(impl)
774 {
775 _props_impl_set(props, impl, value->type, value->size,
776 LV2_ATOM_BODY_CONST(value));
777
778 // send on (e.g. to UI)
779 if(*ref && !impl->def->hidden)
780 *ref = _props_patch_set(props, forge, frames, impl, sequence_num);
781
782 const props_def_t *def = impl->def;
783 if(def->event_cb)
784 def->event_cb(props->data, frames, impl);
785
786 if(sequence_num)
787 {
788 if(*ref)
789 *ref = _props_patch_ack(props, forge, frames, sequence_num);
790 }
791
792 return 1;
793 }
794 else if(props->dyn && props->dyn->prop)
795 {
796 const LV2_URID subj = subject ? subject->body : 0;
797
798 props->dyn->prop(props->data, PROPS_DYN_EV_SET, subj, property->body, value);
799
800 //TODO send ack
801 }
802 else if(sequence_num)
803 {
804 if(*ref)
805 *ref = _props_patch_error(props, forge, frames, sequence_num);
806 }
807 }
808 else if(obj->body.otype == props->urid.patch_put)
809 {
810 const LV2_Atom_URID *subject = NULL;
811 const LV2_Atom_Int *sequence = NULL;
812 const LV2_Atom_Object *body = NULL;
813
814 lv2_atom_object_get(obj,
815 props->urid.patch_subject, &subject,
816 props->urid.patch_sequence, &sequence,
817 props->urid.patch_body, &body,
818 0);
819
820 // check for a matching optional subject
821 if( (subject && props->urid.subject)
822 && ( (subject->atom.type != props->urid.atom_urid)
823 || (subject->body != props->urid.subject) ) )
824 {
825 return 0;
826 }
827
828 int32_t sequence_num = 0;
829 if(sequence && (sequence->atom.type == props->urid.atom_int))
830 {
831 sequence_num = sequence->body;
832 }
833
834 if(!body || !lv2_atom_forge_is_object_type(forge, body->atom.type))
835 {
836 if(sequence_num)
837 {
838 if(*ref)
839 *ref = _props_patch_error(props, forge, frames, sequence_num);
840 }
841
842 return 0;
843 }
844
845 LV2_ATOM_OBJECT_FOREACH(body, prop)
846 {
847 const LV2_URID property = prop->key;
848 const LV2_Atom *value = &prop->value;
849
850 props_impl_t *impl = _props_impl_get(props, property);
851 if(impl)
852 {
853 _props_impl_set(props, impl, value->type, value->size,
854 LV2_ATOM_BODY_CONST(value));
855
856 // send on (e.g. to UI)
857 if(*ref && !impl->def->hidden)
858 *ref = _props_patch_set(props, forge, frames, impl, sequence_num);
859
860 const props_def_t *def = impl->def;
861 if(def->event_cb)
862 def->event_cb(props->data, frames, impl);
863 }
864 else if(props->dyn && props->dyn->prop)
865 {
866 const LV2_URID subj = subject ? subject->body : 0;
867
868 props->dyn->prop(props->data, PROPS_DYN_EV_SET, subj, property, value);
869
870 //TODO send ack
871 }
872 }
873
874 if(sequence_num)
875 {
876 if(*ref)
877 *ref = _props_patch_ack(props, forge, frames, sequence_num);
878 }
879
880 return 1;
881 }
882 else if(obj->body.otype == props->urid.patch_patch)
883 {
884 const LV2_Atom_URID *subject = NULL;
885 const LV2_Atom_Int *sequence = NULL;
886 const LV2_Atom_Object *add = NULL;
887 const LV2_Atom_Object *rem = NULL;
888
889 lv2_atom_object_get(obj,
890 props->urid.patch_subject, &subject,
891 props->urid.patch_sequence, &sequence,
892 props->urid.patch_add, &add,
893 props->urid.patch_remove, &rem,
894 0);
895
896 LV2_URID subj = 0;
897 if(subject && (subject->atom.type == props->urid.atom_urid))
898 {
899 subj = subject->body;
900 }
901
902 int32_t sequence_num = 0;
903 if(sequence && (sequence->atom.type == props->urid.atom_int))
904 {
905 sequence_num = sequence->body;
906 }
907
908 if(rem && lv2_atom_forge_is_object_type(forge, rem->atom.type))
909 {
910 LV2_ATOM_OBJECT_FOREACH(rem, prop)
911 {
912 const LV2_URID property = prop->key;
913 const LV2_Atom *value = &prop->value;
914
915 if(props->dyn && props->dyn->prop)
916 {
917 props->dyn->prop(props->data, PROPS_DYN_EV_REM, subj, property, value);
918 }
919 }
920 }
921
922 if(add && lv2_atom_forge_is_object_type(forge, add->atom.type))
923 {
924 LV2_ATOM_OBJECT_FOREACH(add, prop)
925 {
926 const LV2_URID property = prop->key;
927 const LV2_Atom *value = &prop->value;
928
929 if(props->dyn && props->dyn->prop)
930 {
931 props->dyn->prop(props->data, PROPS_DYN_EV_ADD, subj, property, value);
932 }
933 }
934 }
935
936 if(sequence_num && *ref)
937 {
938 *ref = _props_patch_ack(props, forge, frames, sequence_num);
939 }
940
941 return 1;
942 }
943
944 return 0; // did not handle a patch event
945 }
946
947 static inline void
props_set(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,LV2_URID property,LV2_Atom_Forge_Ref * ref)948 props_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
949 LV2_URID property, LV2_Atom_Forge_Ref *ref)
950 {
951 props_impl_t *impl = _props_impl_get(props, property);
952
953 if(impl)
954 {
955 _props_impl_stash(props, impl);
956
957 if(*ref && !impl->def->hidden) //TODO use patch:sequenceNumber
958 *ref = _props_patch_set(props, forge, frames, impl, 0);
959 }
960 }
961
962 static inline void
props_get(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,LV2_URID property,LV2_Atom_Forge_Ref * ref)963 props_get(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
964 LV2_URID property, LV2_Atom_Forge_Ref *ref)
965 {
966 props_impl_t *impl = _props_impl_get(props, property);
967
968 if(impl)
969 {
970 if(*ref && !impl->def->hidden) //TODO use patch:sequenceNumber
971 *ref = _props_patch_get(props, forge, frames, impl, 0);
972 }
973 }
974
975 static inline void
props_stash(props_t * props,LV2_URID property)976 props_stash(props_t *props, LV2_URID property)
977 {
978 props_impl_t *impl = _props_impl_get(props, property);
979
980 if(impl)
981 _props_impl_stash(props, impl);
982 }
983
984 static inline LV2_URID
props_map(props_t * props,const char * uri)985 props_map(props_t *props, const char *uri)
986 {
987 for(unsigned i = 0; i < props->nimpls; i++)
988 {
989 props_impl_t *impl = &props->impls[i];
990
991 if(!strcmp(impl->def->property, uri))
992 return impl->property;
993 }
994
995 return 0;
996 }
997
998 static inline const char *
props_unmap(props_t * props,LV2_URID property)999 props_unmap(props_t *props, LV2_URID property)
1000 {
1001 props_impl_t *impl = _props_impl_get(props, property);
1002
1003 if(impl)
1004 return impl->def->property;
1005
1006 return NULL;
1007 }
1008
1009 static inline int
_copy_file(const char * to,const char * from)1010 _copy_file(const char *to, const char *from)
1011 {
1012 FILE *dst = NULL;
1013 FILE *src = NULL;
1014 int ch;
1015
1016 dst = fopen(to, "wb");
1017 if(!dst)
1018 {
1019 return 1;
1020 }
1021
1022 src = fopen(from, "rb");
1023 if(!src)
1024 {
1025 fclose(dst);
1026
1027 return 1;
1028 }
1029
1030 while( (ch = fgetc(src)) != EOF)
1031 {
1032 fputc(ch, dst);
1033 }
1034
1035 fclose(dst);
1036 fclose(src);
1037
1038 return 0;
1039 }
1040
1041 static inline void
_free_path(const LV2_State_Free_Path * free_path,char * path)1042 _free_path(const LV2_State_Free_Path *free_path, char *path)
1043 {
1044 if(free_path && free_path->free_path)
1045 {
1046 free_path->free_path(free_path->handle, path);
1047 }
1048 else
1049 {
1050 free(path);
1051 }
1052 }
1053
1054 static inline LV2_State_Status
props_save(props_t * props,LV2_State_Store_Function store,LV2_State_Handle state,uint32_t flags,const LV2_Feature * const * features)1055 props_save(props_t *props, LV2_State_Store_Function store,
1056 LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features)
1057 {
1058 const LV2_State_Map_Path *map_path = NULL;
1059 const LV2_State_Make_Path *make_path = NULL;
1060 const LV2_State_Free_Path *free_path = NULL;
1061
1062 // set POD flag if not already set by host
1063 flags |= LV2_STATE_IS_POD;
1064
1065 for(unsigned i = 0; features[i]; i++)
1066 {
1067 if(!strcmp(features[i]->URI, LV2_STATE__mapPath))
1068 {
1069 map_path = features[i]->data;
1070 }
1071 else if(!strcmp(features[i]->URI, LV2_STATE__makePath))
1072 {
1073 make_path = features[i]->data;
1074 }
1075 else if(!strcmp(features[i]->URI, LV2_STATE__freePath))
1076 {
1077 free_path = features[i]->data;
1078 }
1079 }
1080
1081 void *body = malloc(props->max_size); // create memory to store widest value
1082 if(body)
1083 {
1084 for(unsigned i = 0; i < props->nimpls; i++)
1085 {
1086 props_impl_t *impl = &props->impls[i];
1087
1088 if(impl->access == props->urid.patch_readable)
1089 continue; // skip read-only, as it makes no sense to restore them
1090
1091 _props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
1092
1093 // create temporary copy of value, store() may well be blocking
1094 const uint32_t size = impl->stash.size;
1095 memcpy(body, impl->stash.body, size);
1096
1097 _props_impl_unlock(impl, PROP_STATE_NONE);
1098
1099 if( map_path && map_path->abstract_path
1100 && (impl->type == props->urid.atom_path) )
1101 {
1102 const char *path = strstr(body, "file://") == body
1103 ? (char *)body + 7 // skip "file://"
1104 : (char *)body;
1105
1106 char *abstract = NULL;
1107
1108 if( make_path && make_path->path
1109 && (strstr(path, "/tmp") == path) )
1110 {
1111 char *absolute = make_path->path(make_path->handle, basename(path));
1112
1113 if(absolute)
1114 {
1115 if(_copy_file(absolute, path) == 0)
1116 {
1117 abstract = map_path->abstract_path(map_path->handle, absolute);
1118 }
1119
1120 _free_path(free_path, absolute);
1121 }
1122 }
1123 else
1124 {
1125 abstract = map_path->abstract_path(map_path->handle, path);
1126 }
1127
1128 if(abstract)
1129 {
1130 const uint32_t sz = strlen(abstract) + 1;
1131 store(state, impl->property, abstract, sz, impl->type, flags);
1132
1133 _free_path(free_path, abstract);
1134 }
1135 }
1136 else // !Path
1137 {
1138 store(state, impl->property, body, size, impl->type, flags);
1139 }
1140 }
1141
1142 free(body);
1143 }
1144
1145 return LV2_STATE_SUCCESS;
1146 }
1147
1148 static inline LV2_State_Status
props_restore(props_t * props,LV2_State_Retrieve_Function retrieve,LV2_State_Handle state,uint32_t flags,const LV2_Feature * const * features)1149 props_restore(props_t *props, LV2_State_Retrieve_Function retrieve,
1150 LV2_State_Handle state, uint32_t flags __attribute__((unused)),
1151 const LV2_Feature *const *features)
1152 {
1153 const LV2_State_Map_Path *map_path = NULL;
1154 const LV2_State_Free_Path *free_path = NULL;
1155
1156 for(unsigned i = 0; features[i]; i++)
1157 {
1158 if(!strcmp(features[i]->URI, LV2_STATE__mapPath))
1159 {
1160 map_path = features[i]->data;
1161 }
1162 if(!strcmp(features[i]->URI, LV2_STATE__freePath))
1163 {
1164 free_path = features[i]->data;
1165 }
1166 }
1167
1168 for(unsigned i = 0; i < props->nimpls; i++)
1169 {
1170 props_impl_t *impl = &props->impls[i];
1171
1172 if(impl->access == props->urid.patch_readable)
1173 continue; // skip read-only, as it makes no sense to restore them
1174
1175 size_t size;
1176 uint32_t type;
1177 uint32_t _flags;
1178 const void *body = retrieve(state, impl->property, &size, &type, &_flags);
1179
1180 if( body
1181 && (type == impl->type)
1182 && ( (impl->def->max_size == 0) || (size <= impl->def->max_size) ) )
1183 {
1184 if( map_path && map_path->absolute_path
1185 && (type == props->urid.atom_path) )
1186 {
1187 char *absolute = map_path->absolute_path(map_path->handle, body);
1188 if(absolute)
1189 {
1190 const uint32_t sz = strlen(absolute) + 1;
1191
1192 _props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
1193
1194 impl->stash.size = sz;
1195 memcpy(impl->stash.body, absolute, sz);
1196
1197 _props_impl_unlock(impl, PROP_STATE_RESTORE);
1198
1199 _free_path(free_path, absolute);
1200 }
1201 }
1202 else // !Path
1203 {
1204 _props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
1205
1206 impl->stash.size = size;
1207 memcpy(impl->stash.body, body, size);
1208
1209 _props_impl_unlock(impl, PROP_STATE_RESTORE);
1210 }
1211 }
1212 }
1213
1214 _props_restoring_set(props);
1215
1216 return LV2_STATE_SUCCESS;
1217 }
1218
1219 #ifdef __cplusplus
1220 }
1221 #endif
1222
1223 #endif // _LV2_PROPS_H_
1224