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