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 "obs-scripting-config.h"
20 #include <util/platform.h>
21 #include <util/base.h>
22 #include <util/dstr.h>
23
24 #include <obs.h>
25
26 /* ========================================================================= */
27
28 #if ARCH_BITS == 64
29 #define ARCH_DIR "64bit"
30 #else
31 #define ARCH_DIR "32bit"
32 #endif
33
34 #ifdef __APPLE__
35 #define SO_EXT "dylib"
36 #elif _WIN32
37 #define SO_EXT "dll"
38 #else
39 #define SO_EXT "so"
40 #endif
41
42 static const char *startup_script_template = "\
43 for val in pairs(package.preload) do\n\
44 package.preload[val] = nil\n\
45 end\n\
46 package.cpath = package.cpath .. \";\" .. \"%s/Contents/MacOS/?.so\" .. \";\" .. \"%s\" .. \"/?." SO_EXT
47 "\"\n\
48 require \"obslua\"\n";
49
50 static const char *get_script_path_func = "\
51 function script_path()\n\
52 return \"%s\"\n\
53 end\n\
54 package.path = package.path .. \";\" .. script_path() .. \"/?.lua\"\n";
55
56 static char *startup_script = NULL;
57
58 static pthread_mutex_t tick_mutex;
59 static struct obs_lua_script *first_tick_script = NULL;
60
61 pthread_mutex_t lua_source_def_mutex;
62
63 #define ls_get_libobs_obj(type, lua_index, obs_obj) \
64 ls_get_libobs_obj_(script, #type " *", lua_index, obs_obj, NULL, \
65 __FUNCTION__, __LINE__)
66 #define ls_push_libobs_obj(type, obs_obj, ownership) \
67 ls_push_libobs_obj_(script, #type " *", obs_obj, ownership, NULL, \
68 __FUNCTION__, __LINE__)
69 #define call_func(name, args, rets) \
70 call_func_(script, cb->reg_idx, args, rets, #name, __FUNCTION__)
71
72 /* ========================================================================= */
73
74 static void add_hook_functions(lua_State *script);
75 static int obs_lua_remove_tick_callback(lua_State *script);
76 static int obs_lua_remove_main_render_callback(lua_State *script);
77
78 #if UI_ENABLED
79 void add_lua_frontend_funcs(lua_State *script);
80 #endif
81
load_lua_script(struct obs_lua_script * data)82 static bool load_lua_script(struct obs_lua_script *data)
83 {
84 struct dstr str = {0};
85 bool success = false;
86 int ret;
87
88 lua_State *script = luaL_newstate();
89 if (!script) {
90 script_warn(&data->base, "Failed to create new lua state");
91 goto fail;
92 }
93
94 pthread_mutex_lock(&data->mutex);
95
96 luaL_openlibs(script);
97 luaopen_ffi(script);
98
99 if (luaL_dostring(script, startup_script) != 0) {
100 script_warn(&data->base, "Error executing startup script 1: %s",
101 lua_tostring(script, -1));
102 goto fail;
103 }
104
105 dstr_printf(&str, get_script_path_func, data->dir.array);
106 ret = luaL_dostring(script, str.array);
107 dstr_free(&str);
108
109 if (ret != 0) {
110 script_warn(&data->base, "Error executing startup script 2: %s",
111 lua_tostring(script, -1));
112 goto fail;
113 }
114
115 current_lua_script = data;
116
117 add_lua_source_functions(script);
118 add_hook_functions(script);
119 #if UI_ENABLED
120 add_lua_frontend_funcs(script);
121 #endif
122
123 if (luaL_loadfile(script, data->base.path.array) != 0) {
124 script_warn(&data->base, "Error loading file: %s",
125 lua_tostring(script, -1));
126 goto fail;
127 }
128
129 if (lua_pcall(script, 0, LUA_MULTRET, 0) != 0) {
130 script_warn(&data->base, "Error running file: %s",
131 lua_tostring(script, -1));
132 goto fail;
133 }
134
135 ret = lua_gettop(script);
136 if (ret == 1 && lua_isboolean(script, -1)) {
137 bool success = lua_toboolean(script, -1);
138
139 if (!success) {
140 goto fail;
141 }
142 }
143
144 lua_getglobal(script, "script_tick");
145 if (lua_isfunction(script, -1)) {
146 pthread_mutex_lock(&tick_mutex);
147
148 struct obs_lua_script *next = first_tick_script;
149 data->next_tick = next;
150 data->p_prev_next_tick = &first_tick_script;
151 if (next)
152 next->p_prev_next_tick = &data->next_tick;
153 first_tick_script = data;
154
155 data->tick = luaL_ref(script, LUA_REGISTRYINDEX);
156
157 pthread_mutex_unlock(&tick_mutex);
158 }
159
160 lua_getglobal(script, "script_properties");
161 if (lua_isfunction(script, -1))
162 data->get_properties = luaL_ref(script, LUA_REGISTRYINDEX);
163 else
164 data->get_properties = LUA_REFNIL;
165
166 lua_getglobal(script, "script_update");
167 if (lua_isfunction(script, -1))
168 data->update = luaL_ref(script, LUA_REGISTRYINDEX);
169 else
170 data->update = LUA_REFNIL;
171
172 lua_getglobal(script, "script_save");
173 if (lua_isfunction(script, -1))
174 data->save = luaL_ref(script, LUA_REGISTRYINDEX);
175 else
176 data->save = LUA_REFNIL;
177
178 lua_getglobal(script, "script_defaults");
179 if (lua_isfunction(script, -1)) {
180 ls_push_libobs_obj(obs_data_t, data->base.settings, false);
181 if (lua_pcall(script, 1, 0, 0) != 0) {
182 script_warn(&data->base,
183 "Error calling "
184 "script_defaults: %s",
185 lua_tostring(script, -1));
186 }
187 }
188
189 lua_getglobal(script, "script_description");
190 if (lua_isfunction(script, -1)) {
191 if (lua_pcall(script, 0, 1, 0) != 0) {
192 script_warn(&data->base,
193 "Error calling "
194 "script_defaults: %s",
195 lua_tostring(script, -1));
196 } else {
197 const char *desc = lua_tostring(script, -1);
198 dstr_copy(&data->base.desc, desc);
199 }
200 }
201
202 lua_getglobal(script, "script_load");
203 if (lua_isfunction(script, -1)) {
204 ls_push_libobs_obj(obs_data_t, data->base.settings, false);
205 if (lua_pcall(script, 1, 0, 0) != 0) {
206 script_warn(&data->base,
207 "Error calling "
208 "script_load: %s",
209 lua_tostring(script, -1));
210 }
211 }
212
213 data->script = script;
214 success = true;
215
216 fail:
217 if (script) {
218 lua_settop(script, 0);
219 pthread_mutex_unlock(&data->mutex);
220 }
221
222 if (!success && script) {
223 lua_close(script);
224 }
225
226 current_lua_script = NULL;
227 return success;
228 }
229
230 /* -------------------------------------------- */
231
232 THREAD_LOCAL struct lua_obs_callback *current_lua_cb = NULL;
233 THREAD_LOCAL struct obs_lua_script *current_lua_script = NULL;
234
235 /* -------------------------------------------- */
236
237 struct lua_obs_timer {
238 struct lua_obs_timer *next;
239 struct lua_obs_timer **p_prev_next;
240
241 uint64_t last_ts;
242 uint64_t interval;
243 };
244
245 static pthread_mutex_t timer_mutex;
246 static struct lua_obs_timer *first_timer = NULL;
247
lua_obs_timer_init(struct lua_obs_timer * timer)248 static inline void lua_obs_timer_init(struct lua_obs_timer *timer)
249 {
250 pthread_mutex_lock(&timer_mutex);
251
252 struct lua_obs_timer *next = first_timer;
253 timer->next = next;
254 timer->p_prev_next = &first_timer;
255 if (next)
256 next->p_prev_next = &timer->next;
257 first_timer = timer;
258
259 pthread_mutex_unlock(&timer_mutex);
260 }
261
lua_obs_timer_remove(struct lua_obs_timer * timer)262 static inline void lua_obs_timer_remove(struct lua_obs_timer *timer)
263 {
264 struct lua_obs_timer *next = timer->next;
265 if (next)
266 next->p_prev_next = timer->p_prev_next;
267 *timer->p_prev_next = timer->next;
268 }
269
270 static inline struct lua_obs_callback *
lua_obs_timer_cb(struct lua_obs_timer * timer)271 lua_obs_timer_cb(struct lua_obs_timer *timer)
272 {
273 return &((struct lua_obs_callback *)timer)[-1];
274 }
275
timer_remove(lua_State * script)276 static int timer_remove(lua_State *script)
277 {
278 if (!is_function(script, 1))
279 return 0;
280
281 struct lua_obs_callback *cb = find_lua_obs_callback(script, 1);
282 if (cb)
283 remove_lua_obs_callback(cb);
284 return 0;
285 }
286
timer_call(struct script_callback * p_cb)287 static void timer_call(struct script_callback *p_cb)
288 {
289 struct lua_obs_callback *cb = (struct lua_obs_callback *)p_cb;
290
291 if (p_cb->removed)
292 return;
293
294 lock_callback();
295 call_func_(cb->script, cb->reg_idx, 0, 0, "timer_cb", __FUNCTION__);
296 unlock_callback();
297 }
298
defer_timer_init(void * p_cb)299 static void defer_timer_init(void *p_cb)
300 {
301 struct lua_obs_callback *cb = p_cb;
302 struct lua_obs_timer *timer = lua_obs_callback_extra_data(cb);
303 lua_obs_timer_init(timer);
304 }
305
timer_add(lua_State * script)306 static int timer_add(lua_State *script)
307 {
308 if (!is_function(script, 1))
309 return 0;
310 int ms = (int)lua_tointeger(script, 2);
311 if (!ms)
312 return 0;
313
314 struct lua_obs_callback *cb = add_lua_obs_callback_extra(
315 script, 1, sizeof(struct lua_obs_timer));
316 struct lua_obs_timer *timer = lua_obs_callback_extra_data(cb);
317
318 timer->interval = (uint64_t)ms * 1000000ULL;
319 timer->last_ts = obs_get_video_frame_time();
320
321 defer_call_post(defer_timer_init, cb);
322 return 0;
323 }
324
325 /* -------------------------------------------- */
326
obs_lua_main_render_callback(void * priv,uint32_t cx,uint32_t cy)327 static void obs_lua_main_render_callback(void *priv, uint32_t cx, uint32_t cy)
328 {
329 struct lua_obs_callback *cb = priv;
330 lua_State *script = cb->script;
331
332 if (cb->base.removed) {
333 obs_remove_main_render_callback(obs_lua_main_render_callback,
334 cb);
335 return;
336 }
337
338 lock_callback();
339
340 lua_pushinteger(script, (lua_Integer)cx);
341 lua_pushinteger(script, (lua_Integer)cy);
342 call_func(obs_lua_main_render_callback, 2, 0);
343
344 unlock_callback();
345 }
346
obs_lua_remove_main_render_callback(lua_State * script)347 static int obs_lua_remove_main_render_callback(lua_State *script)
348 {
349 if (!verify_args1(script, is_function))
350 return 0;
351
352 struct lua_obs_callback *cb = find_lua_obs_callback(script, 1);
353 if (cb)
354 remove_lua_obs_callback(cb);
355 return 0;
356 }
357
defer_add_render(void * cb)358 static void defer_add_render(void *cb)
359 {
360 obs_add_main_render_callback(obs_lua_main_render_callback, cb);
361 }
362
obs_lua_add_main_render_callback(lua_State * script)363 static int obs_lua_add_main_render_callback(lua_State *script)
364 {
365 if (!verify_args1(script, is_function))
366 return 0;
367
368 struct lua_obs_callback *cb = add_lua_obs_callback(script, 1);
369 defer_call_post(defer_add_render, cb);
370 return 0;
371 }
372
373 /* -------------------------------------------- */
374
obs_lua_tick_callback(void * priv,float seconds)375 static void obs_lua_tick_callback(void *priv, float seconds)
376 {
377 struct lua_obs_callback *cb = priv;
378 lua_State *script = cb->script;
379
380 if (cb->base.removed) {
381 obs_remove_tick_callback(obs_lua_tick_callback, cb);
382 return;
383 }
384
385 lock_callback();
386
387 lua_pushnumber(script, (lua_Number)seconds);
388 call_func(obs_lua_tick_callback, 1, 0);
389
390 unlock_callback();
391 }
392
obs_lua_remove_tick_callback(lua_State * script)393 static int obs_lua_remove_tick_callback(lua_State *script)
394 {
395 if (!verify_args1(script, is_function))
396 return 0;
397
398 struct lua_obs_callback *cb = find_lua_obs_callback(script, 1);
399 if (cb)
400 remove_lua_obs_callback(cb);
401 return 0;
402 }
403
defer_add_tick(void * cb)404 static void defer_add_tick(void *cb)
405 {
406 obs_add_tick_callback(obs_lua_tick_callback, cb);
407 }
408
obs_lua_add_tick_callback(lua_State * script)409 static int obs_lua_add_tick_callback(lua_State *script)
410 {
411 if (!verify_args1(script, is_function))
412 return 0;
413
414 struct lua_obs_callback *cb = add_lua_obs_callback(script, 1);
415 defer_call_post(defer_add_tick, cb);
416 return 0;
417 }
418
419 /* -------------------------------------------- */
420
calldata_signal_callback(void * priv,calldata_t * cd)421 static void calldata_signal_callback(void *priv, calldata_t *cd)
422 {
423 struct lua_obs_callback *cb = priv;
424 lua_State *script = cb->script;
425
426 if (cb->base.removed) {
427 signal_handler_remove_current();
428 return;
429 }
430
431 lock_callback();
432
433 ls_push_libobs_obj(calldata_t, cd, false);
434 call_func(calldata_signal_callback, 1, 0);
435
436 unlock_callback();
437 }
438
obs_lua_signal_handler_disconnect(lua_State * script)439 static int obs_lua_signal_handler_disconnect(lua_State *script)
440 {
441 signal_handler_t *handler;
442 const char *signal;
443
444 if (!ls_get_libobs_obj(signal_handler_t, 1, &handler))
445 return 0;
446 signal = lua_tostring(script, 2);
447 if (!signal)
448 return 0;
449 if (!is_function(script, 3))
450 return 0;
451
452 struct lua_obs_callback *cb = find_lua_obs_callback(script, 3);
453 while (cb) {
454 signal_handler_t *cb_handler =
455 calldata_ptr(&cb->base.extra, "handler");
456 const char *cb_signal =
457 calldata_string(&cb->base.extra, "signal");
458
459 if (cb_signal && strcmp(signal, cb_signal) == 0 &&
460 handler == cb_handler)
461 break;
462
463 cb = find_next_lua_obs_callback(script, cb, 3);
464 }
465
466 if (cb)
467 remove_lua_obs_callback(cb);
468 return 0;
469 }
470
defer_connect(void * p_cb)471 static void defer_connect(void *p_cb)
472 {
473 struct script_callback *cb = p_cb;
474
475 signal_handler_t *handler = calldata_ptr(&cb->extra, "handler");
476 const char *signal = calldata_string(&cb->extra, "signal");
477 signal_handler_connect(handler, signal, calldata_signal_callback, cb);
478 }
479
obs_lua_signal_handler_connect(lua_State * script)480 static int obs_lua_signal_handler_connect(lua_State *script)
481 {
482 signal_handler_t *handler;
483 const char *signal;
484
485 if (!ls_get_libobs_obj(signal_handler_t, 1, &handler))
486 return 0;
487 signal = lua_tostring(script, 2);
488 if (!signal)
489 return 0;
490 if (!is_function(script, 3))
491 return 0;
492
493 struct lua_obs_callback *cb = add_lua_obs_callback(script, 3);
494 calldata_set_ptr(&cb->base.extra, "handler", handler);
495 calldata_set_string(&cb->base.extra, "signal", signal);
496 defer_call_post(defer_connect, cb);
497 return 0;
498 }
499
500 /* -------------------------------------------- */
501
calldata_signal_callback_global(void * priv,const char * signal,calldata_t * cd)502 static void calldata_signal_callback_global(void *priv, const char *signal,
503 calldata_t *cd)
504 {
505 struct lua_obs_callback *cb = priv;
506 lua_State *script = cb->script;
507
508 if (cb->base.removed) {
509 signal_handler_remove_current();
510 return;
511 }
512
513 lock_callback();
514
515 lua_pushstring(script, signal);
516 ls_push_libobs_obj(calldata_t, cd, false);
517 call_func(calldata_signal_callback_global, 2, 0);
518
519 unlock_callback();
520 }
521
obs_lua_signal_handler_disconnect_global(lua_State * script)522 static int obs_lua_signal_handler_disconnect_global(lua_State *script)
523 {
524 signal_handler_t *handler;
525
526 if (!ls_get_libobs_obj(signal_handler_t, 1, &handler))
527 return 0;
528 if (!is_function(script, 2))
529 return 0;
530
531 struct lua_obs_callback *cb = find_lua_obs_callback(script, 3);
532 while (cb) {
533 signal_handler_t *cb_handler =
534 calldata_ptr(&cb->base.extra, "handler");
535
536 if (handler == cb_handler)
537 break;
538
539 cb = find_next_lua_obs_callback(script, cb, 3);
540 }
541
542 if (cb)
543 remove_lua_obs_callback(cb);
544 return 0;
545 }
546
defer_connect_global(void * p_cb)547 static void defer_connect_global(void *p_cb)
548 {
549 struct script_callback *cb = p_cb;
550
551 signal_handler_t *handler = calldata_ptr(&cb->extra, "handler");
552 signal_handler_connect_global(handler, calldata_signal_callback_global,
553 cb);
554 }
555
obs_lua_signal_handler_connect_global(lua_State * script)556 static int obs_lua_signal_handler_connect_global(lua_State *script)
557 {
558 signal_handler_t *handler;
559
560 if (!ls_get_libobs_obj(signal_handler_t, 1, &handler))
561 return 0;
562 if (!is_function(script, 2))
563 return 0;
564
565 struct lua_obs_callback *cb = add_lua_obs_callback(script, 2);
566 calldata_set_ptr(&cb->base.extra, "handler", handler);
567 defer_call_post(defer_connect_global, cb);
568 return 0;
569 }
570
571 /* -------------------------------------------- */
572
enum_sources_proc(void * param,obs_source_t * source)573 static bool enum_sources_proc(void *param, obs_source_t *source)
574 {
575 lua_State *script = param;
576
577 obs_source_get_ref(source);
578 ls_push_libobs_obj(obs_source_t, source, false);
579
580 size_t idx = lua_rawlen(script, -2);
581 lua_rawseti(script, -2, (int)idx + 1);
582 return true;
583 }
584
enum_sources(lua_State * script)585 static int enum_sources(lua_State *script)
586 {
587 lua_newtable(script);
588 obs_enum_sources(enum_sources_proc, script);
589 return 1;
590 }
591
592 /* -------------------------------------------- */
593
source_enum_filters_proc(obs_source_t * source,obs_source_t * filter,void * param)594 static void source_enum_filters_proc(obs_source_t *source, obs_source_t *filter,
595 void *param)
596 {
597 UNUSED_PARAMETER(source);
598
599 lua_State *script = param;
600
601 obs_source_get_ref(filter);
602 ls_push_libobs_obj(obs_source_t, filter, false);
603
604 size_t idx = lua_rawlen(script, -2);
605 lua_rawseti(script, -2, (int)idx + 1);
606 }
607
source_enum_filters(lua_State * script)608 static int source_enum_filters(lua_State *script)
609 {
610 obs_source_t *source;
611 if (!ls_get_libobs_obj(obs_source_t, 1, &source))
612 return 0;
613
614 lua_newtable(script);
615 obs_source_enum_filters(source, source_enum_filters_proc, script);
616 return 1;
617 }
618
619 /* -------------------------------------------- */
620
enum_items_proc(obs_scene_t * scene,obs_sceneitem_t * item,void * param)621 static bool enum_items_proc(obs_scene_t *scene, obs_sceneitem_t *item,
622 void *param)
623 {
624 lua_State *script = param;
625
626 UNUSED_PARAMETER(scene);
627
628 obs_sceneitem_addref(item);
629 ls_push_libobs_obj(obs_sceneitem_t, item, false);
630 lua_rawseti(script, -2, (int)lua_rawlen(script, -2) + 1);
631 return true;
632 }
633
scene_enum_items(lua_State * script)634 static int scene_enum_items(lua_State *script)
635 {
636 obs_scene_t *scene;
637 if (!ls_get_libobs_obj(obs_scene_t, 1, &scene))
638 return 0;
639
640 lua_newtable(script);
641 obs_scene_enum_items(scene, enum_items_proc, script);
642 return 1;
643 }
644
645 /* -------------------------------------------- */
646
defer_hotkey_unregister(void * p_cb)647 static void defer_hotkey_unregister(void *p_cb)
648 {
649 obs_hotkey_unregister((obs_hotkey_id)(uintptr_t)p_cb);
650 }
651
on_remove_hotkey(void * p_cb)652 static void on_remove_hotkey(void *p_cb)
653 {
654 struct lua_obs_callback *cb = p_cb;
655 obs_hotkey_id id = (obs_hotkey_id)calldata_int(&cb->base.extra, "id");
656
657 if (id != OBS_INVALID_HOTKEY_ID)
658 defer_call_post(defer_hotkey_unregister, (void *)(uintptr_t)id);
659 }
660
hotkey_pressed(void * p_cb,bool pressed)661 static void hotkey_pressed(void *p_cb, bool pressed)
662 {
663 struct lua_obs_callback *cb = p_cb;
664 lua_State *script = cb->script;
665
666 if (cb->base.removed)
667 return;
668
669 lock_callback();
670
671 lua_pushboolean(script, pressed);
672 call_func(hotkey_pressed, 1, 0);
673
674 unlock_callback();
675 }
676
defer_hotkey_pressed(void * p_cb)677 static void defer_hotkey_pressed(void *p_cb)
678 {
679 hotkey_pressed(p_cb, true);
680 }
681
defer_hotkey_unpressed(void * p_cb)682 static void defer_hotkey_unpressed(void *p_cb)
683 {
684 hotkey_pressed(p_cb, false);
685 }
686
hotkey_callback(void * p_cb,obs_hotkey_id id,obs_hotkey_t * hotkey,bool pressed)687 static void hotkey_callback(void *p_cb, obs_hotkey_id id, obs_hotkey_t *hotkey,
688 bool pressed)
689 {
690 struct lua_obs_callback *cb = p_cb;
691
692 if (cb->base.removed)
693 return;
694
695 if (pressed)
696 defer_call_post(defer_hotkey_pressed, cb);
697 else
698 defer_call_post(defer_hotkey_unpressed, cb);
699
700 UNUSED_PARAMETER(hotkey);
701 UNUSED_PARAMETER(id);
702 }
703
hotkey_unregister(lua_State * script)704 static int hotkey_unregister(lua_State *script)
705 {
706 if (!verify_args1(script, is_function))
707 return 0;
708
709 struct lua_obs_callback *cb = find_lua_obs_callback(script, 1);
710 if (cb)
711 remove_lua_obs_callback(cb);
712 return 0;
713 }
714
hotkey_register_frontend(lua_State * script)715 static int hotkey_register_frontend(lua_State *script)
716 {
717 obs_hotkey_id id;
718
719 const char *name = lua_tostring(script, 1);
720 if (!name)
721 return 0;
722 const char *desc = lua_tostring(script, 2);
723 if (!desc)
724 return 0;
725 if (!is_function(script, 3))
726 return 0;
727
728 struct lua_obs_callback *cb = add_lua_obs_callback(script, 3);
729 cb->base.on_remove = on_remove_hotkey;
730 id = obs_hotkey_register_frontend(name, desc, hotkey_callback, cb);
731 calldata_set_int(&cb->base.extra, "id", id);
732 lua_pushinteger(script, (lua_Integer)id);
733
734 if (id == OBS_INVALID_HOTKEY_ID)
735 remove_lua_obs_callback(cb);
736 return 1;
737 }
738
739 /* -------------------------------------------- */
740
button_prop_clicked(obs_properties_t * props,obs_property_t * p,void * p_cb)741 static bool button_prop_clicked(obs_properties_t *props, obs_property_t *p,
742 void *p_cb)
743 {
744 struct lua_obs_callback *cb = p_cb;
745 lua_State *script = cb->script;
746 bool ret = false;
747
748 if (cb->base.removed)
749 return false;
750
751 lock_callback();
752
753 if (!ls_push_libobs_obj(obs_properties_t, props, false))
754 goto fail;
755 if (!ls_push_libobs_obj(obs_property_t, p, false)) {
756 lua_pop(script, 1);
757 goto fail;
758 }
759
760 call_func(button_prop_clicked, 2, 1);
761 if (lua_isboolean(script, -1))
762 ret = lua_toboolean(script, -1);
763
764 fail:
765 unlock_callback();
766
767 return ret;
768 }
769
properties_add_button(lua_State * script)770 static int properties_add_button(lua_State *script)
771 {
772 obs_properties_t *props;
773 obs_property_t *p;
774
775 if (!ls_get_libobs_obj(obs_properties_t, 1, &props))
776 return 0;
777 const char *name = lua_tostring(script, 2);
778 if (!name)
779 return 0;
780 const char *text = lua_tostring(script, 3);
781 if (!text)
782 return 0;
783 if (!is_function(script, 4))
784 return 0;
785
786 struct lua_obs_callback *cb = add_lua_obs_callback(script, 4);
787 p = obs_properties_add_button2(props, name, text, button_prop_clicked,
788 cb);
789
790 if (!p || !ls_push_libobs_obj(obs_property_t, p, false))
791 return 0;
792 return 1;
793 }
794
795 /* -------------------------------------------- */
796
modified_callback(void * p_cb,obs_properties_t * props,obs_property_t * p,obs_data_t * settings)797 static bool modified_callback(void *p_cb, obs_properties_t *props,
798 obs_property_t *p, obs_data_t *settings)
799 {
800 struct lua_obs_callback *cb = p_cb;
801 lua_State *script = cb->script;
802 bool ret = false;
803
804 if (cb->base.removed)
805 return false;
806
807 lock_callback();
808 if (!ls_push_libobs_obj(obs_properties_t, props, false)) {
809 goto fail;
810 }
811 if (!ls_push_libobs_obj(obs_property_t, p, false)) {
812 lua_pop(script, 1);
813 goto fail;
814 }
815 if (!ls_push_libobs_obj(obs_data_t, settings, false)) {
816 lua_pop(script, 2);
817 goto fail;
818 }
819
820 call_func(modified_callback, 3, 1);
821 if (lua_isboolean(script, -1))
822 ret = lua_toboolean(script, -1);
823
824 fail:
825 unlock_callback();
826 return ret;
827 }
828
property_set_modified_callback(lua_State * script)829 static int property_set_modified_callback(lua_State *script)
830 {
831 obs_property_t *p;
832
833 if (!ls_get_libobs_obj(obs_property_t, 1, &p))
834 return 0;
835 if (!is_function(script, 2))
836 return 0;
837
838 struct lua_obs_callback *cb = add_lua_obs_callback(script, 2);
839 obs_property_set_modified_callback2(p, modified_callback, cb);
840 return 0;
841 }
842
843 /* -------------------------------------------- */
844
remove_current_callback(lua_State * script)845 static int remove_current_callback(lua_State *script)
846 {
847 UNUSED_PARAMETER(script);
848 if (current_lua_cb)
849 remove_lua_obs_callback(current_lua_cb);
850 return 0;
851 }
852
853 /* -------------------------------------------- */
854
calldata_source(lua_State * script)855 static int calldata_source(lua_State *script)
856 {
857 calldata_t *cd;
858 const char *str;
859 int ret = 0;
860
861 if (!ls_get_libobs_obj(calldata_t, 1, &cd))
862 goto fail;
863 str = lua_tostring(script, 2);
864 if (!str)
865 goto fail;
866
867 obs_source_t *source = calldata_ptr(cd, str);
868 if (ls_push_libobs_obj(obs_source_t, source, false))
869 ++ret;
870
871 fail:
872 return ret;
873 }
874
calldata_sceneitem(lua_State * script)875 static int calldata_sceneitem(lua_State *script)
876 {
877 calldata_t *cd;
878 const char *str;
879 int ret = 0;
880
881 if (!ls_get_libobs_obj(calldata_t, 1, &cd))
882 goto fail;
883 str = lua_tostring(script, 2);
884 if (!str)
885 goto fail;
886
887 obs_sceneitem_t *sceneitem = calldata_ptr(cd, str);
888 if (ls_push_libobs_obj(obs_sceneitem_t, sceneitem, false))
889 ++ret;
890
891 fail:
892 return ret;
893 }
894
895 /* -------------------------------------------- */
896
source_list_release(lua_State * script)897 static int source_list_release(lua_State *script)
898 {
899 size_t count = lua_rawlen(script, 1);
900 for (size_t i = 0; i < count; i++) {
901 obs_source_t *source;
902
903 lua_rawgeti(script, 1, (int)i + 1);
904 ls_get_libobs_obj(obs_source_t, -1, &source);
905 lua_pop(script, 1);
906
907 obs_source_release(source);
908 }
909 return 0;
910 }
911
sceneitem_list_release(lua_State * script)912 static int sceneitem_list_release(lua_State *script)
913 {
914 size_t count = lua_rawlen(script, 1);
915 for (size_t i = 0; i < count; i++) {
916 obs_sceneitem_t *item;
917
918 lua_rawgeti(script, 1, (int)i + 1);
919 ls_get_libobs_obj(obs_sceneitem_t, -1, &item);
920 lua_pop(script, 1);
921
922 obs_sceneitem_release(item);
923 }
924 return 0;
925 }
926
927 /* -------------------------------------------- */
928
hook_print(lua_State * script)929 static int hook_print(lua_State *script)
930 {
931 struct obs_lua_script *data = current_lua_script;
932 const char *msg = lua_tostring(script, 1);
933 if (!msg)
934 return 0;
935
936 script_info(&data->base, "%s", msg);
937 return 0;
938 }
939
hook_error(lua_State * script)940 static int hook_error(lua_State *script)
941 {
942 struct obs_lua_script *data = current_lua_script;
943 const char *msg = lua_tostring(script, 1);
944 if (!msg)
945 return 0;
946
947 script_error(&data->base, "%s", msg);
948 return 0;
949 }
950
951 /* -------------------------------------------- */
952
lua_script_log(lua_State * script)953 static int lua_script_log(lua_State *script)
954 {
955 struct obs_lua_script *data = current_lua_script;
956 int log_level = (int)lua_tointeger(script, 1);
957 const char *msg = lua_tostring(script, 2);
958
959 if (!msg)
960 return 0;
961
962 /* ------------------- */
963
964 dstr_copy(&data->log_chunk, msg);
965
966 const char *start = data->log_chunk.array;
967 char *endl = strchr(start, '\n');
968
969 while (endl) {
970 *endl = 0;
971 script_log(&data->base, log_level, "%s", start);
972 *endl = '\n';
973
974 start = endl + 1;
975 endl = strchr(start, '\n');
976 }
977
978 if (start && *start)
979 script_log(&data->base, log_level, "%s", start);
980 dstr_resize(&data->log_chunk, 0);
981
982 /* ------------------- */
983
984 return 0;
985 }
986
987 /* -------------------------------------------- */
988
add_hook_functions(lua_State * script)989 static void add_hook_functions(lua_State *script)
990 {
991 #define add_func(name, func) \
992 do { \
993 lua_pushstring(script, name); \
994 lua_pushcfunction(script, func); \
995 lua_rawset(script, -3); \
996 } while (false)
997
998 lua_getglobal(script, "_G");
999
1000 add_func("print", hook_print);
1001 add_func("error", hook_error);
1002
1003 lua_pop(script, 1);
1004
1005 /* ------------- */
1006
1007 lua_getglobal(script, "obslua");
1008
1009 add_func("script_log", lua_script_log);
1010 add_func("timer_remove", timer_remove);
1011 add_func("timer_add", timer_add);
1012 add_func("obs_enum_sources", enum_sources);
1013 add_func("obs_source_enum_filters", source_enum_filters);
1014 add_func("obs_scene_enum_items", scene_enum_items);
1015 add_func("source_list_release", source_list_release);
1016 add_func("sceneitem_list_release", sceneitem_list_release);
1017 add_func("calldata_source", calldata_source);
1018 add_func("calldata_sceneitem", calldata_sceneitem);
1019 add_func("obs_add_main_render_callback",
1020 obs_lua_add_main_render_callback);
1021 add_func("obs_remove_main_render_callback",
1022 obs_lua_remove_main_render_callback);
1023 add_func("obs_add_tick_callback", obs_lua_add_tick_callback);
1024 add_func("obs_remove_tick_callback", obs_lua_remove_tick_callback);
1025 add_func("signal_handler_connect", obs_lua_signal_handler_connect);
1026 add_func("signal_handler_disconnect",
1027 obs_lua_signal_handler_disconnect);
1028 add_func("signal_handler_connect_global",
1029 obs_lua_signal_handler_connect_global);
1030 add_func("signal_handler_disconnect_global",
1031 obs_lua_signal_handler_disconnect_global);
1032 add_func("obs_hotkey_unregister", hotkey_unregister);
1033 add_func("obs_hotkey_register_frontend", hotkey_register_frontend);
1034 add_func("obs_properties_add_button", properties_add_button);
1035 add_func("obs_property_set_modified_callback",
1036 property_set_modified_callback);
1037 add_func("remove_current_callback", remove_current_callback);
1038
1039 lua_pop(script, 1);
1040 #undef add_func
1041 }
1042
1043 /* -------------------------------------------- */
1044
lua_tick(void * param,float seconds)1045 static void lua_tick(void *param, float seconds)
1046 {
1047 struct obs_lua_script *data;
1048 struct lua_obs_timer *timer;
1049 uint64_t ts = obs_get_video_frame_time();
1050
1051 /* --------------------------------- */
1052 /* process script_tick calls */
1053
1054 pthread_mutex_lock(&tick_mutex);
1055 data = first_tick_script;
1056 while (data) {
1057 lua_State *script = data->script;
1058 current_lua_script = data;
1059
1060 pthread_mutex_lock(&data->mutex);
1061
1062 lua_pushnumber(script, (double)seconds);
1063 call_func_(script, data->tick, 1, 0, "tick", __FUNCTION__);
1064
1065 pthread_mutex_unlock(&data->mutex);
1066
1067 data = data->next_tick;
1068 }
1069 current_lua_script = NULL;
1070 pthread_mutex_unlock(&tick_mutex);
1071
1072 /* --------------------------------- */
1073 /* process timers */
1074
1075 pthread_mutex_lock(&timer_mutex);
1076 timer = first_timer;
1077 while (timer) {
1078 struct lua_obs_timer *next = timer->next;
1079 struct lua_obs_callback *cb = lua_obs_timer_cb(timer);
1080
1081 if (cb->base.removed) {
1082 lua_obs_timer_remove(timer);
1083 } else {
1084 uint64_t elapsed = ts - timer->last_ts;
1085
1086 if (elapsed >= timer->interval) {
1087 timer_call(&cb->base);
1088 timer->last_ts += timer->interval;
1089 }
1090 }
1091
1092 timer = next;
1093 }
1094 pthread_mutex_unlock(&timer_mutex);
1095
1096 UNUSED_PARAMETER(param);
1097 }
1098
1099 /* -------------------------------------------- */
1100
1101 void obs_lua_script_update(obs_script_t *script, obs_data_t *settings);
1102
obs_lua_script_load(obs_script_t * s)1103 bool obs_lua_script_load(obs_script_t *s)
1104 {
1105 struct obs_lua_script *data = (struct obs_lua_script *)s;
1106 if (!data->base.loaded) {
1107 data->base.loaded = load_lua_script(data);
1108 if (data->base.loaded)
1109 obs_lua_script_update(s, NULL);
1110 }
1111
1112 return data->base.loaded;
1113 }
1114
obs_lua_script_create(const char * path,obs_data_t * settings)1115 obs_script_t *obs_lua_script_create(const char *path, obs_data_t *settings)
1116 {
1117 struct obs_lua_script *data = bzalloc(sizeof(*data));
1118
1119 data->base.type = OBS_SCRIPT_LANG_LUA;
1120 data->tick = LUA_REFNIL;
1121
1122 pthread_mutex_init_value(&data->mutex);
1123
1124 if (pthread_mutex_init_recursive(&data->mutex) != 0) {
1125 bfree(data);
1126 return NULL;
1127 }
1128
1129 dstr_copy(&data->base.path, path);
1130
1131 char *slash = path && *path ? strrchr(path, '/') : NULL;
1132 if (slash) {
1133 slash++;
1134 dstr_copy(&data->base.file, slash);
1135 dstr_left(&data->dir, &data->base.path, slash - path);
1136 } else {
1137 dstr_copy(&data->base.file, path);
1138 }
1139
1140 data->base.settings = obs_data_create();
1141 if (settings)
1142 obs_data_apply(data->base.settings, settings);
1143
1144 obs_lua_script_load((obs_script_t *)data);
1145 return (obs_script_t *)data;
1146 }
1147
1148 extern void undef_lua_script_sources(struct obs_lua_script *data);
1149
obs_lua_script_unload(obs_script_t * s)1150 void obs_lua_script_unload(obs_script_t *s)
1151 {
1152 struct obs_lua_script *data = (struct obs_lua_script *)s;
1153
1154 if (!s->loaded)
1155 return;
1156
1157 lua_State *script = data->script;
1158
1159 /* ---------------------------- */
1160 /* undefine source types */
1161
1162 undef_lua_script_sources(data);
1163
1164 /* ---------------------------- */
1165 /* unhook tick function */
1166
1167 if (data->p_prev_next_tick) {
1168 pthread_mutex_lock(&tick_mutex);
1169
1170 struct obs_lua_script *next = data->next_tick;
1171 if (next)
1172 next->p_prev_next_tick = data->p_prev_next_tick;
1173 *data->p_prev_next_tick = next;
1174
1175 pthread_mutex_unlock(&tick_mutex);
1176
1177 data->p_prev_next_tick = NULL;
1178 data->next_tick = NULL;
1179 }
1180
1181 /* ---------------------------- */
1182 /* call script_unload */
1183
1184 pthread_mutex_lock(&data->mutex);
1185
1186 lua_getglobal(script, "script_unload");
1187 lua_pcall(script, 0, 0, 0);
1188
1189 /* ---------------------------- */
1190 /* remove all callbacks */
1191
1192 struct lua_obs_callback *cb =
1193 (struct lua_obs_callback *)data->first_callback;
1194 while (cb) {
1195 struct lua_obs_callback *next =
1196 (struct lua_obs_callback *)cb->base.next;
1197 remove_lua_obs_callback(cb);
1198 cb = next;
1199 }
1200
1201 pthread_mutex_unlock(&data->mutex);
1202
1203 /* ---------------------------- */
1204 /* close script */
1205
1206 lua_close(script);
1207 s->loaded = false;
1208 }
1209
obs_lua_script_destroy(obs_script_t * s)1210 void obs_lua_script_destroy(obs_script_t *s)
1211 {
1212 struct obs_lua_script *data = (struct obs_lua_script *)s;
1213
1214 if (data) {
1215 pthread_mutex_destroy(&data->mutex);
1216 dstr_free(&data->base.path);
1217 dstr_free(&data->base.file);
1218 dstr_free(&data->base.desc);
1219 obs_data_release(data->base.settings);
1220 dstr_free(&data->log_chunk);
1221 dstr_free(&data->dir);
1222 bfree(data);
1223 }
1224 }
1225
obs_lua_script_update(obs_script_t * s,obs_data_t * settings)1226 void obs_lua_script_update(obs_script_t *s, obs_data_t *settings)
1227 {
1228 struct obs_lua_script *data = (struct obs_lua_script *)s;
1229 lua_State *script = data->script;
1230
1231 if (!s->loaded)
1232 return;
1233 if (data->update == LUA_REFNIL)
1234 return;
1235
1236 if (settings)
1237 obs_data_apply(s->settings, settings);
1238
1239 current_lua_script = data;
1240 pthread_mutex_lock(&data->mutex);
1241
1242 ls_push_libobs_obj(obs_data_t, s->settings, false);
1243 call_func_(script, data->update, 1, 0, "script_update", __FUNCTION__);
1244
1245 pthread_mutex_unlock(&data->mutex);
1246 current_lua_script = NULL;
1247 }
1248
obs_lua_script_get_properties(obs_script_t * s)1249 obs_properties_t *obs_lua_script_get_properties(obs_script_t *s)
1250 {
1251 struct obs_lua_script *data = (struct obs_lua_script *)s;
1252 lua_State *script = data->script;
1253 obs_properties_t *props = NULL;
1254
1255 if (!s->loaded)
1256 return NULL;
1257 if (data->get_properties == LUA_REFNIL)
1258 return NULL;
1259
1260 current_lua_script = data;
1261 pthread_mutex_lock(&data->mutex);
1262
1263 call_func_(script, data->get_properties, 0, 1, "script_properties",
1264 __FUNCTION__);
1265 ls_get_libobs_obj(obs_properties_t, -1, &props);
1266
1267 pthread_mutex_unlock(&data->mutex);
1268 current_lua_script = NULL;
1269
1270 return props;
1271 }
1272
obs_lua_script_save(obs_script_t * s)1273 void obs_lua_script_save(obs_script_t *s)
1274 {
1275 struct obs_lua_script *data = (struct obs_lua_script *)s;
1276 lua_State *script = data->script;
1277
1278 if (!s->loaded)
1279 return;
1280 if (data->save == LUA_REFNIL)
1281 return;
1282
1283 current_lua_script = data;
1284 pthread_mutex_lock(&data->mutex);
1285
1286 ls_push_libobs_obj(obs_data_t, s->settings, false);
1287 call_func_(script, data->save, 1, 0, "script_save", __FUNCTION__);
1288
1289 pthread_mutex_unlock(&data->mutex);
1290 current_lua_script = NULL;
1291 }
1292
1293 /* -------------------------------------------- */
1294
obs_lua_load(void)1295 void obs_lua_load(void)
1296 {
1297 struct dstr dep_paths = {0};
1298 struct dstr tmp = {0};
1299
1300 pthread_mutex_init(&tick_mutex, NULL);
1301 pthread_mutex_init_recursive(&timer_mutex);
1302 pthread_mutex_init(&lua_source_def_mutex, NULL);
1303
1304 /* ---------------------------------------------- */
1305 /* Initialize Lua startup script */
1306
1307 char *bundlePath = "./";
1308
1309 #ifdef __APPLE__
1310 Class nsRunningApplication = objc_lookUpClass("NSRunningApplication");
1311 SEL currentAppSel = sel_getUid("currentApplication");
1312
1313 typedef id (*running_app_func)(Class, SEL);
1314 running_app_func operatingSystemName = (running_app_func)objc_msgSend;
1315 id app = operatingSystemName(nsRunningApplication, currentAppSel);
1316
1317 typedef id (*bundle_url_func)(id, SEL);
1318 bundle_url_func bundleURL = (bundle_url_func)objc_msgSend;
1319 id url = bundleURL(app, sel_getUid("bundleURL"));
1320
1321 typedef id (*url_path_func)(id, SEL);
1322 url_path_func urlPath = (url_path_func)objc_msgSend;
1323
1324 id path = urlPath(url, sel_getUid("path"));
1325
1326 typedef id (*string_func)(id, SEL);
1327 string_func utf8String = (string_func)objc_msgSend;
1328 bundlePath = (char *)utf8String(path, sel_registerName("UTF8String"));
1329 #endif
1330
1331 dstr_printf(&tmp, startup_script_template, bundlePath, SCRIPT_DIR);
1332 startup_script = tmp.array;
1333
1334 dstr_free(&dep_paths);
1335
1336 obs_add_tick_callback(lua_tick, NULL);
1337 }
1338
obs_lua_unload(void)1339 void obs_lua_unload(void)
1340 {
1341 obs_remove_tick_callback(lua_tick, NULL);
1342
1343 bfree(startup_script);
1344 pthread_mutex_destroy(&tick_mutex);
1345 pthread_mutex_destroy(&timer_mutex);
1346 pthread_mutex_destroy(&lua_source_def_mutex);
1347 }
1348