1 /******************************************************************************
2 Copyright (C) 2014 by Hugh Bailey <obs.jim@gmail.com>
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 ******************************************************************************/
17
18 #include "util/bmem.h"
19 #include "util/darray.h"
20 #include "obs-internal.h"
21 #include "obs-properties.h"
22
23 static inline void *get_property_data(struct obs_property *prop);
24
25 /* ------------------------------------------------------------------------- */
26
27 struct float_data {
28 double min, max, step;
29 enum obs_number_type type;
30 char *suffix;
31 };
32
33 struct int_data {
34 int min, max, step;
35 enum obs_number_type type;
36 char *suffix;
37 };
38
39 struct list_item {
40 char *name;
41 bool disabled;
42
43 union {
44 char *str;
45 long long ll;
46 double d;
47 };
48 };
49
50 struct path_data {
51 char *filter;
52 char *default_path;
53 enum obs_path_type type;
54 };
55
56 struct text_data {
57 enum obs_text_type type;
58 bool monospace;
59 };
60
61 struct list_data {
62 DARRAY(struct list_item) items;
63 enum obs_combo_type type;
64 enum obs_combo_format format;
65 };
66
67 struct editable_list_data {
68 enum obs_editable_list_type type;
69 char *filter;
70 char *default_path;
71 };
72
73 struct button_data {
74 obs_property_clicked_t callback;
75 enum obs_button_type type;
76 char *url;
77 };
78
79 struct frame_rate_option {
80 char *name;
81 char *description;
82 };
83
84 struct frame_rate_range {
85 struct media_frames_per_second min_time;
86 struct media_frames_per_second max_time;
87 };
88
89 struct frame_rate_data {
90 DARRAY(struct frame_rate_option) extra_options;
91 DARRAY(struct frame_rate_range) ranges;
92 };
93
94 struct group_data {
95 enum obs_group_type type;
96 obs_properties_t *content;
97 };
98
path_data_free(struct path_data * data)99 static inline void path_data_free(struct path_data *data)
100 {
101 bfree(data->default_path);
102 if (data->type == OBS_PATH_FILE)
103 bfree(data->filter);
104 }
105
editable_list_data_free(struct editable_list_data * data)106 static inline void editable_list_data_free(struct editable_list_data *data)
107 {
108 bfree(data->default_path);
109 bfree(data->filter);
110 }
111
list_item_free(struct list_data * data,struct list_item * item)112 static inline void list_item_free(struct list_data *data,
113 struct list_item *item)
114 {
115 bfree(item->name);
116 if (data->format == OBS_COMBO_FORMAT_STRING)
117 bfree(item->str);
118 }
119
list_data_free(struct list_data * data)120 static inline void list_data_free(struct list_data *data)
121 {
122 for (size_t i = 0; i < data->items.num; i++)
123 list_item_free(data, data->items.array + i);
124
125 da_free(data->items);
126 }
127
frame_rate_data_options_free(struct frame_rate_data * data)128 static inline void frame_rate_data_options_free(struct frame_rate_data *data)
129 {
130 for (size_t i = 0; i < data->extra_options.num; i++) {
131 struct frame_rate_option *opt = &data->extra_options.array[i];
132 bfree(opt->name);
133 bfree(opt->description);
134 }
135
136 da_resize(data->extra_options, 0);
137 }
138
frame_rate_data_ranges_free(struct frame_rate_data * data)139 static inline void frame_rate_data_ranges_free(struct frame_rate_data *data)
140 {
141 da_resize(data->ranges, 0);
142 }
143
frame_rate_data_free(struct frame_rate_data * data)144 static inline void frame_rate_data_free(struct frame_rate_data *data)
145 {
146 frame_rate_data_options_free(data);
147 frame_rate_data_ranges_free(data);
148
149 da_free(data->extra_options);
150 da_free(data->ranges);
151 }
152
group_data_free(struct group_data * data)153 static inline void group_data_free(struct group_data *data)
154 {
155 obs_properties_destroy(data->content);
156 }
157
int_data_free(struct int_data * data)158 static inline void int_data_free(struct int_data *data)
159 {
160 if (data->suffix)
161 bfree(data->suffix);
162 }
163
float_data_free(struct float_data * data)164 static inline void float_data_free(struct float_data *data)
165 {
166 if (data->suffix)
167 bfree(data->suffix);
168 }
169
170 struct obs_properties;
171
172 struct obs_property {
173 char *name;
174 char *desc;
175 char *long_desc;
176 void *priv;
177 enum obs_property_type type;
178 bool visible;
179 bool enabled;
180
181 struct obs_properties *parent;
182
183 obs_property_modified_t modified;
184 obs_property_modified2_t modified2;
185
186 struct obs_property *next;
187 };
188
189 struct obs_properties {
190 void *param;
191 void (*destroy)(void *param);
192 uint32_t flags;
193
194 struct obs_property *first_property;
195 struct obs_property **last;
196 struct obs_property *parent;
197 };
198
obs_properties_create(void)199 obs_properties_t *obs_properties_create(void)
200 {
201 struct obs_properties *props;
202 props = bzalloc(sizeof(struct obs_properties));
203 props->last = &props->first_property;
204 return props;
205 }
206
obs_properties_set_param(obs_properties_t * props,void * param,void (* destroy)(void * param))207 void obs_properties_set_param(obs_properties_t *props, void *param,
208 void (*destroy)(void *param))
209 {
210 if (!props)
211 return;
212
213 if (props->param && props->destroy)
214 props->destroy(props->param);
215
216 props->param = param;
217 props->destroy = destroy;
218 }
219
obs_properties_set_flags(obs_properties_t * props,uint32_t flags)220 void obs_properties_set_flags(obs_properties_t *props, uint32_t flags)
221 {
222 if (props)
223 props->flags = flags;
224 }
225
obs_properties_get_flags(obs_properties_t * props)226 uint32_t obs_properties_get_flags(obs_properties_t *props)
227 {
228 return props ? props->flags : 0;
229 }
230
obs_properties_get_param(obs_properties_t * props)231 void *obs_properties_get_param(obs_properties_t *props)
232 {
233 return props ? props->param : NULL;
234 }
235
obs_properties_create_param(void * param,void (* destroy)(void * param))236 obs_properties_t *obs_properties_create_param(void *param,
237 void (*destroy)(void *param))
238 {
239 struct obs_properties *props = obs_properties_create();
240 obs_properties_set_param(props, param, destroy);
241 return props;
242 }
243
obs_property_destroy(struct obs_property * property)244 static void obs_property_destroy(struct obs_property *property)
245 {
246 if (property->type == OBS_PROPERTY_LIST)
247 list_data_free(get_property_data(property));
248 else if (property->type == OBS_PROPERTY_PATH)
249 path_data_free(get_property_data(property));
250 else if (property->type == OBS_PROPERTY_EDITABLE_LIST)
251 editable_list_data_free(get_property_data(property));
252 else if (property->type == OBS_PROPERTY_FRAME_RATE)
253 frame_rate_data_free(get_property_data(property));
254 else if (property->type == OBS_PROPERTY_GROUP)
255 group_data_free(get_property_data(property));
256 else if (property->type == OBS_PROPERTY_INT)
257 int_data_free(get_property_data(property));
258 else if (property->type == OBS_PROPERTY_FLOAT)
259 float_data_free(get_property_data(property));
260
261 bfree(property->name);
262 bfree(property->desc);
263 bfree(property->long_desc);
264 bfree(property);
265 }
266
obs_properties_destroy(obs_properties_t * props)267 void obs_properties_destroy(obs_properties_t *props)
268 {
269 if (props) {
270 struct obs_property *p = props->first_property;
271
272 if (props->destroy && props->param)
273 props->destroy(props->param);
274
275 while (p) {
276 struct obs_property *next = p->next;
277 obs_property_destroy(p);
278 p = next;
279 }
280
281 bfree(props);
282 }
283 }
284
obs_properties_first(obs_properties_t * props)285 obs_property_t *obs_properties_first(obs_properties_t *props)
286 {
287 return (props != NULL) ? props->first_property : NULL;
288 }
289
obs_properties_get(obs_properties_t * props,const char * name)290 obs_property_t *obs_properties_get(obs_properties_t *props, const char *name)
291 {
292 struct obs_property *property;
293
294 if (!props)
295 return NULL;
296
297 property = props->first_property;
298 while (property) {
299 if (strcmp(property->name, name) == 0)
300 return property;
301
302 if (property->type == OBS_PROPERTY_GROUP) {
303 obs_properties_t *group =
304 obs_property_group_content(property);
305 obs_property_t *found = obs_properties_get(group, name);
306 if (found != NULL) {
307 return found;
308 }
309 }
310
311 property = property->next;
312 }
313
314 return NULL;
315 }
316
obs_properties_get_parent(obs_properties_t * props)317 obs_properties_t *obs_properties_get_parent(obs_properties_t *props)
318 {
319 return props->parent ? props->parent->parent : NULL;
320 }
321
obs_properties_remove_by_name(obs_properties_t * props,const char * name)322 void obs_properties_remove_by_name(obs_properties_t *props, const char *name)
323 {
324 if (!props)
325 return;
326
327 /* obs_properties_t is a forward-linked-list, so we need to keep both
328 * previous and current pointers around. That way we can fix up the
329 * pointers for the previous element if we find a match.
330 */
331 struct obs_property *cur = props->first_property;
332 struct obs_property *prev = props->first_property;
333
334 while (cur) {
335 if (strcmp(cur->name, name) == 0) {
336 // Fix props->last pointer.
337 if (props->last == &cur->next) {
338 if (cur == prev) {
339 // If we are the last entry and there
340 // is no previous entry, reset.
341 props->last = &props->first_property;
342 } else {
343 // If we are the last entry and there
344 // is a previous entry, update.
345 props->last = &prev->next;
346 }
347 }
348
349 // Fix props->first_property.
350 if (props->first_property == cur)
351 props->first_property = cur->next;
352
353 // Update the previous element next pointer with our
354 // next pointer. This is an automatic no-op if both
355 // elements alias the same memory.
356 prev->next = cur->next;
357
358 // Finally clear our own next pointer and destroy.
359 cur->next = NULL;
360 obs_property_destroy(cur);
361
362 break;
363 }
364
365 if (cur->type == OBS_PROPERTY_GROUP) {
366 obs_properties_remove_by_name(
367 obs_property_group_content(cur), name);
368 }
369
370 prev = cur;
371 cur = cur->next;
372 }
373 }
374
obs_properties_apply_settings_internal(obs_properties_t * props,obs_data_t * settings,obs_properties_t * realprops)375 void obs_properties_apply_settings_internal(obs_properties_t *props,
376 obs_data_t *settings,
377 obs_properties_t *realprops)
378 {
379 struct obs_property *p;
380
381 p = props->first_property;
382 while (p) {
383 if (p->type == OBS_PROPERTY_GROUP) {
384 obs_properties_apply_settings_internal(
385 obs_property_group_content(p), settings,
386 realprops);
387 }
388 if (p->modified)
389 p->modified(realprops, p, settings);
390 else if (p->modified2)
391 p->modified2(p->priv, realprops, p, settings);
392 p = p->next;
393 }
394 }
395
obs_properties_apply_settings(obs_properties_t * props,obs_data_t * settings)396 void obs_properties_apply_settings(obs_properties_t *props,
397 obs_data_t *settings)
398 {
399 if (!props)
400 return;
401
402 obs_properties_apply_settings_internal(props, settings, props);
403 }
404
405 /* ------------------------------------------------------------------------- */
406
propertes_add(struct obs_properties * props,struct obs_property * p)407 static inline void propertes_add(struct obs_properties *props,
408 struct obs_property *p)
409 {
410 *props->last = p;
411 props->last = &p->next;
412 }
413
get_property_size(enum obs_property_type type)414 static inline size_t get_property_size(enum obs_property_type type)
415 {
416 switch (type) {
417 case OBS_PROPERTY_INVALID:
418 return 0;
419 case OBS_PROPERTY_BOOL:
420 return 0;
421 case OBS_PROPERTY_INT:
422 return sizeof(struct int_data);
423 case OBS_PROPERTY_FLOAT:
424 return sizeof(struct float_data);
425 case OBS_PROPERTY_TEXT:
426 return sizeof(struct text_data);
427 case OBS_PROPERTY_PATH:
428 return sizeof(struct path_data);
429 case OBS_PROPERTY_LIST:
430 return sizeof(struct list_data);
431 case OBS_PROPERTY_COLOR:
432 return 0;
433 case OBS_PROPERTY_BUTTON:
434 return sizeof(struct button_data);
435 case OBS_PROPERTY_FONT:
436 return 0;
437 case OBS_PROPERTY_EDITABLE_LIST:
438 return sizeof(struct editable_list_data);
439 case OBS_PROPERTY_FRAME_RATE:
440 return sizeof(struct frame_rate_data);
441 case OBS_PROPERTY_GROUP:
442 return sizeof(struct group_data);
443 case OBS_PROPERTY_COLOR_ALPHA:
444 return 0;
445 }
446
447 return 0;
448 }
449
new_prop(struct obs_properties * props,const char * name,const char * desc,enum obs_property_type type)450 static inline struct obs_property *new_prop(struct obs_properties *props,
451 const char *name, const char *desc,
452 enum obs_property_type type)
453 {
454 size_t data_size = get_property_size(type);
455 struct obs_property *p;
456
457 p = bzalloc(sizeof(struct obs_property) + data_size);
458 p->parent = props;
459 p->enabled = true;
460 p->visible = true;
461 p->type = type;
462 p->name = bstrdup(name);
463 p->desc = bstrdup(desc);
464 propertes_add(props, p);
465
466 return p;
467 }
468
get_topmost_parent(obs_properties_t * props)469 static inline obs_properties_t *get_topmost_parent(obs_properties_t *props)
470 {
471 obs_properties_t *parent = props;
472 obs_properties_t *last_parent = parent;
473 while (parent) {
474 last_parent = parent;
475 parent = obs_properties_get_parent(parent);
476 }
477 return last_parent;
478 }
479
contains_prop(struct obs_properties * props,const char * name)480 static inline bool contains_prop(struct obs_properties *props, const char *name)
481 {
482 struct obs_property *p = props->first_property;
483
484 while (p) {
485 if (strcmp(p->name, name) == 0) {
486 blog(LOG_WARNING, "Property '%s' exists", name);
487 return true;
488 }
489
490 if (p->type == OBS_PROPERTY_GROUP) {
491 if (contains_prop(obs_property_group_content(p),
492 name)) {
493 return true;
494 }
495 }
496
497 p = p->next;
498 }
499
500 return false;
501 }
502
has_prop(struct obs_properties * props,const char * name)503 static inline bool has_prop(struct obs_properties *props, const char *name)
504 {
505 return contains_prop(get_topmost_parent(props), name);
506 }
507
get_property_data(struct obs_property * prop)508 static inline void *get_property_data(struct obs_property *prop)
509 {
510 return (uint8_t *)prop + sizeof(struct obs_property);
511 }
512
get_type_data(struct obs_property * prop,enum obs_property_type type)513 static inline void *get_type_data(struct obs_property *prop,
514 enum obs_property_type type)
515 {
516 if (!prop || prop->type != type)
517 return NULL;
518
519 return get_property_data(prop);
520 }
521
obs_properties_add_bool(obs_properties_t * props,const char * name,const char * desc)522 obs_property_t *obs_properties_add_bool(obs_properties_t *props,
523 const char *name, const char *desc)
524 {
525 if (!props || has_prop(props, name))
526 return NULL;
527 return new_prop(props, name, desc, OBS_PROPERTY_BOOL);
528 }
529
add_int(obs_properties_t * props,const char * name,const char * desc,int min,int max,int step,enum obs_number_type type)530 static obs_property_t *add_int(obs_properties_t *props, const char *name,
531 const char *desc, int min, int max, int step,
532 enum obs_number_type type)
533 {
534 if (!props || has_prop(props, name))
535 return NULL;
536
537 struct obs_property *p = new_prop(props, name, desc, OBS_PROPERTY_INT);
538 struct int_data *data = get_property_data(p);
539 data->min = min;
540 data->max = max;
541 data->step = step;
542 data->type = type;
543 return p;
544 }
545
add_flt(obs_properties_t * props,const char * name,const char * desc,double min,double max,double step,enum obs_number_type type)546 static obs_property_t *add_flt(obs_properties_t *props, const char *name,
547 const char *desc, double min, double max,
548 double step, enum obs_number_type type)
549 {
550 if (!props || has_prop(props, name))
551 return NULL;
552
553 struct obs_property *p =
554 new_prop(props, name, desc, OBS_PROPERTY_FLOAT);
555 struct float_data *data = get_property_data(p);
556 data->min = min;
557 data->max = max;
558 data->step = step;
559 data->type = type;
560 return p;
561 }
562
obs_properties_add_int(obs_properties_t * props,const char * name,const char * desc,int min,int max,int step)563 obs_property_t *obs_properties_add_int(obs_properties_t *props,
564 const char *name, const char *desc,
565 int min, int max, int step)
566 {
567 return add_int(props, name, desc, min, max, step, OBS_NUMBER_SCROLLER);
568 }
569
obs_properties_add_float(obs_properties_t * props,const char * name,const char * desc,double min,double max,double step)570 obs_property_t *obs_properties_add_float(obs_properties_t *props,
571 const char *name, const char *desc,
572 double min, double max, double step)
573 {
574 return add_flt(props, name, desc, min, max, step, OBS_NUMBER_SCROLLER);
575 }
576
obs_properties_add_int_slider(obs_properties_t * props,const char * name,const char * desc,int min,int max,int step)577 obs_property_t *obs_properties_add_int_slider(obs_properties_t *props,
578 const char *name,
579 const char *desc, int min,
580 int max, int step)
581 {
582 return add_int(props, name, desc, min, max, step, OBS_NUMBER_SLIDER);
583 }
584
obs_properties_add_float_slider(obs_properties_t * props,const char * name,const char * desc,double min,double max,double step)585 obs_property_t *obs_properties_add_float_slider(obs_properties_t *props,
586 const char *name,
587 const char *desc, double min,
588 double max, double step)
589 {
590 return add_flt(props, name, desc, min, max, step, OBS_NUMBER_SLIDER);
591 }
592
obs_properties_add_text(obs_properties_t * props,const char * name,const char * desc,enum obs_text_type type)593 obs_property_t *obs_properties_add_text(obs_properties_t *props,
594 const char *name, const char *desc,
595 enum obs_text_type type)
596 {
597 if (!props || has_prop(props, name))
598 return NULL;
599
600 struct obs_property *p = new_prop(props, name, desc, OBS_PROPERTY_TEXT);
601 struct text_data *data = get_property_data(p);
602 data->type = type;
603 return p;
604 }
605
obs_properties_add_path(obs_properties_t * props,const char * name,const char * desc,enum obs_path_type type,const char * filter,const char * default_path)606 obs_property_t *obs_properties_add_path(obs_properties_t *props,
607 const char *name, const char *desc,
608 enum obs_path_type type,
609 const char *filter,
610 const char *default_path)
611 {
612 if (!props || has_prop(props, name))
613 return NULL;
614
615 struct obs_property *p = new_prop(props, name, desc, OBS_PROPERTY_PATH);
616 struct path_data *data = get_property_data(p);
617 data->type = type;
618 data->default_path = bstrdup(default_path);
619
620 if (data->type == OBS_PATH_FILE)
621 data->filter = bstrdup(filter);
622
623 return p;
624 }
625
obs_properties_add_list(obs_properties_t * props,const char * name,const char * desc,enum obs_combo_type type,enum obs_combo_format format)626 obs_property_t *obs_properties_add_list(obs_properties_t *props,
627 const char *name, const char *desc,
628 enum obs_combo_type type,
629 enum obs_combo_format format)
630 {
631 if (!props || has_prop(props, name))
632 return NULL;
633
634 if (type == OBS_COMBO_TYPE_EDITABLE &&
635 format != OBS_COMBO_FORMAT_STRING) {
636 blog(LOG_WARNING,
637 "List '%s', error: Editable combo boxes "
638 "must be of the 'string' type",
639 name);
640 return NULL;
641 }
642
643 struct obs_property *p = new_prop(props, name, desc, OBS_PROPERTY_LIST);
644 struct list_data *data = get_property_data(p);
645 data->format = format;
646 data->type = type;
647
648 return p;
649 }
650
obs_properties_add_color(obs_properties_t * props,const char * name,const char * desc)651 obs_property_t *obs_properties_add_color(obs_properties_t *props,
652 const char *name, const char *desc)
653 {
654 if (!props || has_prop(props, name))
655 return NULL;
656 return new_prop(props, name, desc, OBS_PROPERTY_COLOR);
657 }
658
obs_properties_add_color_alpha(obs_properties_t * props,const char * name,const char * desc)659 obs_property_t *obs_properties_add_color_alpha(obs_properties_t *props,
660 const char *name,
661 const char *desc)
662 {
663 if (!props || has_prop(props, name))
664 return NULL;
665 return new_prop(props, name, desc, OBS_PROPERTY_COLOR_ALPHA);
666 }
667
obs_properties_add_button(obs_properties_t * props,const char * name,const char * text,obs_property_clicked_t callback)668 obs_property_t *obs_properties_add_button(obs_properties_t *props,
669 const char *name, const char *text,
670 obs_property_clicked_t callback)
671 {
672 if (!props || has_prop(props, name))
673 return NULL;
674
675 struct obs_property *p =
676 new_prop(props, name, text, OBS_PROPERTY_BUTTON);
677 struct button_data *data = get_property_data(p);
678 data->callback = callback;
679 return p;
680 }
681
obs_properties_add_button2(obs_properties_t * props,const char * name,const char * text,obs_property_clicked_t callback,void * priv)682 obs_property_t *obs_properties_add_button2(obs_properties_t *props,
683 const char *name, const char *text,
684 obs_property_clicked_t callback,
685 void *priv)
686 {
687 if (!props || has_prop(props, name))
688 return NULL;
689
690 struct obs_property *p =
691 new_prop(props, name, text, OBS_PROPERTY_BUTTON);
692 struct button_data *data = get_property_data(p);
693 data->callback = callback;
694 p->priv = priv;
695 return p;
696 }
697
obs_properties_add_font(obs_properties_t * props,const char * name,const char * desc)698 obs_property_t *obs_properties_add_font(obs_properties_t *props,
699 const char *name, const char *desc)
700 {
701 if (!props || has_prop(props, name))
702 return NULL;
703 return new_prop(props, name, desc, OBS_PROPERTY_FONT);
704 }
705
706 obs_property_t *
obs_properties_add_editable_list(obs_properties_t * props,const char * name,const char * desc,enum obs_editable_list_type type,const char * filter,const char * default_path)707 obs_properties_add_editable_list(obs_properties_t *props, const char *name,
708 const char *desc,
709 enum obs_editable_list_type type,
710 const char *filter, const char *default_path)
711 {
712 if (!props || has_prop(props, name))
713 return NULL;
714 struct obs_property *p =
715 new_prop(props, name, desc, OBS_PROPERTY_EDITABLE_LIST);
716
717 struct editable_list_data *data = get_property_data(p);
718 data->type = type;
719 data->filter = bstrdup(filter);
720 data->default_path = bstrdup(default_path);
721 return p;
722 }
723
obs_properties_add_frame_rate(obs_properties_t * props,const char * name,const char * desc)724 obs_property_t *obs_properties_add_frame_rate(obs_properties_t *props,
725 const char *name,
726 const char *desc)
727 {
728 if (!props || has_prop(props, name))
729 return NULL;
730
731 struct obs_property *p =
732 new_prop(props, name, desc, OBS_PROPERTY_FRAME_RATE);
733
734 struct frame_rate_data *data = get_property_data(p);
735 da_init(data->extra_options);
736 da_init(data->ranges);
737 return p;
738 }
739
check_property_group_recursion(obs_properties_t * parent,obs_properties_t * group)740 static bool check_property_group_recursion(obs_properties_t *parent,
741 obs_properties_t *group)
742 {
743 /* Scan the group for the parent. */
744 obs_property_t *current_property = group->first_property;
745 while (current_property) {
746 if (current_property->type == OBS_PROPERTY_GROUP) {
747 obs_properties_t *cprops =
748 obs_property_group_content(current_property);
749 if (cprops == parent) {
750 /* Contains find_props */
751 return true;
752 } else if (cprops == group) {
753 /* Contains self, shouldn't be possible but
754 * lets verify anyway. */
755 return true;
756 }
757 if (check_property_group_recursion(parent, cprops))
758 return true;
759 }
760
761 current_property = current_property->next;
762 }
763
764 return false;
765 }
766
check_property_group_duplicates(obs_properties_t * parent,obs_properties_t * group)767 static bool check_property_group_duplicates(obs_properties_t *parent,
768 obs_properties_t *group)
769 {
770 obs_property_t *current_property = group->first_property;
771 while (current_property) {
772 if (has_prop(parent, current_property->name)) {
773 return true;
774 }
775
776 current_property = current_property->next;
777 }
778
779 return false;
780 }
781
obs_properties_add_group(obs_properties_t * props,const char * name,const char * desc,enum obs_group_type type,obs_properties_t * group)782 obs_property_t *obs_properties_add_group(obs_properties_t *props,
783 const char *name, const char *desc,
784 enum obs_group_type type,
785 obs_properties_t *group)
786 {
787 if (!props || has_prop(props, name))
788 return NULL;
789 if (!group)
790 return NULL;
791
792 /* Prevent recursion. */
793 if (props == group)
794 return NULL;
795 if (check_property_group_recursion(props, group))
796 return NULL;
797
798 /* Prevent duplicate properties */
799 if (check_property_group_duplicates(props, group))
800 return NULL;
801
802 obs_property_t *p = new_prop(props, name, desc, OBS_PROPERTY_GROUP);
803 group->parent = p;
804
805 struct group_data *data = get_property_data(p);
806 data->type = type;
807 data->content = group;
808 return p;
809 }
810
811 /* ------------------------------------------------------------------------- */
812
is_combo(struct obs_property * p)813 static inline bool is_combo(struct obs_property *p)
814 {
815 return p->type == OBS_PROPERTY_LIST;
816 }
817
get_list_data(struct obs_property * p)818 static inline struct list_data *get_list_data(struct obs_property *p)
819 {
820 if (!p || !is_combo(p))
821 return NULL;
822
823 return get_property_data(p);
824 }
825
get_list_fmt_data(struct obs_property * p,enum obs_combo_format format)826 static inline struct list_data *get_list_fmt_data(struct obs_property *p,
827 enum obs_combo_format format)
828 {
829 struct list_data *data = get_list_data(p);
830 return (data && data->format == format) ? data : NULL;
831 }
832
833 /* ------------------------------------------------------------------------- */
834
obs_property_next(obs_property_t ** p)835 bool obs_property_next(obs_property_t **p)
836 {
837 if (!p || !*p)
838 return false;
839
840 *p = (*p)->next;
841 return *p != NULL;
842 }
843
obs_property_set_modified_callback(obs_property_t * p,obs_property_modified_t modified)844 void obs_property_set_modified_callback(obs_property_t *p,
845 obs_property_modified_t modified)
846 {
847 if (p)
848 p->modified = modified;
849 }
850
obs_property_set_modified_callback2(obs_property_t * p,obs_property_modified2_t modified2,void * priv)851 void obs_property_set_modified_callback2(obs_property_t *p,
852 obs_property_modified2_t modified2,
853 void *priv)
854 {
855 if (p) {
856 p->modified2 = modified2;
857 p->priv = priv;
858 }
859 }
860
obs_property_modified(obs_property_t * p,obs_data_t * settings)861 bool obs_property_modified(obs_property_t *p, obs_data_t *settings)
862 {
863 if (p) {
864 if (p->modified) {
865 obs_properties_t *top = get_topmost_parent(p->parent);
866 return p->modified(top, p, settings);
867 } else if (p->modified2) {
868 obs_properties_t *top = get_topmost_parent(p->parent);
869 return p->modified2(p->priv, top, p, settings);
870 }
871 }
872 return false;
873 }
874
obs_property_button_clicked(obs_property_t * p,void * obj)875 bool obs_property_button_clicked(obs_property_t *p, void *obj)
876 {
877 struct obs_context_data *context = obj;
878 if (p) {
879 struct button_data *data =
880 get_type_data(p, OBS_PROPERTY_BUTTON);
881 if (data && data->callback) {
882 obs_properties_t *top = get_topmost_parent(p->parent);
883 if (p->priv)
884 return data->callback(top, p, p->priv);
885 return data->callback(top, p,
886 (context ? context->data : NULL));
887 }
888 }
889
890 return false;
891 }
892
obs_property_set_visible(obs_property_t * p,bool visible)893 void obs_property_set_visible(obs_property_t *p, bool visible)
894 {
895 if (p)
896 p->visible = visible;
897 }
898
obs_property_set_enabled(obs_property_t * p,bool enabled)899 void obs_property_set_enabled(obs_property_t *p, bool enabled)
900 {
901 if (p)
902 p->enabled = enabled;
903 }
904
obs_property_set_description(obs_property_t * p,const char * description)905 void obs_property_set_description(obs_property_t *p, const char *description)
906 {
907 if (p) {
908 bfree(p->desc);
909 p->desc = description && *description ? bstrdup(description)
910 : NULL;
911 }
912 }
913
obs_property_set_long_description(obs_property_t * p,const char * long_desc)914 void obs_property_set_long_description(obs_property_t *p, const char *long_desc)
915 {
916 if (p) {
917 bfree(p->long_desc);
918 p->long_desc = long_desc && *long_desc ? bstrdup(long_desc)
919 : NULL;
920 }
921 }
922
obs_property_name(obs_property_t * p)923 const char *obs_property_name(obs_property_t *p)
924 {
925 return p ? p->name : NULL;
926 }
927
obs_property_description(obs_property_t * p)928 const char *obs_property_description(obs_property_t *p)
929 {
930 return p ? p->desc : NULL;
931 }
932
obs_property_long_description(obs_property_t * p)933 const char *obs_property_long_description(obs_property_t *p)
934 {
935 return p ? p->long_desc : NULL;
936 }
937
obs_property_get_type(obs_property_t * p)938 enum obs_property_type obs_property_get_type(obs_property_t *p)
939 {
940 return p ? p->type : OBS_PROPERTY_INVALID;
941 }
942
obs_property_enabled(obs_property_t * p)943 bool obs_property_enabled(obs_property_t *p)
944 {
945 return p ? p->enabled : false;
946 }
947
obs_property_visible(obs_property_t * p)948 bool obs_property_visible(obs_property_t *p)
949 {
950 return p ? p->visible : false;
951 }
952
obs_property_int_min(obs_property_t * p)953 int obs_property_int_min(obs_property_t *p)
954 {
955 struct int_data *data = get_type_data(p, OBS_PROPERTY_INT);
956 return data ? data->min : 0;
957 }
958
obs_property_int_max(obs_property_t * p)959 int obs_property_int_max(obs_property_t *p)
960 {
961 struct int_data *data = get_type_data(p, OBS_PROPERTY_INT);
962 return data ? data->max : 0;
963 }
964
obs_property_int_step(obs_property_t * p)965 int obs_property_int_step(obs_property_t *p)
966 {
967 struct int_data *data = get_type_data(p, OBS_PROPERTY_INT);
968 return data ? data->step : 0;
969 }
970
obs_property_int_type(obs_property_t * p)971 enum obs_number_type obs_property_int_type(obs_property_t *p)
972 {
973 struct int_data *data = get_type_data(p, OBS_PROPERTY_INT);
974 return data ? data->type : OBS_NUMBER_SCROLLER;
975 }
976
obs_property_int_suffix(obs_property_t * p)977 const char *obs_property_int_suffix(obs_property_t *p)
978 {
979 struct int_data *data = get_type_data(p, OBS_PROPERTY_INT);
980 return data ? data->suffix : NULL;
981 }
982
obs_property_float_min(obs_property_t * p)983 double obs_property_float_min(obs_property_t *p)
984 {
985 struct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);
986 return data ? data->min : 0;
987 }
988
obs_property_float_max(obs_property_t * p)989 double obs_property_float_max(obs_property_t *p)
990 {
991 struct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);
992 return data ? data->max : 0;
993 }
994
obs_property_float_step(obs_property_t * p)995 double obs_property_float_step(obs_property_t *p)
996 {
997 struct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);
998 return data ? data->step : 0;
999 }
1000
obs_property_float_suffix(obs_property_t * p)1001 const char *obs_property_float_suffix(obs_property_t *p)
1002 {
1003 struct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);
1004 return data ? data->suffix : NULL;
1005 }
1006
obs_property_float_type(obs_property_t * p)1007 enum obs_number_type obs_property_float_type(obs_property_t *p)
1008 {
1009 struct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);
1010 return data ? data->type : OBS_NUMBER_SCROLLER;
1011 }
1012
obs_property_text_type(obs_property_t * p)1013 enum obs_text_type obs_property_text_type(obs_property_t *p)
1014 {
1015 struct text_data *data = get_type_data(p, OBS_PROPERTY_TEXT);
1016 return data ? data->type : OBS_TEXT_DEFAULT;
1017 }
1018
obs_property_text_monospace(obs_property_t * p)1019 enum obs_text_type obs_property_text_monospace(obs_property_t *p)
1020 {
1021 struct text_data *data = get_type_data(p, OBS_PROPERTY_TEXT);
1022 return data ? data->monospace : false;
1023 }
1024
obs_property_path_type(obs_property_t * p)1025 enum obs_path_type obs_property_path_type(obs_property_t *p)
1026 {
1027 struct path_data *data = get_type_data(p, OBS_PROPERTY_PATH);
1028 return data ? data->type : OBS_PATH_DIRECTORY;
1029 }
1030
obs_property_path_filter(obs_property_t * p)1031 const char *obs_property_path_filter(obs_property_t *p)
1032 {
1033 struct path_data *data = get_type_data(p, OBS_PROPERTY_PATH);
1034 return data ? data->filter : NULL;
1035 }
1036
obs_property_path_default_path(obs_property_t * p)1037 const char *obs_property_path_default_path(obs_property_t *p)
1038 {
1039 struct path_data *data = get_type_data(p, OBS_PROPERTY_PATH);
1040 return data ? data->default_path : NULL;
1041 }
1042
obs_property_list_type(obs_property_t * p)1043 enum obs_combo_type obs_property_list_type(obs_property_t *p)
1044 {
1045 struct list_data *data = get_list_data(p);
1046 return data ? data->type : OBS_COMBO_TYPE_INVALID;
1047 }
1048
obs_property_list_format(obs_property_t * p)1049 enum obs_combo_format obs_property_list_format(obs_property_t *p)
1050 {
1051 struct list_data *data = get_list_data(p);
1052 return data ? data->format : OBS_COMBO_FORMAT_INVALID;
1053 }
1054
obs_property_int_set_limits(obs_property_t * p,int min,int max,int step)1055 void obs_property_int_set_limits(obs_property_t *p, int min, int max, int step)
1056 {
1057 struct int_data *data = get_type_data(p, OBS_PROPERTY_INT);
1058 if (!data)
1059 return;
1060
1061 data->min = min;
1062 data->max = max;
1063 data->step = step;
1064 }
1065
obs_property_float_set_limits(obs_property_t * p,double min,double max,double step)1066 void obs_property_float_set_limits(obs_property_t *p, double min, double max,
1067 double step)
1068 {
1069 struct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);
1070 if (!data)
1071 return;
1072
1073 data->min = min;
1074 data->max = max;
1075 data->step = step;
1076 }
1077
obs_property_int_set_suffix(obs_property_t * p,const char * suffix)1078 void obs_property_int_set_suffix(obs_property_t *p, const char *suffix)
1079 {
1080 struct int_data *data = get_type_data(p, OBS_PROPERTY_INT);
1081 if (!data)
1082 return;
1083
1084 bfree(data->suffix);
1085 data->suffix = bstrdup(suffix);
1086 }
1087
obs_property_float_set_suffix(obs_property_t * p,const char * suffix)1088 void obs_property_float_set_suffix(obs_property_t *p, const char *suffix)
1089 {
1090 struct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);
1091 if (!data)
1092 return;
1093
1094 bfree(data->suffix);
1095 data->suffix = bstrdup(suffix);
1096 }
1097
obs_property_text_set_monospace(obs_property_t * p,bool monospace)1098 void obs_property_text_set_monospace(obs_property_t *p, bool monospace)
1099 {
1100 struct text_data *data = get_type_data(p, OBS_PROPERTY_TEXT);
1101 if (!data)
1102 return;
1103
1104 data->monospace = monospace;
1105 }
1106
obs_property_button_set_type(obs_property_t * p,enum obs_button_type type)1107 void obs_property_button_set_type(obs_property_t *p, enum obs_button_type type)
1108 {
1109 struct button_data *data = get_type_data(p, OBS_PROPERTY_BUTTON);
1110 if (!data)
1111 return;
1112
1113 data->type = type;
1114 }
1115
obs_property_button_set_url(obs_property_t * p,char * url)1116 void obs_property_button_set_url(obs_property_t *p, char *url)
1117 {
1118 struct button_data *data = get_type_data(p, OBS_PROPERTY_BUTTON);
1119 if (!data)
1120 return;
1121
1122 data->url = url;
1123 }
1124
obs_property_list_clear(obs_property_t * p)1125 void obs_property_list_clear(obs_property_t *p)
1126 {
1127 struct list_data *data = get_list_data(p);
1128 if (data)
1129 list_data_free(data);
1130 }
1131
add_item(struct list_data * data,const char * name,const void * val)1132 static size_t add_item(struct list_data *data, const char *name,
1133 const void *val)
1134 {
1135 struct list_item item = {NULL};
1136 item.name = bstrdup(name);
1137
1138 if (data->format == OBS_COMBO_FORMAT_INT)
1139 item.ll = *(const long long *)val;
1140 else if (data->format == OBS_COMBO_FORMAT_FLOAT)
1141 item.d = *(const double *)val;
1142 else
1143 item.str = bstrdup(val);
1144
1145 return da_push_back(data->items, &item);
1146 }
1147
insert_item(struct list_data * data,size_t idx,const char * name,const void * val)1148 static void insert_item(struct list_data *data, size_t idx, const char *name,
1149 const void *val)
1150 {
1151 struct list_item item = {NULL};
1152 item.name = bstrdup(name);
1153
1154 if (data->format == OBS_COMBO_FORMAT_INT)
1155 item.ll = *(const long long *)val;
1156 else if (data->format == OBS_COMBO_FORMAT_FLOAT)
1157 item.d = *(const double *)val;
1158 else
1159 item.str = bstrdup(val);
1160
1161 da_insert(data->items, idx, &item);
1162 }
1163
obs_property_list_add_string(obs_property_t * p,const char * name,const char * val)1164 size_t obs_property_list_add_string(obs_property_t *p, const char *name,
1165 const char *val)
1166 {
1167 struct list_data *data = get_list_data(p);
1168 if (data && data->format == OBS_COMBO_FORMAT_STRING)
1169 return add_item(data, name, val);
1170 return 0;
1171 }
1172
obs_property_list_add_int(obs_property_t * p,const char * name,long long val)1173 size_t obs_property_list_add_int(obs_property_t *p, const char *name,
1174 long long val)
1175 {
1176 struct list_data *data = get_list_data(p);
1177 if (data && data->format == OBS_COMBO_FORMAT_INT)
1178 return add_item(data, name, &val);
1179 return 0;
1180 }
1181
obs_property_list_add_float(obs_property_t * p,const char * name,double val)1182 size_t obs_property_list_add_float(obs_property_t *p, const char *name,
1183 double val)
1184 {
1185 struct list_data *data = get_list_data(p);
1186 if (data && data->format == OBS_COMBO_FORMAT_FLOAT)
1187 return add_item(data, name, &val);
1188 return 0;
1189 }
1190
obs_property_list_insert_string(obs_property_t * p,size_t idx,const char * name,const char * val)1191 void obs_property_list_insert_string(obs_property_t *p, size_t idx,
1192 const char *name, const char *val)
1193 {
1194 struct list_data *data = get_list_data(p);
1195 if (data && data->format == OBS_COMBO_FORMAT_STRING)
1196 insert_item(data, idx, name, val);
1197 }
1198
obs_property_list_insert_int(obs_property_t * p,size_t idx,const char * name,long long val)1199 void obs_property_list_insert_int(obs_property_t *p, size_t idx,
1200 const char *name, long long val)
1201 {
1202 struct list_data *data = get_list_data(p);
1203 if (data && data->format == OBS_COMBO_FORMAT_INT)
1204 insert_item(data, idx, name, &val);
1205 }
1206
obs_property_list_insert_float(obs_property_t * p,size_t idx,const char * name,double val)1207 void obs_property_list_insert_float(obs_property_t *p, size_t idx,
1208 const char *name, double val)
1209 {
1210 struct list_data *data = get_list_data(p);
1211 if (data && data->format == OBS_COMBO_FORMAT_FLOAT)
1212 insert_item(data, idx, name, &val);
1213 }
1214
obs_property_list_item_remove(obs_property_t * p,size_t idx)1215 void obs_property_list_item_remove(obs_property_t *p, size_t idx)
1216 {
1217 struct list_data *data = get_list_data(p);
1218 if (data && idx < data->items.num) {
1219 list_item_free(data, data->items.array + idx);
1220 da_erase(data->items, idx);
1221 }
1222 }
1223
obs_property_list_item_count(obs_property_t * p)1224 size_t obs_property_list_item_count(obs_property_t *p)
1225 {
1226 struct list_data *data = get_list_data(p);
1227 return data ? data->items.num : 0;
1228 }
1229
obs_property_list_item_disabled(obs_property_t * p,size_t idx)1230 bool obs_property_list_item_disabled(obs_property_t *p, size_t idx)
1231 {
1232 struct list_data *data = get_list_data(p);
1233 return (data && idx < data->items.num) ? data->items.array[idx].disabled
1234 : false;
1235 }
1236
obs_property_list_item_disable(obs_property_t * p,size_t idx,bool disabled)1237 void obs_property_list_item_disable(obs_property_t *p, size_t idx,
1238 bool disabled)
1239 {
1240 struct list_data *data = get_list_data(p);
1241 if (!data || idx >= data->items.num)
1242 return;
1243 data->items.array[idx].disabled = disabled;
1244 }
1245
obs_property_list_item_name(obs_property_t * p,size_t idx)1246 const char *obs_property_list_item_name(obs_property_t *p, size_t idx)
1247 {
1248 struct list_data *data = get_list_data(p);
1249 return (data && idx < data->items.num) ? data->items.array[idx].name
1250 : NULL;
1251 }
1252
obs_property_list_item_string(obs_property_t * p,size_t idx)1253 const char *obs_property_list_item_string(obs_property_t *p, size_t idx)
1254 {
1255 struct list_data *data = get_list_fmt_data(p, OBS_COMBO_FORMAT_STRING);
1256 return (data && idx < data->items.num) ? data->items.array[idx].str
1257 : NULL;
1258 }
1259
obs_property_list_item_int(obs_property_t * p,size_t idx)1260 long long obs_property_list_item_int(obs_property_t *p, size_t idx)
1261 {
1262 struct list_data *data = get_list_fmt_data(p, OBS_COMBO_FORMAT_INT);
1263 return (data && idx < data->items.num) ? data->items.array[idx].ll : 0;
1264 }
1265
obs_property_list_item_float(obs_property_t * p,size_t idx)1266 double obs_property_list_item_float(obs_property_t *p, size_t idx)
1267 {
1268 struct list_data *data = get_list_fmt_data(p, OBS_COMBO_FORMAT_FLOAT);
1269 return (data && idx < data->items.num) ? data->items.array[idx].d : 0.0;
1270 }
1271
obs_property_editable_list_type(obs_property_t * p)1272 enum obs_editable_list_type obs_property_editable_list_type(obs_property_t *p)
1273 {
1274 struct editable_list_data *data =
1275 get_type_data(p, OBS_PROPERTY_EDITABLE_LIST);
1276 return data ? data->type : OBS_EDITABLE_LIST_TYPE_STRINGS;
1277 }
1278
obs_property_editable_list_filter(obs_property_t * p)1279 const char *obs_property_editable_list_filter(obs_property_t *p)
1280 {
1281 struct editable_list_data *data =
1282 get_type_data(p, OBS_PROPERTY_EDITABLE_LIST);
1283 return data ? data->filter : NULL;
1284 }
1285
obs_property_editable_list_default_path(obs_property_t * p)1286 const char *obs_property_editable_list_default_path(obs_property_t *p)
1287 {
1288 struct editable_list_data *data =
1289 get_type_data(p, OBS_PROPERTY_EDITABLE_LIST);
1290 return data ? data->default_path : NULL;
1291 }
1292
1293 /* ------------------------------------------------------------------------- */
1294 /* OBS_PROPERTY_FRAME_RATE */
1295
obs_property_frame_rate_clear(obs_property_t * p)1296 void obs_property_frame_rate_clear(obs_property_t *p)
1297 {
1298 struct frame_rate_data *data =
1299 get_type_data(p, OBS_PROPERTY_FRAME_RATE);
1300 if (!data)
1301 return;
1302
1303 frame_rate_data_options_free(data);
1304 frame_rate_data_ranges_free(data);
1305 }
1306
obs_property_frame_rate_options_clear(obs_property_t * p)1307 void obs_property_frame_rate_options_clear(obs_property_t *p)
1308 {
1309 struct frame_rate_data *data =
1310 get_type_data(p, OBS_PROPERTY_FRAME_RATE);
1311 if (!data)
1312 return;
1313
1314 frame_rate_data_options_free(data);
1315 }
1316
obs_property_frame_rate_fps_ranges_clear(obs_property_t * p)1317 void obs_property_frame_rate_fps_ranges_clear(obs_property_t *p)
1318 {
1319 struct frame_rate_data *data =
1320 get_type_data(p, OBS_PROPERTY_FRAME_RATE);
1321 if (!data)
1322 return;
1323
1324 frame_rate_data_ranges_free(data);
1325 }
1326
obs_property_frame_rate_option_add(obs_property_t * p,const char * name,const char * description)1327 size_t obs_property_frame_rate_option_add(obs_property_t *p, const char *name,
1328 const char *description)
1329 {
1330 struct frame_rate_data *data =
1331 get_type_data(p, OBS_PROPERTY_FRAME_RATE);
1332 if (!data)
1333 return DARRAY_INVALID;
1334
1335 struct frame_rate_option *opt = da_push_back_new(data->extra_options);
1336
1337 opt->name = bstrdup(name);
1338 opt->description = bstrdup(description);
1339
1340 return data->extra_options.num - 1;
1341 }
1342
obs_property_frame_rate_fps_range_add(obs_property_t * p,struct media_frames_per_second min,struct media_frames_per_second max)1343 size_t obs_property_frame_rate_fps_range_add(obs_property_t *p,
1344 struct media_frames_per_second min,
1345 struct media_frames_per_second max)
1346 {
1347 struct frame_rate_data *data =
1348 get_type_data(p, OBS_PROPERTY_FRAME_RATE);
1349 if (!data)
1350 return DARRAY_INVALID;
1351
1352 struct frame_rate_range *rng = da_push_back_new(data->ranges);
1353
1354 rng->min_time = min;
1355 rng->max_time = max;
1356
1357 return data->ranges.num - 1;
1358 }
1359
obs_property_frame_rate_option_insert(obs_property_t * p,size_t idx,const char * name,const char * description)1360 void obs_property_frame_rate_option_insert(obs_property_t *p, size_t idx,
1361 const char *name,
1362 const char *description)
1363 {
1364 struct frame_rate_data *data =
1365 get_type_data(p, OBS_PROPERTY_FRAME_RATE);
1366 if (!data)
1367 return;
1368
1369 struct frame_rate_option *opt = da_insert_new(data->extra_options, idx);
1370
1371 opt->name = bstrdup(name);
1372 opt->description = bstrdup(description);
1373 }
1374
obs_property_frame_rate_fps_range_insert(obs_property_t * p,size_t idx,struct media_frames_per_second min,struct media_frames_per_second max)1375 void obs_property_frame_rate_fps_range_insert(
1376 obs_property_t *p, size_t idx, struct media_frames_per_second min,
1377 struct media_frames_per_second max)
1378 {
1379 struct frame_rate_data *data =
1380 get_type_data(p, OBS_PROPERTY_FRAME_RATE);
1381 if (!data)
1382 return;
1383
1384 struct frame_rate_range *rng = da_insert_new(data->ranges, idx);
1385
1386 rng->min_time = min;
1387 rng->max_time = max;
1388 }
1389
obs_property_frame_rate_options_count(obs_property_t * p)1390 size_t obs_property_frame_rate_options_count(obs_property_t *p)
1391 {
1392 struct frame_rate_data *data =
1393 get_type_data(p, OBS_PROPERTY_FRAME_RATE);
1394 return data ? data->extra_options.num : 0;
1395 }
1396
obs_property_frame_rate_option_name(obs_property_t * p,size_t idx)1397 const char *obs_property_frame_rate_option_name(obs_property_t *p, size_t idx)
1398 {
1399 struct frame_rate_data *data =
1400 get_type_data(p, OBS_PROPERTY_FRAME_RATE);
1401 return data && data->extra_options.num > idx
1402 ? data->extra_options.array[idx].name
1403 : NULL;
1404 }
1405
obs_property_frame_rate_option_description(obs_property_t * p,size_t idx)1406 const char *obs_property_frame_rate_option_description(obs_property_t *p,
1407 size_t idx)
1408 {
1409 struct frame_rate_data *data =
1410 get_type_data(p, OBS_PROPERTY_FRAME_RATE);
1411 return data && data->extra_options.num > idx
1412 ? data->extra_options.array[idx].description
1413 : NULL;
1414 }
1415
obs_property_frame_rate_fps_ranges_count(obs_property_t * p)1416 size_t obs_property_frame_rate_fps_ranges_count(obs_property_t *p)
1417 {
1418 struct frame_rate_data *data =
1419 get_type_data(p, OBS_PROPERTY_FRAME_RATE);
1420 return data ? data->ranges.num : 0;
1421 }
1422
1423 struct media_frames_per_second
obs_property_frame_rate_fps_range_min(obs_property_t * p,size_t idx)1424 obs_property_frame_rate_fps_range_min(obs_property_t *p, size_t idx)
1425 {
1426 struct frame_rate_data *data =
1427 get_type_data(p, OBS_PROPERTY_FRAME_RATE);
1428 return data && data->ranges.num > idx
1429 ? data->ranges.array[idx].min_time
1430 : (struct media_frames_per_second){0};
1431 }
1432 struct media_frames_per_second
obs_property_frame_rate_fps_range_max(obs_property_t * p,size_t idx)1433 obs_property_frame_rate_fps_range_max(obs_property_t *p, size_t idx)
1434 {
1435 struct frame_rate_data *data =
1436 get_type_data(p, OBS_PROPERTY_FRAME_RATE);
1437 return data && data->ranges.num > idx
1438 ? data->ranges.array[idx].max_time
1439 : (struct media_frames_per_second){0};
1440 }
1441
obs_proprety_text_type(obs_property_t * p)1442 enum obs_text_type obs_proprety_text_type(obs_property_t *p)
1443 {
1444 return obs_property_text_type(p);
1445 }
1446
obs_property_group_type(obs_property_t * p)1447 enum obs_group_type obs_property_group_type(obs_property_t *p)
1448 {
1449 struct group_data *data = get_type_data(p, OBS_PROPERTY_GROUP);
1450 return data ? data->type : OBS_COMBO_INVALID;
1451 }
1452
obs_property_group_content(obs_property_t * p)1453 obs_properties_t *obs_property_group_content(obs_property_t *p)
1454 {
1455 struct group_data *data = get_type_data(p, OBS_PROPERTY_GROUP);
1456 return data ? data->content : NULL;
1457 }
1458
obs_property_button_type(obs_property_t * p)1459 enum obs_button_type obs_property_button_type(obs_property_t *p)
1460 {
1461 struct button_data *data = get_type_data(p, OBS_PROPERTY_BUTTON);
1462 return data ? data->type : OBS_BUTTON_DEFAULT;
1463 }
1464
obs_property_button_url(obs_property_t * p)1465 const char *obs_property_button_url(obs_property_t *p)
1466 {
1467 struct button_data *data = get_type_data(p, OBS_PROPERTY_BUTTON);
1468 return data ? data->url : "";
1469 }
1470