1
0

obs-properties.c 33 KB

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