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