obs-properties.c 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524
  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. typedef DARRAY(struct obs_property *) obs_property_da_t;
  291. void obs_properties_apply_settings_internal(
  292. obs_properties_t *props, obs_property_da_t *properties_with_callback)
  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),
  299. properties_with_callback);
  300. }
  301. if (p->modified || p->modified2)
  302. da_push_back((*properties_with_callback), &p);
  303. p = p->hh.next;
  304. }
  305. }
  306. void obs_properties_apply_settings(obs_properties_t *props,
  307. obs_data_t *settings)
  308. {
  309. if (!props)
  310. return;
  311. obs_property_da_t properties_with_callback;
  312. da_init(properties_with_callback);
  313. obs_properties_apply_settings_internal(props,
  314. &properties_with_callback);
  315. while (properties_with_callback.num > 0) {
  316. struct obs_property *p = *(struct obs_property **)da_end(
  317. properties_with_callback);
  318. if (p->modified)
  319. p->modified(props, p, settings);
  320. else if (p->modified2)
  321. p->modified2(p->priv, props, p, settings);
  322. da_pop_back(properties_with_callback);
  323. }
  324. da_free(properties_with_callback);
  325. }
  326. /* ------------------------------------------------------------------------- */
  327. static inline size_t get_property_size(enum obs_property_type type)
  328. {
  329. switch (type) {
  330. case OBS_PROPERTY_INVALID:
  331. return 0;
  332. case OBS_PROPERTY_BOOL:
  333. return 0;
  334. case OBS_PROPERTY_INT:
  335. return sizeof(struct int_data);
  336. case OBS_PROPERTY_FLOAT:
  337. return sizeof(struct float_data);
  338. case OBS_PROPERTY_TEXT:
  339. return sizeof(struct text_data);
  340. case OBS_PROPERTY_PATH:
  341. return sizeof(struct path_data);
  342. case OBS_PROPERTY_LIST:
  343. return sizeof(struct list_data);
  344. case OBS_PROPERTY_COLOR:
  345. return 0;
  346. case OBS_PROPERTY_BUTTON:
  347. return sizeof(struct button_data);
  348. case OBS_PROPERTY_FONT:
  349. return 0;
  350. case OBS_PROPERTY_EDITABLE_LIST:
  351. return sizeof(struct editable_list_data);
  352. case OBS_PROPERTY_FRAME_RATE:
  353. return sizeof(struct frame_rate_data);
  354. case OBS_PROPERTY_GROUP:
  355. return sizeof(struct group_data);
  356. case OBS_PROPERTY_COLOR_ALPHA:
  357. return 0;
  358. }
  359. return 0;
  360. }
  361. static inline struct obs_property *new_prop(struct obs_properties *props,
  362. const char *name, const char *desc,
  363. enum obs_property_type type)
  364. {
  365. size_t data_size = get_property_size(type);
  366. struct obs_property *p;
  367. p = bzalloc(sizeof(struct obs_property) + data_size);
  368. p->parent = props;
  369. p->enabled = true;
  370. p->visible = true;
  371. p->type = type;
  372. p->name = bstrdup(name);
  373. p->desc = bstrdup(desc);
  374. HASH_ADD_STR(props->properties, name, p);
  375. return p;
  376. }
  377. static inline obs_properties_t *get_topmost_parent(obs_properties_t *props)
  378. {
  379. obs_properties_t *parent = props;
  380. obs_properties_t *last_parent = parent;
  381. while (parent) {
  382. last_parent = parent;
  383. parent = obs_properties_get_parent(parent);
  384. }
  385. return last_parent;
  386. }
  387. static inline bool contains_prop(struct obs_properties *props, const char *name)
  388. {
  389. struct obs_property *p, *tmp;
  390. HASH_FIND_STR(props->properties, name, p);
  391. if (p) {
  392. blog(LOG_WARNING, "Property '%s' exists", name);
  393. return true;
  394. }
  395. if (!props->groups)
  396. return false;
  397. HASH_ITER (hh, props->properties, p, tmp) {
  398. if (p->type != OBS_PROPERTY_GROUP)
  399. continue;
  400. if (contains_prop(obs_property_group_content(p), name))
  401. return true;
  402. }
  403. return false;
  404. }
  405. static inline bool has_prop(struct obs_properties *props, const char *name)
  406. {
  407. return contains_prop(get_topmost_parent(props), name);
  408. }
  409. static inline void *get_property_data(struct obs_property *prop)
  410. {
  411. return (uint8_t *)prop + sizeof(struct obs_property);
  412. }
  413. static inline void *get_type_data(struct obs_property *prop,
  414. enum obs_property_type type)
  415. {
  416. if (!prop || prop->type != type)
  417. return NULL;
  418. return get_property_data(prop);
  419. }
  420. obs_property_t *obs_properties_add_bool(obs_properties_t *props,
  421. const char *name, const char *desc)
  422. {
  423. if (!props || has_prop(props, name))
  424. return NULL;
  425. return new_prop(props, name, desc, OBS_PROPERTY_BOOL);
  426. }
  427. static obs_property_t *add_int(obs_properties_t *props, const char *name,
  428. const char *desc, int min, int max, int step,
  429. enum obs_number_type type)
  430. {
  431. if (!props || has_prop(props, name))
  432. return NULL;
  433. struct obs_property *p = new_prop(props, name, desc, OBS_PROPERTY_INT);
  434. struct int_data *data = get_property_data(p);
  435. data->min = min;
  436. data->max = max;
  437. data->step = step;
  438. data->type = type;
  439. return p;
  440. }
  441. static obs_property_t *add_flt(obs_properties_t *props, const char *name,
  442. const char *desc, double min, double max,
  443. double step, enum obs_number_type type)
  444. {
  445. if (!props || has_prop(props, name))
  446. return NULL;
  447. struct obs_property *p =
  448. new_prop(props, name, desc, OBS_PROPERTY_FLOAT);
  449. struct float_data *data = get_property_data(p);
  450. data->min = min;
  451. data->max = max;
  452. data->step = step;
  453. data->type = type;
  454. return p;
  455. }
  456. obs_property_t *obs_properties_add_int(obs_properties_t *props,
  457. const char *name, const char *desc,
  458. int min, int max, int step)
  459. {
  460. return add_int(props, name, desc, min, max, step, OBS_NUMBER_SCROLLER);
  461. }
  462. obs_property_t *obs_properties_add_float(obs_properties_t *props,
  463. const char *name, const char *desc,
  464. double min, double max, double step)
  465. {
  466. return add_flt(props, name, desc, min, max, step, OBS_NUMBER_SCROLLER);
  467. }
  468. obs_property_t *obs_properties_add_int_slider(obs_properties_t *props,
  469. const char *name,
  470. const char *desc, int min,
  471. int max, int step)
  472. {
  473. return add_int(props, name, desc, min, max, step, OBS_NUMBER_SLIDER);
  474. }
  475. obs_property_t *obs_properties_add_float_slider(obs_properties_t *props,
  476. const char *name,
  477. const char *desc, double min,
  478. double max, double step)
  479. {
  480. return add_flt(props, name, desc, min, max, step, OBS_NUMBER_SLIDER);
  481. }
  482. obs_property_t *obs_properties_add_text(obs_properties_t *props,
  483. const char *name, const char *desc,
  484. enum obs_text_type type)
  485. {
  486. if (!props || has_prop(props, name))
  487. return NULL;
  488. struct obs_property *p = new_prop(props, name, desc, OBS_PROPERTY_TEXT);
  489. struct text_data *data = get_property_data(p);
  490. data->type = type;
  491. data->info_type = OBS_TEXT_INFO_NORMAL;
  492. data->info_word_wrap = true;
  493. return p;
  494. }
  495. obs_property_t *obs_properties_add_path(obs_properties_t *props,
  496. const char *name, const char *desc,
  497. enum obs_path_type type,
  498. const char *filter,
  499. const char *default_path)
  500. {
  501. if (!props || has_prop(props, name))
  502. return NULL;
  503. struct obs_property *p = new_prop(props, name, desc, OBS_PROPERTY_PATH);
  504. struct path_data *data = get_property_data(p);
  505. data->type = type;
  506. data->default_path = bstrdup(default_path);
  507. if (data->type == OBS_PATH_FILE)
  508. data->filter = bstrdup(filter);
  509. return p;
  510. }
  511. obs_property_t *obs_properties_add_list(obs_properties_t *props,
  512. const char *name, const char *desc,
  513. enum obs_combo_type type,
  514. enum obs_combo_format format)
  515. {
  516. if (!props || has_prop(props, name))
  517. return NULL;
  518. if (type == OBS_COMBO_TYPE_EDITABLE &&
  519. format != OBS_COMBO_FORMAT_STRING) {
  520. blog(LOG_WARNING,
  521. "List '%s', error: Editable combo boxes "
  522. "must be of the 'string' type",
  523. name);
  524. return NULL;
  525. }
  526. struct obs_property *p = new_prop(props, name, desc, OBS_PROPERTY_LIST);
  527. struct list_data *data = get_property_data(p);
  528. data->format = format;
  529. data->type = type;
  530. return p;
  531. }
  532. obs_property_t *obs_properties_add_color(obs_properties_t *props,
  533. const char *name, const char *desc)
  534. {
  535. if (!props || has_prop(props, name))
  536. return NULL;
  537. return new_prop(props, name, desc, OBS_PROPERTY_COLOR);
  538. }
  539. obs_property_t *obs_properties_add_color_alpha(obs_properties_t *props,
  540. const char *name,
  541. const char *desc)
  542. {
  543. if (!props || has_prop(props, name))
  544. return NULL;
  545. return new_prop(props, name, desc, OBS_PROPERTY_COLOR_ALPHA);
  546. }
  547. obs_property_t *obs_properties_add_button(obs_properties_t *props,
  548. const char *name, const char *text,
  549. obs_property_clicked_t callback)
  550. {
  551. if (!props || has_prop(props, name))
  552. return NULL;
  553. struct obs_property *p =
  554. new_prop(props, name, text, OBS_PROPERTY_BUTTON);
  555. struct button_data *data = get_property_data(p);
  556. data->callback = callback;
  557. return p;
  558. }
  559. obs_property_t *obs_properties_add_button2(obs_properties_t *props,
  560. const char *name, const char *text,
  561. obs_property_clicked_t callback,
  562. void *priv)
  563. {
  564. if (!props || has_prop(props, name))
  565. return NULL;
  566. struct obs_property *p =
  567. new_prop(props, name, text, OBS_PROPERTY_BUTTON);
  568. struct button_data *data = get_property_data(p);
  569. data->callback = callback;
  570. p->priv = priv;
  571. return p;
  572. }
  573. obs_property_t *obs_properties_add_font(obs_properties_t *props,
  574. const char *name, const char *desc)
  575. {
  576. if (!props || has_prop(props, name))
  577. return NULL;
  578. return new_prop(props, name, desc, OBS_PROPERTY_FONT);
  579. }
  580. obs_property_t *
  581. obs_properties_add_editable_list(obs_properties_t *props, const char *name,
  582. const char *desc,
  583. enum obs_editable_list_type type,
  584. const char *filter, const char *default_path)
  585. {
  586. if (!props || has_prop(props, name))
  587. return NULL;
  588. struct obs_property *p =
  589. new_prop(props, name, desc, OBS_PROPERTY_EDITABLE_LIST);
  590. struct editable_list_data *data = get_property_data(p);
  591. data->type = type;
  592. data->filter = bstrdup(filter);
  593. data->default_path = bstrdup(default_path);
  594. return p;
  595. }
  596. obs_property_t *obs_properties_add_frame_rate(obs_properties_t *props,
  597. const char *name,
  598. const char *desc)
  599. {
  600. if (!props || has_prop(props, name))
  601. return NULL;
  602. struct obs_property *p =
  603. new_prop(props, name, desc, OBS_PROPERTY_FRAME_RATE);
  604. struct frame_rate_data *data = get_property_data(p);
  605. da_init(data->extra_options);
  606. da_init(data->ranges);
  607. return p;
  608. }
  609. static bool check_property_group_recursion(obs_properties_t *parent,
  610. obs_properties_t *group)
  611. {
  612. /* Scan the group for the parent. */
  613. obs_property_t *p, *tmp;
  614. HASH_ITER (hh, group->properties, p, tmp) {
  615. if (p->type != OBS_PROPERTY_GROUP)
  616. continue;
  617. obs_properties_t *cprops = obs_property_group_content(p);
  618. if (cprops == parent) {
  619. /* Contains find_props */
  620. return true;
  621. } else if (cprops == group) {
  622. /* Contains self, shouldn't be possible but
  623. * lets verify anyway. */
  624. return true;
  625. }
  626. if (check_property_group_recursion(parent, cprops))
  627. return true;
  628. }
  629. return false;
  630. }
  631. static bool check_property_group_duplicates(obs_properties_t *parent,
  632. obs_properties_t *group)
  633. {
  634. obs_property_t *p, *tmp;
  635. HASH_ITER (hh, group->properties, p, tmp) {
  636. if (has_prop(parent, p->name))
  637. return true;
  638. }
  639. return false;
  640. }
  641. obs_property_t *obs_properties_add_group(obs_properties_t *props,
  642. const char *name, const char *desc,
  643. enum obs_group_type type,
  644. obs_properties_t *group)
  645. {
  646. if (!props || has_prop(props, name))
  647. return NULL;
  648. if (!group)
  649. return NULL;
  650. /* Prevent recursion. */
  651. if (props == group)
  652. return NULL;
  653. if (check_property_group_recursion(props, group))
  654. return NULL;
  655. /* Prevent duplicate properties */
  656. if (check_property_group_duplicates(props, group))
  657. return NULL;
  658. obs_property_t *p = new_prop(props, name, desc, OBS_PROPERTY_GROUP);
  659. props->groups++;
  660. group->parent = p;
  661. struct group_data *data = get_property_data(p);
  662. data->type = type;
  663. data->content = group;
  664. return p;
  665. }
  666. /* ------------------------------------------------------------------------- */
  667. static inline bool is_combo(struct obs_property *p)
  668. {
  669. return p->type == OBS_PROPERTY_LIST;
  670. }
  671. static inline struct list_data *get_list_data(struct obs_property *p)
  672. {
  673. if (!p || !is_combo(p))
  674. return NULL;
  675. return get_property_data(p);
  676. }
  677. static inline struct list_data *get_list_fmt_data(struct obs_property *p,
  678. enum obs_combo_format format)
  679. {
  680. struct list_data *data = get_list_data(p);
  681. return (data && data->format == format) ? data : NULL;
  682. }
  683. /* ------------------------------------------------------------------------- */
  684. bool obs_property_next(obs_property_t **p)
  685. {
  686. if (!p || !*p)
  687. return false;
  688. *p = (*p)->hh.next;
  689. return *p != NULL;
  690. }
  691. void obs_property_set_modified_callback(obs_property_t *p,
  692. obs_property_modified_t modified)
  693. {
  694. if (p)
  695. p->modified = modified;
  696. }
  697. void obs_property_set_modified_callback2(obs_property_t *p,
  698. obs_property_modified2_t modified2,
  699. void *priv)
  700. {
  701. if (p) {
  702. p->modified2 = modified2;
  703. p->priv = priv;
  704. }
  705. }
  706. bool obs_property_modified(obs_property_t *p, obs_data_t *settings)
  707. {
  708. if (p) {
  709. if (p->modified) {
  710. obs_properties_t *top = get_topmost_parent(p->parent);
  711. return p->modified(top, p, settings);
  712. } else if (p->modified2) {
  713. obs_properties_t *top = get_topmost_parent(p->parent);
  714. return p->modified2(p->priv, top, p, settings);
  715. }
  716. }
  717. return false;
  718. }
  719. bool obs_property_button_clicked(obs_property_t *p, void *obj)
  720. {
  721. struct obs_context_data *context = obj;
  722. if (p) {
  723. struct button_data *data =
  724. get_type_data(p, OBS_PROPERTY_BUTTON);
  725. if (data && data->callback) {
  726. obs_properties_t *top = get_topmost_parent(p->parent);
  727. if (p->priv)
  728. return data->callback(top, p, p->priv);
  729. return data->callback(top, p,
  730. (context ? context->data : NULL));
  731. }
  732. }
  733. return false;
  734. }
  735. void obs_property_set_visible(obs_property_t *p, bool visible)
  736. {
  737. if (p)
  738. p->visible = visible;
  739. }
  740. void obs_property_set_enabled(obs_property_t *p, bool enabled)
  741. {
  742. if (p)
  743. p->enabled = enabled;
  744. }
  745. void obs_property_set_description(obs_property_t *p, const char *description)
  746. {
  747. if (p) {
  748. bfree(p->desc);
  749. p->desc = description && *description ? bstrdup(description)
  750. : NULL;
  751. }
  752. }
  753. void obs_property_set_long_description(obs_property_t *p, const char *long_desc)
  754. {
  755. if (p) {
  756. bfree(p->long_desc);
  757. p->long_desc = long_desc && *long_desc ? bstrdup(long_desc)
  758. : NULL;
  759. }
  760. }
  761. const char *obs_property_name(obs_property_t *p)
  762. {
  763. return p ? p->name : NULL;
  764. }
  765. const char *obs_property_description(obs_property_t *p)
  766. {
  767. return p ? p->desc : NULL;
  768. }
  769. const char *obs_property_long_description(obs_property_t *p)
  770. {
  771. return p ? p->long_desc : NULL;
  772. }
  773. enum obs_property_type obs_property_get_type(obs_property_t *p)
  774. {
  775. return p ? p->type : OBS_PROPERTY_INVALID;
  776. }
  777. bool obs_property_enabled(obs_property_t *p)
  778. {
  779. return p ? p->enabled : false;
  780. }
  781. bool obs_property_visible(obs_property_t *p)
  782. {
  783. return p ? p->visible : false;
  784. }
  785. int obs_property_int_min(obs_property_t *p)
  786. {
  787. struct int_data *data = get_type_data(p, OBS_PROPERTY_INT);
  788. return data ? data->min : 0;
  789. }
  790. int obs_property_int_max(obs_property_t *p)
  791. {
  792. struct int_data *data = get_type_data(p, OBS_PROPERTY_INT);
  793. return data ? data->max : 0;
  794. }
  795. int obs_property_int_step(obs_property_t *p)
  796. {
  797. struct int_data *data = get_type_data(p, OBS_PROPERTY_INT);
  798. return data ? data->step : 0;
  799. }
  800. enum obs_number_type obs_property_int_type(obs_property_t *p)
  801. {
  802. struct int_data *data = get_type_data(p, OBS_PROPERTY_INT);
  803. return data ? data->type : OBS_NUMBER_SCROLLER;
  804. }
  805. const char *obs_property_int_suffix(obs_property_t *p)
  806. {
  807. struct int_data *data = get_type_data(p, OBS_PROPERTY_INT);
  808. return data ? data->suffix : NULL;
  809. }
  810. double obs_property_float_min(obs_property_t *p)
  811. {
  812. struct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);
  813. return data ? data->min : 0;
  814. }
  815. double obs_property_float_max(obs_property_t *p)
  816. {
  817. struct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);
  818. return data ? data->max : 0;
  819. }
  820. double obs_property_float_step(obs_property_t *p)
  821. {
  822. struct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);
  823. return data ? data->step : 0;
  824. }
  825. const char *obs_property_float_suffix(obs_property_t *p)
  826. {
  827. struct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);
  828. return data ? data->suffix : NULL;
  829. }
  830. enum obs_number_type obs_property_float_type(obs_property_t *p)
  831. {
  832. struct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);
  833. return data ? data->type : OBS_NUMBER_SCROLLER;
  834. }
  835. enum obs_text_type obs_property_text_type(obs_property_t *p)
  836. {
  837. struct text_data *data = get_type_data(p, OBS_PROPERTY_TEXT);
  838. return data ? data->type : OBS_TEXT_DEFAULT;
  839. }
  840. bool obs_property_text_monospace(obs_property_t *p)
  841. {
  842. struct text_data *data = get_type_data(p, OBS_PROPERTY_TEXT);
  843. return data ? data->monospace : false;
  844. }
  845. enum obs_text_info_type obs_property_text_info_type(obs_property_t *p)
  846. {
  847. struct text_data *data = get_type_data(p, OBS_PROPERTY_TEXT);
  848. return data ? data->info_type : OBS_TEXT_INFO_NORMAL;
  849. }
  850. bool obs_property_text_info_word_wrap(obs_property_t *p)
  851. {
  852. struct text_data *data = get_type_data(p, OBS_PROPERTY_TEXT);
  853. return data ? data->info_word_wrap : true;
  854. }
  855. enum obs_path_type obs_property_path_type(obs_property_t *p)
  856. {
  857. struct path_data *data = get_type_data(p, OBS_PROPERTY_PATH);
  858. return data ? data->type : OBS_PATH_DIRECTORY;
  859. }
  860. const char *obs_property_path_filter(obs_property_t *p)
  861. {
  862. struct path_data *data = get_type_data(p, OBS_PROPERTY_PATH);
  863. return data ? data->filter : NULL;
  864. }
  865. const char *obs_property_path_default_path(obs_property_t *p)
  866. {
  867. struct path_data *data = get_type_data(p, OBS_PROPERTY_PATH);
  868. return data ? data->default_path : NULL;
  869. }
  870. enum obs_combo_type obs_property_list_type(obs_property_t *p)
  871. {
  872. struct list_data *data = get_list_data(p);
  873. return data ? data->type : OBS_COMBO_TYPE_INVALID;
  874. }
  875. enum obs_combo_format obs_property_list_format(obs_property_t *p)
  876. {
  877. struct list_data *data = get_list_data(p);
  878. return data ? data->format : OBS_COMBO_FORMAT_INVALID;
  879. }
  880. void obs_property_int_set_limits(obs_property_t *p, int min, int max, int step)
  881. {
  882. struct int_data *data = get_type_data(p, OBS_PROPERTY_INT);
  883. if (!data)
  884. return;
  885. data->min = min;
  886. data->max = max;
  887. data->step = step;
  888. }
  889. void obs_property_float_set_limits(obs_property_t *p, double min, double max,
  890. double step)
  891. {
  892. struct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);
  893. if (!data)
  894. return;
  895. data->min = min;
  896. data->max = max;
  897. data->step = step;
  898. }
  899. void obs_property_int_set_suffix(obs_property_t *p, const char *suffix)
  900. {
  901. struct int_data *data = get_type_data(p, OBS_PROPERTY_INT);
  902. if (!data)
  903. return;
  904. bfree(data->suffix);
  905. data->suffix = bstrdup(suffix);
  906. }
  907. void obs_property_float_set_suffix(obs_property_t *p, const char *suffix)
  908. {
  909. struct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);
  910. if (!data)
  911. return;
  912. bfree(data->suffix);
  913. data->suffix = bstrdup(suffix);
  914. }
  915. void obs_property_text_set_monospace(obs_property_t *p, bool monospace)
  916. {
  917. struct text_data *data = get_type_data(p, OBS_PROPERTY_TEXT);
  918. if (!data)
  919. return;
  920. data->monospace = monospace;
  921. }
  922. void obs_property_text_set_info_type(obs_property_t *p,
  923. enum obs_text_info_type type)
  924. {
  925. struct text_data *data = get_type_data(p, OBS_PROPERTY_TEXT);
  926. if (!data)
  927. return;
  928. data->info_type = type;
  929. }
  930. void obs_property_text_set_info_word_wrap(obs_property_t *p, bool word_wrap)
  931. {
  932. struct text_data *data = get_type_data(p, OBS_PROPERTY_TEXT);
  933. if (!data)
  934. return;
  935. data->info_word_wrap = word_wrap;
  936. }
  937. void obs_property_button_set_type(obs_property_t *p, enum obs_button_type type)
  938. {
  939. struct button_data *data = get_type_data(p, OBS_PROPERTY_BUTTON);
  940. if (!data)
  941. return;
  942. data->type = type;
  943. }
  944. void obs_property_button_set_url(obs_property_t *p, char *url)
  945. {
  946. struct button_data *data = get_type_data(p, OBS_PROPERTY_BUTTON);
  947. if (!data)
  948. return;
  949. data->url = bstrdup(url);
  950. }
  951. void obs_property_list_clear(obs_property_t *p)
  952. {
  953. struct list_data *data = get_list_data(p);
  954. if (data)
  955. list_data_free(data);
  956. }
  957. static size_t add_item(struct list_data *data, const char *name,
  958. const void *val)
  959. {
  960. struct list_item item = {NULL};
  961. item.name = bstrdup(name);
  962. if (data->format == OBS_COMBO_FORMAT_INT)
  963. item.ll = *(const long long *)val;
  964. else if (data->format == OBS_COMBO_FORMAT_FLOAT)
  965. item.d = *(const double *)val;
  966. else if (data->format == OBS_COMBO_FORMAT_BOOL)
  967. item.b = *(const bool *)val;
  968. else
  969. item.str = bstrdup(val);
  970. return da_push_back(data->items, &item);
  971. }
  972. static void insert_item(struct list_data *data, size_t idx, const char *name,
  973. const void *val)
  974. {
  975. struct list_item item = {NULL};
  976. item.name = bstrdup(name);
  977. if (data->format == OBS_COMBO_FORMAT_INT)
  978. item.ll = *(const long long *)val;
  979. else if (data->format == OBS_COMBO_FORMAT_FLOAT)
  980. item.d = *(const double *)val;
  981. else if (data->format == OBS_COMBO_FORMAT_BOOL)
  982. item.b = *(const bool *)val;
  983. else
  984. item.str = bstrdup(val);
  985. da_insert(data->items, idx, &item);
  986. }
  987. size_t obs_property_list_add_string(obs_property_t *p, const char *name,
  988. const char *val)
  989. {
  990. struct list_data *data = get_list_data(p);
  991. if (data && data->format == OBS_COMBO_FORMAT_STRING)
  992. return add_item(data, name, val);
  993. return 0;
  994. }
  995. size_t obs_property_list_add_int(obs_property_t *p, const char *name,
  996. long long val)
  997. {
  998. struct list_data *data = get_list_data(p);
  999. if (data && data->format == OBS_COMBO_FORMAT_INT)
  1000. return add_item(data, name, &val);
  1001. return 0;
  1002. }
  1003. size_t obs_property_list_add_float(obs_property_t *p, const char *name,
  1004. double val)
  1005. {
  1006. struct list_data *data = get_list_data(p);
  1007. if (data && data->format == OBS_COMBO_FORMAT_FLOAT)
  1008. return add_item(data, name, &val);
  1009. return 0;
  1010. }
  1011. size_t obs_property_list_add_bool(obs_property_t *p, const char *name, bool val)
  1012. {
  1013. struct list_data *data = get_list_data(p);
  1014. if (data && data->format == OBS_COMBO_FORMAT_BOOL)
  1015. return add_item(data, name, &val);
  1016. return 0;
  1017. }
  1018. void obs_property_list_insert_string(obs_property_t *p, size_t idx,
  1019. const char *name, const char *val)
  1020. {
  1021. struct list_data *data = get_list_data(p);
  1022. if (data && data->format == OBS_COMBO_FORMAT_STRING)
  1023. insert_item(data, idx, name, val);
  1024. }
  1025. void obs_property_list_insert_int(obs_property_t *p, size_t idx,
  1026. const char *name, long long val)
  1027. {
  1028. struct list_data *data = get_list_data(p);
  1029. if (data && data->format == OBS_COMBO_FORMAT_INT)
  1030. insert_item(data, idx, name, &val);
  1031. }
  1032. void obs_property_list_insert_float(obs_property_t *p, size_t idx,
  1033. const char *name, double val)
  1034. {
  1035. struct list_data *data = get_list_data(p);
  1036. if (data && data->format == OBS_COMBO_FORMAT_FLOAT)
  1037. insert_item(data, idx, name, &val);
  1038. }
  1039. void obs_property_list_insert_bool(obs_property_t *p, size_t idx,
  1040. const char *name, bool val)
  1041. {
  1042. struct list_data *data = get_list_data(p);
  1043. if (data && data->format == OBS_COMBO_FORMAT_BOOL)
  1044. insert_item(data, idx, name, &val);
  1045. }
  1046. void obs_property_list_item_remove(obs_property_t *p, size_t idx)
  1047. {
  1048. struct list_data *data = get_list_data(p);
  1049. if (data && idx < data->items.num) {
  1050. list_item_free(data, data->items.array + idx);
  1051. da_erase(data->items, idx);
  1052. }
  1053. }
  1054. size_t obs_property_list_item_count(obs_property_t *p)
  1055. {
  1056. struct list_data *data = get_list_data(p);
  1057. return data ? data->items.num : 0;
  1058. }
  1059. bool obs_property_list_item_disabled(obs_property_t *p, size_t idx)
  1060. {
  1061. struct list_data *data = get_list_data(p);
  1062. return (data && idx < data->items.num) ? data->items.array[idx].disabled
  1063. : false;
  1064. }
  1065. void obs_property_list_item_disable(obs_property_t *p, size_t idx,
  1066. bool disabled)
  1067. {
  1068. struct list_data *data = get_list_data(p);
  1069. if (!data || idx >= data->items.num)
  1070. return;
  1071. data->items.array[idx].disabled = disabled;
  1072. }
  1073. const char *obs_property_list_item_name(obs_property_t *p, size_t idx)
  1074. {
  1075. struct list_data *data = get_list_data(p);
  1076. return (data && idx < data->items.num) ? data->items.array[idx].name
  1077. : NULL;
  1078. }
  1079. const char *obs_property_list_item_string(obs_property_t *p, size_t idx)
  1080. {
  1081. struct list_data *data = get_list_fmt_data(p, OBS_COMBO_FORMAT_STRING);
  1082. return (data && idx < data->items.num) ? data->items.array[idx].str
  1083. : NULL;
  1084. }
  1085. long long obs_property_list_item_int(obs_property_t *p, size_t idx)
  1086. {
  1087. struct list_data *data = get_list_fmt_data(p, OBS_COMBO_FORMAT_INT);
  1088. return (data && idx < data->items.num) ? data->items.array[idx].ll : 0;
  1089. }
  1090. double obs_property_list_item_float(obs_property_t *p, size_t idx)
  1091. {
  1092. struct list_data *data = get_list_fmt_data(p, OBS_COMBO_FORMAT_FLOAT);
  1093. return (data && idx < data->items.num) ? data->items.array[idx].d : 0.0;
  1094. }
  1095. bool obs_property_list_item_bool(obs_property_t *p, size_t idx)
  1096. {
  1097. struct list_data *data = get_list_fmt_data(p, OBS_COMBO_FORMAT_BOOL);
  1098. return (data && idx < data->items.num) ? data->items.array[idx].d
  1099. : false;
  1100. }
  1101. enum obs_editable_list_type obs_property_editable_list_type(obs_property_t *p)
  1102. {
  1103. struct editable_list_data *data =
  1104. get_type_data(p, OBS_PROPERTY_EDITABLE_LIST);
  1105. return data ? data->type : OBS_EDITABLE_LIST_TYPE_STRINGS;
  1106. }
  1107. const char *obs_property_editable_list_filter(obs_property_t *p)
  1108. {
  1109. struct editable_list_data *data =
  1110. get_type_data(p, OBS_PROPERTY_EDITABLE_LIST);
  1111. return data ? data->filter : NULL;
  1112. }
  1113. const char *obs_property_editable_list_default_path(obs_property_t *p)
  1114. {
  1115. struct editable_list_data *data =
  1116. get_type_data(p, OBS_PROPERTY_EDITABLE_LIST);
  1117. return data ? data->default_path : NULL;
  1118. }
  1119. /* ------------------------------------------------------------------------- */
  1120. /* OBS_PROPERTY_FRAME_RATE */
  1121. void obs_property_frame_rate_clear(obs_property_t *p)
  1122. {
  1123. struct frame_rate_data *data =
  1124. get_type_data(p, OBS_PROPERTY_FRAME_RATE);
  1125. if (!data)
  1126. return;
  1127. frame_rate_data_options_free(data);
  1128. frame_rate_data_ranges_free(data);
  1129. }
  1130. void obs_property_frame_rate_options_clear(obs_property_t *p)
  1131. {
  1132. struct frame_rate_data *data =
  1133. get_type_data(p, OBS_PROPERTY_FRAME_RATE);
  1134. if (!data)
  1135. return;
  1136. frame_rate_data_options_free(data);
  1137. }
  1138. void obs_property_frame_rate_fps_ranges_clear(obs_property_t *p)
  1139. {
  1140. struct frame_rate_data *data =
  1141. get_type_data(p, OBS_PROPERTY_FRAME_RATE);
  1142. if (!data)
  1143. return;
  1144. frame_rate_data_ranges_free(data);
  1145. }
  1146. size_t obs_property_frame_rate_option_add(obs_property_t *p, const char *name,
  1147. const char *description)
  1148. {
  1149. struct frame_rate_data *data =
  1150. get_type_data(p, OBS_PROPERTY_FRAME_RATE);
  1151. if (!data)
  1152. return DARRAY_INVALID;
  1153. struct frame_rate_option *opt = da_push_back_new(data->extra_options);
  1154. opt->name = bstrdup(name);
  1155. opt->description = bstrdup(description);
  1156. return data->extra_options.num - 1;
  1157. }
  1158. size_t obs_property_frame_rate_fps_range_add(obs_property_t *p,
  1159. struct media_frames_per_second min,
  1160. struct media_frames_per_second max)
  1161. {
  1162. struct frame_rate_data *data =
  1163. get_type_data(p, OBS_PROPERTY_FRAME_RATE);
  1164. if (!data)
  1165. return DARRAY_INVALID;
  1166. struct frame_rate_range *rng = da_push_back_new(data->ranges);
  1167. rng->min_time = min;
  1168. rng->max_time = max;
  1169. return data->ranges.num - 1;
  1170. }
  1171. void obs_property_frame_rate_option_insert(obs_property_t *p, size_t idx,
  1172. const char *name,
  1173. const char *description)
  1174. {
  1175. struct frame_rate_data *data =
  1176. get_type_data(p, OBS_PROPERTY_FRAME_RATE);
  1177. if (!data)
  1178. return;
  1179. struct frame_rate_option *opt = da_insert_new(data->extra_options, idx);
  1180. opt->name = bstrdup(name);
  1181. opt->description = bstrdup(description);
  1182. }
  1183. void obs_property_frame_rate_fps_range_insert(
  1184. obs_property_t *p, size_t idx, struct media_frames_per_second min,
  1185. struct media_frames_per_second max)
  1186. {
  1187. struct frame_rate_data *data =
  1188. get_type_data(p, OBS_PROPERTY_FRAME_RATE);
  1189. if (!data)
  1190. return;
  1191. struct frame_rate_range *rng = da_insert_new(data->ranges, idx);
  1192. rng->min_time = min;
  1193. rng->max_time = max;
  1194. }
  1195. size_t obs_property_frame_rate_options_count(obs_property_t *p)
  1196. {
  1197. struct frame_rate_data *data =
  1198. get_type_data(p, OBS_PROPERTY_FRAME_RATE);
  1199. return data ? data->extra_options.num : 0;
  1200. }
  1201. const char *obs_property_frame_rate_option_name(obs_property_t *p, size_t idx)
  1202. {
  1203. struct frame_rate_data *data =
  1204. get_type_data(p, OBS_PROPERTY_FRAME_RATE);
  1205. return data && data->extra_options.num > idx
  1206. ? data->extra_options.array[idx].name
  1207. : NULL;
  1208. }
  1209. const char *obs_property_frame_rate_option_description(obs_property_t *p,
  1210. size_t idx)
  1211. {
  1212. struct frame_rate_data *data =
  1213. get_type_data(p, OBS_PROPERTY_FRAME_RATE);
  1214. return data && data->extra_options.num > idx
  1215. ? data->extra_options.array[idx].description
  1216. : NULL;
  1217. }
  1218. size_t obs_property_frame_rate_fps_ranges_count(obs_property_t *p)
  1219. {
  1220. struct frame_rate_data *data =
  1221. get_type_data(p, OBS_PROPERTY_FRAME_RATE);
  1222. return data ? data->ranges.num : 0;
  1223. }
  1224. struct media_frames_per_second
  1225. obs_property_frame_rate_fps_range_min(obs_property_t *p, size_t idx)
  1226. {
  1227. struct frame_rate_data *data =
  1228. get_type_data(p, OBS_PROPERTY_FRAME_RATE);
  1229. return data && data->ranges.num > idx
  1230. ? data->ranges.array[idx].min_time
  1231. : (struct media_frames_per_second){0};
  1232. }
  1233. struct media_frames_per_second
  1234. obs_property_frame_rate_fps_range_max(obs_property_t *p, size_t idx)
  1235. {
  1236. struct frame_rate_data *data =
  1237. get_type_data(p, OBS_PROPERTY_FRAME_RATE);
  1238. return data && data->ranges.num > idx
  1239. ? data->ranges.array[idx].max_time
  1240. : (struct media_frames_per_second){0};
  1241. }
  1242. enum obs_text_type obs_proprety_text_type(obs_property_t *p)
  1243. {
  1244. return obs_property_text_type(p);
  1245. }
  1246. enum obs_group_type obs_property_group_type(obs_property_t *p)
  1247. {
  1248. struct group_data *data = get_type_data(p, OBS_PROPERTY_GROUP);
  1249. return data ? data->type : OBS_COMBO_INVALID;
  1250. }
  1251. obs_properties_t *obs_property_group_content(obs_property_t *p)
  1252. {
  1253. struct group_data *data = get_type_data(p, OBS_PROPERTY_GROUP);
  1254. return data ? data->content : NULL;
  1255. }
  1256. enum obs_button_type obs_property_button_type(obs_property_t *p)
  1257. {
  1258. struct button_data *data = get_type_data(p, OBS_PROPERTY_BUTTON);
  1259. return data ? data->type : OBS_BUTTON_DEFAULT;
  1260. }
  1261. const char *obs_property_button_url(obs_property_t *p)
  1262. {
  1263. struct button_data *data = get_type_data(p, OBS_PROPERTY_BUTTON);
  1264. return data ? data->url : "";
  1265. }