1 /******************************************************************************
2 Copyright (C) 2017 by Hugh Bailey <jim@obsproject.com>
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 ******************************************************************************/
17
18 #include "obs-scripting-lua.h"
19 #include "cstrcache.h"
20
21 #include <obs-module.h>
22
23 /* ========================================================================= */
24
get_table_string_(lua_State * script,int idx,const char * name,const char * func)25 static inline const char *get_table_string_(lua_State *script, int idx,
26 const char *name, const char *func)
27 {
28 const char *str = "";
29
30 lua_pushstring(script, name);
31 lua_gettable(script, idx - 1);
32 if (!lua_isstring(script, -1))
33 warn("%s: no item '%s' of type %s", func, name, "string");
34 else
35 str = cstrcache_get(lua_tostring(script, -1));
36 lua_pop(script, 1);
37
38 return str;
39 }
40
get_table_int_(lua_State * script,int idx,const char * name,const char * func)41 static inline int get_table_int_(lua_State *script, int idx, const char *name,
42 const char *func)
43 {
44 int val = 0;
45
46 lua_pushstring(script, name);
47 lua_gettable(script, idx - 1);
48 val = (int)lua_tointeger(script, -1);
49 lua_pop(script, 1);
50
51 UNUSED_PARAMETER(func);
52
53 return val;
54 }
55
get_callback_from_table_(lua_State * script,int idx,const char * name,int * p_reg_idx,const char * func)56 static inline void get_callback_from_table_(lua_State *script, int idx,
57 const char *name, int *p_reg_idx,
58 const char *func)
59 {
60 *p_reg_idx = LUA_REFNIL;
61
62 lua_pushstring(script, name);
63 lua_gettable(script, idx - 1);
64 if (!lua_isfunction(script, -1)) {
65 if (!lua_isnil(script, -1)) {
66 warn("%s: item '%s' is not a function", func, name);
67 }
68 lua_pop(script, 1);
69 } else {
70 *p_reg_idx = luaL_ref(script, LUA_REGISTRYINDEX);
71 }
72 }
73
74 #define get_table_string(script, idx, name) \
75 get_table_string_(script, idx, name, __FUNCTION__)
76 #define get_table_int(script, idx, name) \
77 get_table_int_(script, idx, name, __FUNCTION__)
78 #define get_callback_from_table(script, idx, name, p_reg_idx) \
79 get_callback_from_table_(script, idx, name, p_reg_idx, __FUNCTION__)
80
ls_get_libobs_obj_(lua_State * script,const char * type,int lua_idx,void * libobs_out,const char * id,const char * func,int line)81 bool ls_get_libobs_obj_(lua_State *script, const char *type, int lua_idx,
82 void *libobs_out, const char *id, const char *func,
83 int line)
84 {
85 swig_type_info *info = SWIG_TypeQuery(script, type);
86 if (info == NULL) {
87 warn("%s:%d: SWIG could not find type: %s%s%s", func, line,
88 id ? id : "", id ? "::" : "", type);
89 return false;
90 }
91
92 int ret = SWIG_ConvertPtr(script, lua_idx, libobs_out, info, 0);
93 if (!SWIG_IsOK(ret)) {
94 warn("%s:%d: SWIG failed to convert lua object to obs "
95 "object: %s%s%s",
96 func, line, id ? id : "", id ? "::" : "", type);
97 return false;
98 }
99
100 return true;
101 }
102
103 #define ls_get_libobs_obj(type, lua_index, obs_obj) \
104 ls_get_libobs_obj_(ls->script, #type " *", lua_index, obs_obj, ls->id, \
105 __FUNCTION__, __LINE__)
106
ls_push_libobs_obj_(lua_State * script,const char * type,void * libobs_in,bool ownership,const char * id,const char * func,int line)107 bool ls_push_libobs_obj_(lua_State *script, const char *type, void *libobs_in,
108 bool ownership, const char *id, const char *func,
109 int line)
110 {
111 swig_type_info *info = SWIG_TypeQuery(script, type);
112 if (info == NULL) {
113 warn("%s:%d: SWIG could not find type: %s%s%s", func, line,
114 id ? id : "", id ? "::" : "", type);
115 return false;
116 }
117
118 SWIG_NewPointerObj(script, libobs_in, info, (int)ownership);
119 return true;
120 }
121
122 #define ls_push_libobs_obj(type, obs_obj, ownership) \
123 ls_push_libobs_obj_(ls->script, #type " *", obs_obj, ownership, \
124 ls->id, __FUNCTION__, __LINE__)
125
126 /* ========================================================================= */
127
128 struct obs_lua_data;
129
130 struct obs_lua_source {
131 struct obs_lua_script *data;
132
133 lua_State *script;
134 const char *id;
135 const char *display_name;
136 int func_create;
137 int func_destroy;
138 int func_get_width;
139 int func_get_height;
140 int func_get_defaults;
141 int func_get_properties;
142 int func_update;
143 int func_activate;
144 int func_deactivate;
145 int func_show;
146 int func_hide;
147 int func_video_tick;
148 int func_video_render;
149 int func_save;
150 int func_load;
151
152 pthread_mutex_t definition_mutex;
153 struct obs_lua_data *first_source;
154
155 struct obs_lua_source *next;
156 struct obs_lua_source **p_prev_next;
157 };
158
159 extern pthread_mutex_t lua_source_def_mutex;
160 struct obs_lua_source *first_source_def = NULL;
161
162 struct obs_lua_data {
163 obs_source_t *source;
164 struct obs_lua_source *ls;
165 int lua_data_ref;
166
167 struct obs_lua_data *next;
168 struct obs_lua_data **p_prev_next;
169 };
170
171 #define call_func(name, args, rets) \
172 call_func_(ls->script, ls->func_##name, args, rets, #name, \
173 ls->display_name)
174 #define have_func(name) (ls->func_##name != LUA_REFNIL)
175 #define ls_push_data() \
176 lua_rawgeti(ls->script, LUA_REGISTRYINDEX, ld->lua_data_ref)
177 #define ls_pop(count) lua_pop(ls->script, count)
178 #define lock_script() \
179 struct obs_lua_script *__data = ls->data; \
180 struct obs_lua_script *__prev_script = current_lua_script; \
181 current_lua_script = __data; \
182 pthread_mutex_lock(&__data->mutex);
183 #define unlock_script() \
184 pthread_mutex_unlock(&__data->mutex); \
185 current_lua_script = __prev_script;
186
obs_lua_source_get_name(void * type_data)187 static const char *obs_lua_source_get_name(void *type_data)
188 {
189 struct obs_lua_source *ls = type_data;
190 return ls->display_name;
191 }
192
obs_lua_source_create(obs_data_t * settings,obs_source_t * source)193 static void *obs_lua_source_create(obs_data_t *settings, obs_source_t *source)
194 {
195 struct obs_lua_source *ls = obs_source_get_type_data(source);
196 struct obs_lua_data *data = NULL;
197
198 pthread_mutex_lock(&ls->definition_mutex);
199 if (!ls->script)
200 goto fail;
201 if (!have_func(create))
202 goto fail;
203
204 lock_script();
205
206 ls_push_libobs_obj(obs_data_t, settings, false);
207 ls_push_libobs_obj(obs_source_t, source, false);
208 call_func(create, 2, 1);
209
210 int lua_data_ref = luaL_ref(ls->script, LUA_REGISTRYINDEX);
211 if (lua_data_ref != LUA_REFNIL) {
212 data = bmalloc(sizeof(*data));
213 data->source = source;
214 data->ls = ls;
215 data->lua_data_ref = lua_data_ref;
216 }
217
218 unlock_script();
219
220 if (data) {
221 struct obs_lua_data *next = ls->first_source;
222 data->next = next;
223 data->p_prev_next = &ls->first_source;
224 if (next)
225 next->p_prev_next = &data->next;
226 ls->first_source = data;
227 }
228
229 fail:
230 pthread_mutex_unlock(&ls->definition_mutex);
231 return data;
232 }
233
call_destroy(struct obs_lua_data * ld)234 static void call_destroy(struct obs_lua_data *ld)
235 {
236 struct obs_lua_source *ls = ld->ls;
237
238 ls_push_data();
239 call_func(destroy, 1, 0);
240 luaL_unref(ls->script, LUA_REGISTRYINDEX, ld->lua_data_ref);
241 ld->lua_data_ref = LUA_REFNIL;
242 }
243
obs_lua_source_destroy(void * data)244 static void obs_lua_source_destroy(void *data)
245 {
246 struct obs_lua_data *ld = data;
247 struct obs_lua_source *ls = ld->ls;
248 struct obs_lua_data *next;
249
250 pthread_mutex_lock(&ls->definition_mutex);
251 if (!ls->script)
252 goto fail;
253 if (!have_func(destroy))
254 goto fail;
255
256 lock_script();
257 call_destroy(ld);
258 unlock_script();
259
260 fail:
261 next = ld->next;
262 *ld->p_prev_next = next;
263 if (next)
264 next->p_prev_next = ld->p_prev_next;
265
266 bfree(data);
267 pthread_mutex_unlock(&ls->definition_mutex);
268 }
269
obs_lua_source_get_width(void * data)270 static uint32_t obs_lua_source_get_width(void *data)
271 {
272 struct obs_lua_data *ld = data;
273 struct obs_lua_source *ls = ld->ls;
274 uint32_t width = 0;
275
276 pthread_mutex_lock(&ls->definition_mutex);
277 if (!ls->script)
278 goto fail;
279 if (!have_func(get_width))
280 goto fail;
281
282 lock_script();
283
284 ls_push_data();
285 if (call_func(get_width, 1, 1)) {
286 width = (uint32_t)lua_tointeger(ls->script, -1);
287 ls_pop(1);
288 }
289
290 unlock_script();
291
292 fail:
293 pthread_mutex_unlock(&ls->definition_mutex);
294 return width;
295 }
296
obs_lua_source_get_height(void * data)297 static uint32_t obs_lua_source_get_height(void *data)
298 {
299 struct obs_lua_data *ld = data;
300 struct obs_lua_source *ls = ld->ls;
301 uint32_t height = 0;
302
303 pthread_mutex_lock(&ls->definition_mutex);
304 if (!ls->script)
305 goto fail;
306 if (!have_func(get_height))
307 goto fail;
308
309 lock_script();
310
311 ls_push_data();
312 if (call_func(get_height, 1, 1)) {
313 height = (uint32_t)lua_tointeger(ls->script, -1);
314 ls_pop(1);
315 }
316
317 unlock_script();
318
319 fail:
320 pthread_mutex_unlock(&ls->definition_mutex);
321 return height;
322 }
323
obs_lua_source_get_defaults(void * type_data,obs_data_t * settings)324 static void obs_lua_source_get_defaults(void *type_data, obs_data_t *settings)
325 {
326 struct obs_lua_source *ls = type_data;
327
328 pthread_mutex_lock(&ls->definition_mutex);
329 if (!ls->script)
330 goto fail;
331 if (!have_func(get_defaults))
332 goto fail;
333
334 lock_script();
335
336 ls_push_libobs_obj(obs_data_t, settings, false);
337 call_func(get_defaults, 1, 0);
338
339 unlock_script();
340
341 fail:
342 pthread_mutex_unlock(&ls->definition_mutex);
343 }
344
obs_lua_source_get_properties(void * data)345 static obs_properties_t *obs_lua_source_get_properties(void *data)
346 {
347 struct obs_lua_data *ld = data;
348 struct obs_lua_source *ls = ld->ls;
349 obs_properties_t *props = NULL;
350
351 pthread_mutex_lock(&ls->definition_mutex);
352 if (!ls->script)
353 goto fail;
354 if (!have_func(get_properties))
355 goto fail;
356
357 lock_script();
358
359 ls_push_data();
360 if (call_func(get_properties, 1, 1)) {
361 ls_get_libobs_obj(obs_properties_t, -1, &props);
362 ls_pop(1);
363 }
364
365 unlock_script();
366
367 fail:
368 pthread_mutex_unlock(&ls->definition_mutex);
369 return props;
370 }
371
obs_lua_source_update(void * data,obs_data_t * settings)372 static void obs_lua_source_update(void *data, obs_data_t *settings)
373 {
374 struct obs_lua_data *ld = data;
375 struct obs_lua_source *ls = ld->ls;
376
377 pthread_mutex_lock(&ls->definition_mutex);
378 if (!ls->script)
379 goto fail;
380 if (!have_func(update))
381 goto fail;
382
383 lock_script();
384
385 ls_push_data();
386 ls_push_libobs_obj(obs_data_t, settings, false);
387 call_func(update, 2, 0);
388
389 unlock_script();
390
391 fail:
392 pthread_mutex_unlock(&ls->definition_mutex);
393 }
394
395 #define DEFINE_VOID_DATA_CALLBACK(name) \
396 static void obs_lua_source_##name(void *data) \
397 { \
398 struct obs_lua_data *ld = data; \
399 struct obs_lua_source *ls = ld->ls; \
400 if (!have_func(name)) \
401 return; \
402 lock_script(); \
403 ls_push_data(); \
404 call_func(name, 1, 0); \
405 unlock_script(); \
406 }
407 DEFINE_VOID_DATA_CALLBACK(activate)
DEFINE_VOID_DATA_CALLBACK(deactivate)408 DEFINE_VOID_DATA_CALLBACK(deactivate)
409 DEFINE_VOID_DATA_CALLBACK(show)
410 DEFINE_VOID_DATA_CALLBACK(hide)
411 #undef DEFINE_VOID_DATA_CALLBACK
412
413 static void obs_lua_source_video_tick(void *data, float seconds)
414 {
415 struct obs_lua_data *ld = data;
416 struct obs_lua_source *ls = ld->ls;
417
418 pthread_mutex_lock(&ls->definition_mutex);
419 if (!ls->script)
420 goto fail;
421 if (!have_func(video_tick))
422 goto fail;
423
424 lock_script();
425
426 ls_push_data();
427 lua_pushnumber(ls->script, (double)seconds);
428 call_func(video_tick, 2, 0);
429
430 unlock_script();
431
432 fail:
433 pthread_mutex_unlock(&ls->definition_mutex);
434 }
435
obs_lua_source_video_render(void * data,gs_effect_t * effect)436 static void obs_lua_source_video_render(void *data, gs_effect_t *effect)
437 {
438 struct obs_lua_data *ld = data;
439 struct obs_lua_source *ls = ld->ls;
440
441 pthread_mutex_lock(&ls->definition_mutex);
442 if (!ls->script)
443 goto fail;
444 if (!have_func(video_render))
445 goto fail;
446
447 lock_script();
448
449 ls_push_data();
450 ls_push_libobs_obj(gs_effect_t, effect, false);
451 call_func(video_render, 2, 0);
452
453 unlock_script();
454
455 fail:
456 pthread_mutex_unlock(&ls->definition_mutex);
457 }
458
obs_lua_source_save(void * data,obs_data_t * settings)459 static void obs_lua_source_save(void *data, obs_data_t *settings)
460 {
461 struct obs_lua_data *ld = data;
462 struct obs_lua_source *ls = ld->ls;
463
464 pthread_mutex_lock(&ls->definition_mutex);
465 if (!ls->script)
466 goto fail;
467 if (!have_func(save))
468 goto fail;
469
470 lock_script();
471
472 ls_push_data();
473 ls_push_libobs_obj(obs_data_t, settings, false);
474 call_func(save, 2, 0);
475
476 unlock_script();
477
478 fail:
479 pthread_mutex_unlock(&ls->definition_mutex);
480 }
481
obs_lua_source_load(void * data,obs_data_t * settings)482 static void obs_lua_source_load(void *data, obs_data_t *settings)
483 {
484 struct obs_lua_data *ld = data;
485 struct obs_lua_source *ls = ld->ls;
486
487 pthread_mutex_lock(&ls->definition_mutex);
488 if (!ls->script)
489 goto fail;
490 if (!have_func(load))
491 goto fail;
492
493 lock_script();
494
495 ls_push_data();
496 ls_push_libobs_obj(obs_data_t, settings, false);
497 call_func(load, 2, 0);
498
499 unlock_script();
500
501 fail:
502 pthread_mutex_unlock(&ls->definition_mutex);
503 }
504
source_type_unload(struct obs_lua_source * ls)505 static void source_type_unload(struct obs_lua_source *ls)
506 {
507 #define unref(name) \
508 luaL_unref(ls->script, LUA_REGISTRYINDEX, name); \
509 name = LUA_REFNIL
510
511 unref(ls->func_create);
512 unref(ls->func_destroy);
513 unref(ls->func_get_width);
514 unref(ls->func_get_height);
515 unref(ls->func_get_defaults);
516 unref(ls->func_get_properties);
517 unref(ls->func_update);
518 unref(ls->func_activate);
519 unref(ls->func_deactivate);
520 unref(ls->func_show);
521 unref(ls->func_hide);
522 unref(ls->func_video_tick);
523 unref(ls->func_video_render);
524 unref(ls->func_save);
525 unref(ls->func_load);
526 #undef unref
527 }
528
obs_lua_source_free_type_data(void * type_data)529 static void obs_lua_source_free_type_data(void *type_data)
530 {
531 struct obs_lua_source *ls = type_data;
532
533 pthread_mutex_lock(&ls->definition_mutex);
534
535 if (ls->script) {
536 lock_script();
537 source_type_unload(ls);
538 unlock_script();
539 ls->script = NULL;
540 }
541
542 pthread_mutex_unlock(&ls->definition_mutex);
543 pthread_mutex_destroy(&ls->definition_mutex);
544 bfree(ls);
545 }
546
547 EXPORT void obs_enable_source_type(const char *name, bool enable);
548
find_existing(const char * id)549 static inline struct obs_lua_source *find_existing(const char *id)
550 {
551 struct obs_lua_source *existing = NULL;
552
553 pthread_mutex_lock(&lua_source_def_mutex);
554 struct obs_lua_source *ls = first_source_def;
555 while (ls) {
556 /* can compare pointers here due to string table */
557 if (ls->id == id) {
558 existing = ls;
559 break;
560 }
561
562 ls = ls->next;
563 }
564 pthread_mutex_unlock(&lua_source_def_mutex);
565
566 return existing;
567 }
568
obs_lua_register_source(lua_State * script)569 static int obs_lua_register_source(lua_State *script)
570 {
571 struct obs_lua_source ls = {0};
572 struct obs_lua_source *existing = NULL;
573 struct obs_lua_source *v = NULL;
574 struct obs_source_info info = {0};
575 const char *id;
576
577 if (!verify_args1(script, is_table))
578 goto fail;
579
580 id = get_table_string(script, -1, "id");
581 if (!id || !*id)
582 goto fail;
583
584 /* redefinition */
585 existing = find_existing(id);
586 if (existing) {
587 if (existing->script) {
588 existing = NULL;
589 goto fail;
590 }
591
592 pthread_mutex_lock(&existing->definition_mutex);
593 }
594
595 v = existing ? existing : &ls;
596
597 v->script = script;
598 v->id = id;
599
600 info.id = v->id;
601 info.type = (enum obs_source_type)get_table_int(script, -1, "type");
602
603 info.output_flags = get_table_int(script, -1, "output_flags");
604
605 lua_pushstring(script, "get_name");
606 lua_gettable(script, -2);
607 if (lua_pcall(script, 0, 1, 0) == 0) {
608 v->display_name = cstrcache_get(lua_tostring(script, -1));
609 lua_pop(script, 1);
610 }
611
612 if (!v->display_name || !*v->display_name || !*info.id ||
613 !info.output_flags)
614 goto fail;
615
616 #define get_callback(val) \
617 do { \
618 get_callback_from_table(script, -1, #val, &v->func_##val); \
619 info.val = obs_lua_source_##val; \
620 } while (false)
621
622 get_callback(create);
623 get_callback(destroy);
624 get_callback(get_width);
625 get_callback(get_height);
626 get_callback(get_properties);
627 get_callback(update);
628 get_callback(activate);
629 get_callback(deactivate);
630 get_callback(show);
631 get_callback(hide);
632 get_callback(video_tick);
633 get_callback(video_render);
634 get_callback(save);
635 get_callback(load);
636 #undef get_callback
637
638 get_callback_from_table(script, -1, "get_defaults",
639 &v->func_get_defaults);
640
641 if (!existing) {
642 ls.data = current_lua_script;
643
644 pthread_mutex_init_recursive(&ls.definition_mutex);
645
646 info.type_data = bmemdup(&ls, sizeof(ls));
647 info.free_type_data = obs_lua_source_free_type_data;
648 info.get_name = obs_lua_source_get_name;
649 info.get_defaults2 = obs_lua_source_get_defaults;
650 obs_register_source(&info);
651
652 pthread_mutex_lock(&lua_source_def_mutex);
653 v = info.type_data;
654
655 struct obs_lua_source *next = first_source_def;
656 v->next = next;
657 if (next)
658 next->p_prev_next = &v->next;
659 v->p_prev_next = &first_source_def;
660 first_source_def = v;
661
662 pthread_mutex_unlock(&lua_source_def_mutex);
663 } else {
664 existing->script = script;
665 existing->data = current_lua_script;
666 obs_enable_source_type(id, true);
667
668 struct obs_lua_data *ld = v->first_source;
669 while (ld) {
670 struct obs_lua_source *ls = v;
671
672 if (have_func(create)) {
673 obs_source_t *source = ld->source;
674 obs_data_t *settings =
675 obs_source_get_settings(source);
676
677 ls_push_libobs_obj(obs_data_t, settings, false);
678 ls_push_libobs_obj(obs_source_t, source, false);
679 call_func(create, 2, 1);
680
681 ld->lua_data_ref =
682 luaL_ref(ls->script, LUA_REGISTRYINDEX);
683 obs_data_release(settings);
684 }
685
686 ld = ld->next;
687 }
688 }
689
690 fail:
691 if (existing) {
692 pthread_mutex_unlock(&existing->definition_mutex);
693 }
694 return 0;
695 }
696
697 /* ========================================================================= */
698
add_lua_source_functions(lua_State * script)699 void add_lua_source_functions(lua_State *script)
700 {
701 lua_getglobal(script, "obslua");
702
703 lua_pushstring(script, "obs_register_source");
704 lua_pushcfunction(script, obs_lua_register_source);
705 lua_rawset(script, -3);
706
707 lua_pop(script, 1);
708 }
709
undef_source_type(struct obs_lua_script * data,struct obs_lua_source * ls)710 static inline void undef_source_type(struct obs_lua_script *data,
711 struct obs_lua_source *ls)
712 {
713 pthread_mutex_lock(&ls->definition_mutex);
714 pthread_mutex_lock(&data->mutex);
715
716 obs_enable_source_type(ls->id, false);
717
718 struct obs_lua_data *ld = ls->first_source;
719 while (ld) {
720 call_destroy(ld);
721 ld = ld->next;
722 }
723
724 source_type_unload(ls);
725 ls->script = NULL;
726
727 pthread_mutex_unlock(&data->mutex);
728 pthread_mutex_unlock(&ls->definition_mutex);
729 }
730
undef_lua_script_sources(struct obs_lua_script * data)731 void undef_lua_script_sources(struct obs_lua_script *data)
732 {
733 pthread_mutex_lock(&lua_source_def_mutex);
734
735 struct obs_lua_source *def = first_source_def;
736 while (def) {
737 if (def->script == data->script)
738 undef_source_type(data, def);
739 def = def->next;
740 }
741
742 pthread_mutex_unlock(&lua_source_def_mutex);
743 }
744