obs-properties.c 36 KB

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