obs-scripting-lua.c 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294
  1. /******************************************************************************
  2. Copyright (C) 2017 by Hugh Bailey <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include "obs-scripting-lua.h"
  15. #include "obs-scripting-config.h"
  16. #include <util/platform.h>
  17. #include <util/base.h>
  18. #include <util/dstr.h>
  19. #include <obs.h>
  20. /* ========================================================================= */
  21. #if ARCH_BITS == 64
  22. # define ARCH_DIR "64bit"
  23. #else
  24. # define ARCH_DIR "32bit"
  25. #endif
  26. #ifdef __APPLE__
  27. # define SO_EXT "dylib"
  28. #elif _WIN32
  29. # define SO_EXT "dll"
  30. #else
  31. # define SO_EXT "so"
  32. #endif
  33. static const char *startup_script_template = "\
  34. for val in pairs(package.preload) do\n\
  35. package.preload[val] = nil\n\
  36. end\n\
  37. package.cpath = package.cpath .. \";\" .. \"%s\" .. \"/?." SO_EXT "\"\n\
  38. require \"obslua\"\n";
  39. static const char *get_script_path_func = "\
  40. function script_path()\n\
  41. return \"%s\"\n\
  42. end\n\
  43. package.path = package.path .. \";\" .. script_path() .. \"/?.lua\"\n";
  44. static char *startup_script = NULL;
  45. static pthread_mutex_t tick_mutex = PTHREAD_MUTEX_INITIALIZER;
  46. static struct obs_lua_script *first_tick_script = NULL;
  47. pthread_mutex_t lua_source_def_mutex = PTHREAD_MUTEX_INITIALIZER;
  48. #define ls_get_libobs_obj(type, lua_index, obs_obj) \
  49. ls_get_libobs_obj_(script, #type " *", lua_index, obs_obj, \
  50. NULL, __FUNCTION__, __LINE__)
  51. #define ls_push_libobs_obj(type, obs_obj, ownership) \
  52. ls_push_libobs_obj_(script, #type " *", obs_obj, ownership, \
  53. NULL, __FUNCTION__, __LINE__)
  54. #define call_func(name, args, rets) \
  55. call_func_(script, cb->reg_idx, \
  56. args, rets, #name, __FUNCTION__)
  57. /* ========================================================================= */
  58. static void add_hook_functions(lua_State *script);
  59. static int obs_lua_remove_tick_callback(lua_State *script);
  60. static int obs_lua_remove_main_render_callback(lua_State *script);
  61. #if UI_ENABLED
  62. void add_lua_frontend_funcs(lua_State *script);
  63. #endif
  64. static bool load_lua_script(struct obs_lua_script *data)
  65. {
  66. struct dstr str = {0};
  67. bool success = false;
  68. int ret;
  69. lua_State *script = luaL_newstate();
  70. if (!script) {
  71. script_warn(&data->base, "Failed to create new lua state");
  72. goto fail;
  73. }
  74. pthread_mutex_lock(&data->mutex);
  75. luaL_openlibs(script);
  76. if (luaL_dostring(script, startup_script) != 0) {
  77. script_warn(&data->base, "Error executing startup script 1: %s",
  78. lua_tostring(script, -1));
  79. goto fail;
  80. }
  81. dstr_printf(&str, get_script_path_func, data->dir.array);
  82. ret = luaL_dostring(script, str.array);
  83. dstr_free(&str);
  84. if (ret != 0) {
  85. script_warn(&data->base, "Error executing startup script 2: %s",
  86. lua_tostring(script, -1));
  87. goto fail;
  88. }
  89. add_lua_source_functions(script);
  90. add_hook_functions(script);
  91. #if UI_ENABLED
  92. add_lua_frontend_funcs(script);
  93. #endif
  94. if (luaL_loadfile(script, data->base.path.array) != 0) {
  95. script_warn(&data->base, "Error loading file: %s",
  96. lua_tostring(script, -1));
  97. goto fail;
  98. }
  99. if (lua_pcall(script, 0, LUA_MULTRET, 0) != 0) {
  100. script_warn(&data->base, "Error running file: %s",
  101. lua_tostring(script, -1));
  102. goto fail;
  103. }
  104. ret = lua_gettop(script);
  105. if (ret == 1 && lua_isboolean(script, -1)) {
  106. bool success = lua_toboolean(script, -1);
  107. if (!success) {
  108. goto fail;
  109. }
  110. }
  111. lua_getglobal(script, "script_tick");
  112. if (lua_isfunction(script, -1)) {
  113. pthread_mutex_lock(&tick_mutex);
  114. struct obs_lua_script *next = first_tick_script;
  115. data->next_tick = next;
  116. data->p_prev_next_tick = &first_tick_script;
  117. if (next) next->p_prev_next_tick = &data->next_tick;
  118. first_tick_script = data;
  119. data->tick = luaL_ref(script, LUA_REGISTRYINDEX);
  120. pthread_mutex_unlock(&tick_mutex);
  121. }
  122. lua_getglobal(script, "script_properties");
  123. if (lua_isfunction(script, -1))
  124. data->get_properties = luaL_ref(script, LUA_REGISTRYINDEX);
  125. else
  126. data->get_properties = LUA_REFNIL;
  127. lua_getglobal(script, "script_update");
  128. if (lua_isfunction(script, -1))
  129. data->update = luaL_ref(script, LUA_REGISTRYINDEX);
  130. else
  131. data->update = LUA_REFNIL;
  132. lua_getglobal(script, "script_save");
  133. if (lua_isfunction(script, -1))
  134. data->save = luaL_ref(script, LUA_REGISTRYINDEX);
  135. else
  136. data->save = LUA_REFNIL;
  137. current_lua_script = data;
  138. lua_getglobal(script, "script_defaults");
  139. if (lua_isfunction(script, -1)) {
  140. ls_push_libobs_obj(obs_data_t, data->base.settings, false);
  141. if (lua_pcall(script, 1, 0, 0) != 0) {
  142. script_warn(&data->base, "Error calling "
  143. "script_defaults: %s",
  144. lua_tostring(script, -1));
  145. }
  146. }
  147. lua_getglobal(script, "script_description");
  148. if (lua_isfunction(script, -1)) {
  149. if (lua_pcall(script, 0, 1, 0) != 0) {
  150. script_warn(&data->base, "Error calling "
  151. "script_defaults: %s",
  152. lua_tostring(script, -1));
  153. } else {
  154. const char *desc = lua_tostring(script, -1);
  155. dstr_copy(&data->base.desc, desc);
  156. }
  157. }
  158. lua_getglobal(script, "script_load");
  159. if (lua_isfunction(script, -1)) {
  160. ls_push_libobs_obj(obs_data_t, data->base.settings, false);
  161. if (lua_pcall(script, 1, 0, 0) != 0) {
  162. script_warn(&data->base, "Error calling "
  163. "script_load: %s",
  164. lua_tostring(script, -1));
  165. }
  166. }
  167. data->script = script;
  168. success = true;
  169. fail:
  170. if (script) {
  171. lua_settop(script, 0);
  172. pthread_mutex_unlock(&data->mutex);
  173. }
  174. if (!success) {
  175. lua_close(script);
  176. }
  177. current_lua_script = NULL;
  178. return success;
  179. }
  180. /* -------------------------------------------- */
  181. THREAD_LOCAL struct lua_obs_callback *current_lua_cb = NULL;
  182. THREAD_LOCAL struct obs_lua_script *current_lua_script = NULL;
  183. /* -------------------------------------------- */
  184. struct lua_obs_timer {
  185. struct lua_obs_timer *next;
  186. struct lua_obs_timer **p_prev_next;
  187. uint64_t last_ts;
  188. uint64_t interval;
  189. };
  190. static pthread_mutex_t timer_mutex = PTHREAD_MUTEX_INITIALIZER;
  191. static struct lua_obs_timer *first_timer = NULL;
  192. static inline void lua_obs_timer_init(struct lua_obs_timer *timer)
  193. {
  194. pthread_mutex_lock(&timer_mutex);
  195. struct lua_obs_timer *next = first_timer;
  196. timer->next = next;
  197. timer->p_prev_next = &first_timer;
  198. if (next) next->p_prev_next = &timer->next;
  199. first_timer = timer;
  200. pthread_mutex_unlock(&timer_mutex);
  201. }
  202. static inline void lua_obs_timer_remove(struct lua_obs_timer *timer)
  203. {
  204. struct lua_obs_timer *next = timer->next;
  205. if (next) next->p_prev_next = timer->p_prev_next;
  206. *timer->p_prev_next = timer->next;
  207. }
  208. static inline struct lua_obs_callback *lua_obs_timer_cb(
  209. struct lua_obs_timer *timer)
  210. {
  211. return &((struct lua_obs_callback *)timer)[-1];
  212. }
  213. static int timer_remove(lua_State *script)
  214. {
  215. if (!is_function(script, 1))
  216. return 0;
  217. struct lua_obs_callback *cb = find_lua_obs_callback(script, 1);
  218. if (cb) remove_lua_obs_callback(cb);
  219. return 0;
  220. }
  221. static void timer_call(struct script_callback *p_cb)
  222. {
  223. struct lua_obs_callback *cb = (struct lua_obs_callback *)p_cb;
  224. if (p_cb->removed)
  225. return;
  226. lock_callback();
  227. call_func_(cb->script, cb->reg_idx, 0, 0, "timer_cb", __FUNCTION__);
  228. unlock_callback();
  229. }
  230. static void defer_timer_init(void *p_cb)
  231. {
  232. struct lua_obs_callback *cb = p_cb;
  233. struct lua_obs_timer *timer = lua_obs_callback_extra_data(cb);
  234. lua_obs_timer_init(timer);
  235. }
  236. static int timer_add(lua_State *script)
  237. {
  238. if (!is_function(script, 1))
  239. return 0;
  240. int ms = (int)lua_tointeger(script, 2);
  241. if (!ms)
  242. return 0;
  243. struct lua_obs_callback *cb = add_lua_obs_callback_extra(script, 1,
  244. sizeof(struct lua_obs_timer));
  245. struct lua_obs_timer *timer = lua_obs_callback_extra_data(cb);
  246. timer->interval = (uint64_t)ms * 1000000ULL;
  247. timer->last_ts = obs_get_video_frame_time();
  248. defer_call_post(defer_timer_init, cb);
  249. return 0;
  250. }
  251. /* -------------------------------------------- */
  252. static void obs_lua_main_render_callback(void *priv, uint32_t cx, uint32_t cy)
  253. {
  254. struct lua_obs_callback *cb = priv;
  255. lua_State *script = cb->script;
  256. if (cb->base.removed) {
  257. obs_remove_main_render_callback(obs_lua_main_render_callback,
  258. cb);
  259. return;
  260. }
  261. lock_callback();
  262. lua_pushinteger(script, (lua_Integer)cx);
  263. lua_pushinteger(script, (lua_Integer)cy);
  264. call_func(obs_lua_main_render_callback, 2, 0);
  265. unlock_callback();
  266. }
  267. static int obs_lua_remove_main_render_callback(lua_State *script)
  268. {
  269. if (!verify_args1(script, is_function))
  270. return 0;
  271. struct lua_obs_callback *cb = find_lua_obs_callback(script, 1);
  272. if (cb) remove_lua_obs_callback(cb);
  273. return 0;
  274. }
  275. static void defer_add_render(void *cb)
  276. {
  277. obs_add_main_render_callback(obs_lua_main_render_callback, cb);
  278. }
  279. static int obs_lua_add_main_render_callback(lua_State *script)
  280. {
  281. if (!verify_args1(script, is_function))
  282. return 0;
  283. struct lua_obs_callback *cb = add_lua_obs_callback(script, 1);
  284. defer_call_post(defer_add_render, cb);
  285. return 0;
  286. }
  287. /* -------------------------------------------- */
  288. static void obs_lua_tick_callback(void *priv, float seconds)
  289. {
  290. struct lua_obs_callback *cb = priv;
  291. lua_State *script = cb->script;
  292. if (cb->base.removed) {
  293. obs_remove_tick_callback(obs_lua_tick_callback, cb);
  294. return;
  295. }
  296. lock_callback();
  297. lua_pushnumber(script, (lua_Number)seconds);
  298. call_func(obs_lua_tick_callback, 2, 0);
  299. unlock_callback();
  300. }
  301. static int obs_lua_remove_tick_callback(lua_State *script)
  302. {
  303. if (!verify_args1(script, is_function))
  304. return 0;
  305. struct lua_obs_callback *cb = find_lua_obs_callback(script, 1);
  306. if (cb) remove_lua_obs_callback(cb);
  307. return 0;
  308. }
  309. static void defer_add_tick(void *cb)
  310. {
  311. obs_add_tick_callback(obs_lua_tick_callback, cb);
  312. }
  313. static int obs_lua_add_tick_callback(lua_State *script)
  314. {
  315. if (!verify_args1(script, is_function))
  316. return 0;
  317. struct lua_obs_callback *cb = add_lua_obs_callback(script, 1);
  318. defer_call_post(defer_add_tick, cb);
  319. return 0;
  320. }
  321. /* -------------------------------------------- */
  322. static void calldata_signal_callback(void *priv, calldata_t *cd)
  323. {
  324. struct lua_obs_callback *cb = priv;
  325. lua_State *script = cb->script;
  326. if (cb->base.removed) {
  327. signal_handler_remove_current();
  328. return;
  329. }
  330. lock_callback();
  331. ls_push_libobs_obj(calldata_t, cd, false);
  332. call_func(calldata_signal_callback, 1, 0);
  333. unlock_callback();
  334. }
  335. static int obs_lua_signal_handler_disconnect(lua_State *script)
  336. {
  337. signal_handler_t *handler;
  338. const char *signal;
  339. if (!ls_get_libobs_obj(signal_handler_t, 1, &handler))
  340. return 0;
  341. signal = lua_tostring(script, 2);
  342. if (!signal)
  343. return 0;
  344. if (!is_function(script, 3))
  345. return 0;
  346. struct lua_obs_callback *cb = find_lua_obs_callback(script, 3);
  347. while (cb) {
  348. signal_handler_t *cb_handler =
  349. calldata_ptr(&cb->base.extra, "handler");
  350. const char *cb_signal =
  351. calldata_string(&cb->base.extra, "signal");
  352. if (cb_signal &&
  353. strcmp(signal, cb_signal) != 0 &&
  354. handler == cb_handler)
  355. break;
  356. cb = find_next_lua_obs_callback(script, cb, 3);
  357. }
  358. if (cb) remove_lua_obs_callback(cb);
  359. return 0;
  360. }
  361. static void defer_connect(void *p_cb)
  362. {
  363. struct script_callback *cb = p_cb;
  364. signal_handler_t *handler = calldata_ptr(&cb->extra, "handler");
  365. const char *signal = calldata_string(&cb->extra, "signal");
  366. signal_handler_connect(handler, signal, calldata_signal_callback, cb);
  367. }
  368. static int obs_lua_signal_handler_connect(lua_State *script)
  369. {
  370. signal_handler_t *handler;
  371. const char *signal;
  372. if (!ls_get_libobs_obj(signal_handler_t, 1, &handler))
  373. return 0;
  374. signal = lua_tostring(script, 2);
  375. if (!signal)
  376. return 0;
  377. if (!is_function(script, 3))
  378. return 0;
  379. struct lua_obs_callback *cb = add_lua_obs_callback(script, 3);
  380. calldata_set_ptr(&cb->base.extra, "handler", handler);
  381. calldata_set_string(&cb->base.extra, "signal", signal);
  382. defer_call_post(defer_connect, cb);
  383. return 0;
  384. }
  385. /* -------------------------------------------- */
  386. static void calldata_signal_callback_global(void *priv, const char *signal,
  387. calldata_t *cd)
  388. {
  389. struct lua_obs_callback *cb = priv;
  390. lua_State *script = cb->script;
  391. if (cb->base.removed) {
  392. signal_handler_remove_current();
  393. return;
  394. }
  395. lock_callback();
  396. lua_pushstring(script, signal);
  397. ls_push_libobs_obj(calldata_t, cd, false);
  398. call_func(calldata_signal_callback_global, 2, 0);
  399. unlock_callback();
  400. }
  401. static int obs_lua_signal_handler_disconnect_global(lua_State *script)
  402. {
  403. signal_handler_t *handler;
  404. if (!ls_get_libobs_obj(signal_handler_t, 1, &handler))
  405. return 0;
  406. if (!is_function(script, 2))
  407. return 0;
  408. struct lua_obs_callback *cb = find_lua_obs_callback(script, 3);
  409. while (cb) {
  410. signal_handler_t *cb_handler =
  411. calldata_ptr(&cb->base.extra, "handler");
  412. if (handler == cb_handler)
  413. break;
  414. cb = find_next_lua_obs_callback(script, cb, 3);
  415. }
  416. if (cb) remove_lua_obs_callback(cb);
  417. return 0;
  418. }
  419. static void defer_connect_global(void *p_cb)
  420. {
  421. struct script_callback *cb = p_cb;
  422. signal_handler_t *handler = calldata_ptr(&cb->extra, "handler");
  423. signal_handler_connect_global(handler,
  424. calldata_signal_callback_global, cb);
  425. }
  426. static int obs_lua_signal_handler_connect_global(lua_State *script)
  427. {
  428. signal_handler_t *handler;
  429. if (!ls_get_libobs_obj(signal_handler_t, 1, &handler))
  430. return 0;
  431. if (!is_function(script, 2))
  432. return 0;
  433. struct lua_obs_callback *cb = add_lua_obs_callback(script, 2);
  434. calldata_set_ptr(&cb->base.extra, "handler", handler);
  435. defer_call_post(defer_connect_global, cb);
  436. return 0;
  437. }
  438. /* -------------------------------------------- */
  439. static bool enum_sources_proc(void *param, obs_source_t *source)
  440. {
  441. lua_State *script = param;
  442. obs_source_get_ref(source);
  443. ls_push_libobs_obj(obs_source_t, source, false);
  444. size_t idx = lua_rawlen(script, -2);
  445. lua_rawseti(script, -2, (int)idx + 1);
  446. return true;
  447. }
  448. static int enum_sources(lua_State *script)
  449. {
  450. lua_newtable(script);
  451. obs_enum_sources(enum_sources_proc, script);
  452. return 1;
  453. }
  454. /* -------------------------------------------- */
  455. static bool enum_items_proc(obs_scene_t *scene, obs_sceneitem_t *item,
  456. void *param)
  457. {
  458. lua_State *script = param;
  459. UNUSED_PARAMETER(scene);
  460. obs_sceneitem_addref(item);
  461. ls_push_libobs_obj(obs_sceneitem_t, item, false);
  462. lua_rawseti(script, -2, (int)lua_rawlen(script, -2) + 1);
  463. return true;
  464. }
  465. static int scene_enum_items(lua_State *script)
  466. {
  467. obs_scene_t *scene;
  468. if (!ls_get_libobs_obj(obs_scene_t, 1, &scene))
  469. return 0;
  470. lua_newtable(script);
  471. obs_scene_enum_items(scene, enum_items_proc, script);
  472. return 1;
  473. }
  474. /* -------------------------------------------- */
  475. static void defer_hotkey_unregister(void *p_cb)
  476. {
  477. obs_hotkey_unregister((obs_hotkey_id)(uintptr_t)p_cb);
  478. }
  479. static void on_remove_hotkey(void *p_cb)
  480. {
  481. struct lua_obs_callback *cb = p_cb;
  482. obs_hotkey_id id = (obs_hotkey_id)calldata_int(&cb->base.extra, "id");
  483. if (id != OBS_INVALID_HOTKEY_ID)
  484. defer_call_post(defer_hotkey_unregister, (void*)(uintptr_t)id);
  485. }
  486. static void hotkey_pressed(void *p_cb, bool pressed)
  487. {
  488. struct lua_obs_callback *cb = p_cb;
  489. lua_State *script = cb->script;
  490. if (cb->base.removed)
  491. return;
  492. lock_callback();
  493. lua_pushboolean(script, pressed);
  494. call_func(hotkey_pressed, 1, 0);
  495. unlock_callback();
  496. }
  497. static void defer_hotkey_pressed(void *p_cb)
  498. {
  499. hotkey_pressed(p_cb, true);
  500. }
  501. static void defer_hotkey_unpressed(void *p_cb)
  502. {
  503. hotkey_pressed(p_cb, false);
  504. }
  505. static void hotkey_callback(void *p_cb, obs_hotkey_id id,
  506. obs_hotkey_t *hotkey, bool pressed)
  507. {
  508. struct lua_obs_callback *cb = p_cb;
  509. if (cb->base.removed)
  510. return;
  511. if (pressed)
  512. defer_call_post(defer_hotkey_pressed, cb);
  513. else
  514. defer_call_post(defer_hotkey_unpressed, cb);
  515. UNUSED_PARAMETER(hotkey);
  516. UNUSED_PARAMETER(id);
  517. }
  518. static int hotkey_unregister(lua_State *script)
  519. {
  520. if (!verify_args1(script, is_function))
  521. return 0;
  522. struct lua_obs_callback *cb = find_lua_obs_callback(script, 1);
  523. if (cb) remove_lua_obs_callback(cb);
  524. return 0;
  525. }
  526. static int hotkey_register_frontend(lua_State *script)
  527. {
  528. obs_hotkey_id id;
  529. const char *name = lua_tostring(script, 1);
  530. if (!name)
  531. return 0;
  532. const char *desc = lua_tostring(script, 2);
  533. if (!desc)
  534. return 0;
  535. if (!is_function(script, 3))
  536. return 0;
  537. struct lua_obs_callback *cb = add_lua_obs_callback(script, 3);
  538. cb->base.on_remove = on_remove_hotkey;
  539. id = obs_hotkey_register_frontend(name, desc, hotkey_callback, cb);
  540. calldata_set_int(&cb->base.extra, "id", id);
  541. lua_pushinteger(script, (lua_Integer)id);
  542. if (id == OBS_INVALID_HOTKEY_ID)
  543. remove_lua_obs_callback(cb);
  544. return 1;
  545. }
  546. /* -------------------------------------------- */
  547. static bool button_prop_clicked(obs_properties_t *props, obs_property_t *p,
  548. void *p_cb)
  549. {
  550. struct lua_obs_callback *cb = p_cb;
  551. lua_State *script = cb->script;
  552. bool ret = false;
  553. if (cb->base.removed)
  554. return false;
  555. lock_callback();
  556. if (!ls_push_libobs_obj(obs_properties_t, props, false))
  557. goto fail;
  558. if (!ls_push_libobs_obj(obs_property_t, p, false)) {
  559. lua_pop(script, 1);
  560. goto fail;
  561. }
  562. call_func(button_prop_clicked, 2, 1);
  563. if (lua_isboolean(script, -1))
  564. ret = lua_toboolean(script, -1);
  565. fail:
  566. unlock_callback();
  567. return ret;
  568. }
  569. static int properties_add_button(lua_State *script)
  570. {
  571. obs_properties_t *props;
  572. obs_property_t *p;
  573. if (!ls_get_libobs_obj(obs_properties_t, 1, &props))
  574. return 0;
  575. const char *name = lua_tostring(script, 2);
  576. if (!name)
  577. return 0;
  578. const char *text = lua_tostring(script, 3);
  579. if (!text)
  580. return 0;
  581. if (!is_function(script, 4))
  582. return 0;
  583. struct lua_obs_callback *cb = add_lua_obs_callback(script, 4);
  584. p = obs_properties_add_button2(props, name, text, button_prop_clicked,
  585. cb);
  586. if (!p || !ls_push_libobs_obj(obs_property_t, p, false))
  587. return 0;
  588. return 1;
  589. }
  590. /* -------------------------------------------- */
  591. static bool modified_callback(void *p_cb, obs_properties_t *props,
  592. obs_property_t *p, obs_data_t *settings)
  593. {
  594. struct lua_obs_callback *cb = p_cb;
  595. lua_State *script = cb->script;
  596. bool ret = false;
  597. if (cb->base.removed)
  598. return false;
  599. lock_callback();
  600. if (!ls_push_libobs_obj(obs_properties_t, props, false)) {
  601. goto fail;
  602. }
  603. if (!ls_push_libobs_obj(obs_property_t, p, false)) {
  604. lua_pop(script, 1);
  605. goto fail;
  606. }
  607. if (!ls_push_libobs_obj(obs_data_t, settings, false)) {
  608. lua_pop(script, 2);
  609. goto fail;
  610. }
  611. call_func(modified_callback, 3, 1);
  612. if (lua_isboolean(script, -1))
  613. ret = lua_toboolean(script, -1);
  614. fail:
  615. unlock_callback();
  616. return ret;
  617. }
  618. static int property_set_modified_callback(lua_State *script)
  619. {
  620. obs_property_t *p;
  621. if (!ls_get_libobs_obj(obs_property_t, 1, &p))
  622. return 0;
  623. if (!is_function(script, 2))
  624. return 0;
  625. struct lua_obs_callback *cb = add_lua_obs_callback(script, 2);
  626. obs_property_set_modified_callback2(p, modified_callback, cb);
  627. return 0;
  628. }
  629. /* -------------------------------------------- */
  630. static int remove_current_callback(lua_State *script)
  631. {
  632. UNUSED_PARAMETER(script);
  633. if (current_lua_cb)
  634. remove_lua_obs_callback(current_lua_cb);
  635. return 0;
  636. }
  637. /* -------------------------------------------- */
  638. static int calldata_source(lua_State *script)
  639. {
  640. calldata_t *cd;
  641. const char *str;
  642. int ret = 0;
  643. if (!ls_get_libobs_obj(calldata_t, 1, &cd))
  644. goto fail;
  645. str = lua_tostring(script, 2);
  646. if (!str)
  647. goto fail;
  648. obs_source_t *source = calldata_ptr(cd, str);
  649. if (ls_push_libobs_obj(obs_source_t, source, false))
  650. ++ret;
  651. fail:
  652. return ret;
  653. }
  654. static int calldata_sceneitem(lua_State *script)
  655. {
  656. calldata_t *cd;
  657. const char *str;
  658. int ret = 0;
  659. if (!ls_get_libobs_obj(calldata_t, 1, &cd))
  660. goto fail;
  661. str = lua_tostring(script, 2);
  662. if (!str)
  663. goto fail;
  664. obs_sceneitem_t *sceneitem = calldata_ptr(cd, str);
  665. if (ls_push_libobs_obj(obs_sceneitem_t, sceneitem, false))
  666. ++ret;
  667. fail:
  668. return ret;
  669. }
  670. /* -------------------------------------------- */
  671. static int source_list_release(lua_State *script)
  672. {
  673. size_t count = lua_rawlen(script, 1);
  674. for (size_t i = 0; i < count; i++) {
  675. obs_source_t *source;
  676. lua_rawgeti(script, 1, (int)i + 1);
  677. ls_get_libobs_obj(obs_source_t, -1, &source);
  678. lua_pop(script, 1);
  679. obs_source_release(source);
  680. }
  681. return 0;
  682. }
  683. static int sceneitem_list_release(lua_State *script)
  684. {
  685. size_t count = lua_rawlen(script, 1);
  686. for (size_t i = 0; i < count; i++) {
  687. obs_sceneitem_t *item;
  688. lua_rawgeti(script, 1, (int)i + 1);
  689. ls_get_libobs_obj(obs_sceneitem_t, -1, &item);
  690. lua_pop(script, 1);
  691. obs_sceneitem_release(item);
  692. }
  693. return 0;
  694. }
  695. /* -------------------------------------------- */
  696. static int hook_print(lua_State *script)
  697. {
  698. struct obs_lua_script *data = current_lua_script;
  699. const char *msg = lua_tostring(script, 1);
  700. if (!msg)
  701. return 0;
  702. script_info(&data->base, "%s", msg);
  703. return 0;
  704. }
  705. static int hook_error(lua_State *script)
  706. {
  707. struct obs_lua_script *data = current_lua_script;
  708. const char *msg = lua_tostring(script, 1);
  709. if (!msg)
  710. return 0;
  711. script_error(&data->base, "%s", msg);
  712. return 0;
  713. }
  714. /* -------------------------------------------- */
  715. static int lua_script_log(lua_State *script)
  716. {
  717. struct obs_lua_script *data = current_lua_script;
  718. int log_level = (int)lua_tointeger(script, 1);
  719. const char *msg = lua_tostring(script, 2);
  720. if (!msg)
  721. return 0;
  722. /* ------------------- */
  723. dstr_copy(&data->log_chunk, msg);
  724. const char *start = data->log_chunk.array;
  725. char *endl = strchr(start, '\n');
  726. while (endl) {
  727. *endl = 0;
  728. script_log(&data->base, log_level, "%s", start);
  729. *endl = '\n';
  730. start = endl + 1;
  731. endl = strchr(start, '\n');
  732. }
  733. if (start && *start)
  734. script_log(&data->base, log_level, "%s", start);
  735. dstr_resize(&data->log_chunk, 0);
  736. /* ------------------- */
  737. return 0;
  738. }
  739. /* -------------------------------------------- */
  740. static void add_hook_functions(lua_State *script)
  741. {
  742. #define add_func(name, func) \
  743. do { \
  744. lua_pushstring(script, name); \
  745. lua_pushcfunction(script, func); \
  746. lua_rawset(script, -3); \
  747. } while (false)
  748. lua_getglobal(script, "_G");
  749. add_func("print", hook_print);
  750. add_func("error", hook_error);
  751. lua_pop(script, 1);
  752. /* ------------- */
  753. lua_getglobal(script, "obslua");
  754. add_func("script_log", lua_script_log);
  755. add_func("timer_remove", timer_remove);
  756. add_func("timer_add", timer_add);
  757. add_func("obs_enum_sources", enum_sources);
  758. add_func("obs_scene_enum_items", scene_enum_items);
  759. add_func("source_list_release", source_list_release);
  760. add_func("sceneitem_list_release", sceneitem_list_release);
  761. add_func("calldata_source", calldata_source);
  762. add_func("calldata_sceneitem", calldata_sceneitem);
  763. add_func("obs_add_main_render_callback",
  764. obs_lua_add_main_render_callback);
  765. add_func("obs_remove_main_render_callback",
  766. obs_lua_remove_main_render_callback);
  767. add_func("obs_add_tick_callback",
  768. obs_lua_add_tick_callback);
  769. add_func("obs_remove_tick_callback",
  770. obs_lua_remove_tick_callback);
  771. add_func("signal_handler_connect",
  772. obs_lua_signal_handler_connect);
  773. add_func("signal_handler_disconnect",
  774. obs_lua_signal_handler_disconnect);
  775. add_func("signal_handler_connect_global",
  776. obs_lua_signal_handler_connect_global);
  777. add_func("signal_handler_disconnect_global",
  778. obs_lua_signal_handler_disconnect_global);
  779. add_func("obs_hotkey_unregister",
  780. hotkey_unregister);
  781. add_func("obs_hotkey_register_frontend",
  782. hotkey_register_frontend);
  783. add_func("obs_properties_add_button",
  784. properties_add_button);
  785. add_func("obs_property_set_modified_callback",
  786. property_set_modified_callback);
  787. add_func("remove_current_callback",
  788. remove_current_callback);
  789. lua_pop(script, 1);
  790. #undef add_func
  791. }
  792. /* -------------------------------------------- */
  793. static void lua_tick(void *param, float seconds)
  794. {
  795. struct obs_lua_script *data;
  796. struct lua_obs_timer *timer;
  797. uint64_t ts = obs_get_video_frame_time();
  798. /* --------------------------------- */
  799. /* process script_tick calls */
  800. pthread_mutex_lock(&tick_mutex);
  801. data = first_tick_script;
  802. while (data) {
  803. lua_State *script = data->script;
  804. pthread_mutex_lock(&data->mutex);
  805. lua_pushnumber(script, (double)seconds);
  806. call_func_(script, data->tick, 1, 0, "tick", __FUNCTION__);
  807. pthread_mutex_unlock(&data->mutex);
  808. data = data->next_tick;
  809. }
  810. pthread_mutex_unlock(&tick_mutex);
  811. /* --------------------------------- */
  812. /* process timers */
  813. pthread_mutex_lock(&timer_mutex);
  814. timer = first_timer;
  815. while (timer) {
  816. struct lua_obs_timer *next = timer->next;
  817. struct lua_obs_callback *cb = lua_obs_timer_cb(timer);
  818. if (cb->base.removed) {
  819. lua_obs_timer_remove(timer);
  820. } else {
  821. uint64_t elapsed = ts - timer->last_ts;
  822. if (elapsed >= timer->interval) {
  823. timer_call(&cb->base);
  824. timer->last_ts += timer->interval;
  825. }
  826. }
  827. timer = next;
  828. }
  829. pthread_mutex_unlock(&timer_mutex);
  830. UNUSED_PARAMETER(param);
  831. }
  832. /* -------------------------------------------- */
  833. void obs_lua_script_update(obs_script_t *script, obs_data_t *settings);
  834. bool obs_lua_script_load(obs_script_t *s)
  835. {
  836. struct obs_lua_script *data = (struct obs_lua_script *)s;
  837. if (!data->base.loaded) {
  838. data->base.loaded = load_lua_script(data);
  839. if (data->base.loaded)
  840. obs_lua_script_update(s, NULL);
  841. }
  842. return data->base.loaded;
  843. }
  844. obs_script_t *obs_lua_script_create(const char *path, obs_data_t *settings)
  845. {
  846. struct obs_lua_script *data = bzalloc(sizeof(*data));
  847. data->base.type = OBS_SCRIPT_LANG_LUA;
  848. data->tick = LUA_REFNIL;
  849. pthread_mutexattr_t attr;
  850. pthread_mutexattr_init(&attr);
  851. pthread_mutex_init_value(&data->mutex);
  852. pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
  853. if (pthread_mutex_init(&data->mutex, &attr) != 0) {
  854. bfree(data);
  855. return NULL;
  856. }
  857. dstr_copy(&data->base.path, path);
  858. char *slash = path && *path ? strrchr(path, '/') : NULL;
  859. if (slash) {
  860. slash++;
  861. dstr_copy(&data->base.file, slash);
  862. dstr_left(&data->dir, &data->base.path, slash - path);
  863. } else {
  864. dstr_copy(&data->base.file, path);
  865. }
  866. data->base.settings = obs_data_create();
  867. if (settings)
  868. obs_data_apply(data->base.settings, settings);
  869. obs_lua_script_load((obs_script_t *)data);
  870. return (obs_script_t *)data;
  871. }
  872. extern void undef_lua_script_sources(struct obs_lua_script *data);
  873. void obs_lua_script_unload(obs_script_t *s)
  874. {
  875. struct obs_lua_script *data = (struct obs_lua_script *)s;
  876. if (!s->loaded)
  877. return;
  878. lua_State *script = data->script;
  879. /* ---------------------------- */
  880. /* undefine source types */
  881. undef_lua_script_sources(data);
  882. /* ---------------------------- */
  883. /* unhook tick function */
  884. if (data->p_prev_next_tick) {
  885. pthread_mutex_lock(&tick_mutex);
  886. struct obs_lua_script *next = data->next_tick;
  887. if (next) next->p_prev_next_tick = data->p_prev_next_tick;
  888. *data->p_prev_next_tick = next;
  889. pthread_mutex_unlock(&tick_mutex);
  890. data->p_prev_next_tick = NULL;
  891. data->next_tick = NULL;
  892. }
  893. /* ---------------------------- */
  894. /* call script_unload */
  895. pthread_mutex_lock(&data->mutex);
  896. lua_getglobal(script, "script_unload");
  897. lua_pcall(script, 0, 0, 0);
  898. /* ---------------------------- */
  899. /* remove all callbacks */
  900. struct lua_obs_callback *cb =
  901. (struct lua_obs_callback *)data->first_callback;
  902. while (cb) {
  903. struct lua_obs_callback *next =
  904. (struct lua_obs_callback *)cb->base.next;
  905. remove_lua_obs_callback(cb);
  906. cb = next;
  907. }
  908. pthread_mutex_unlock(&data->mutex);
  909. /* ---------------------------- */
  910. /* close script */
  911. lua_close(script);
  912. s->loaded = false;
  913. }
  914. void obs_lua_script_destroy(obs_script_t *s)
  915. {
  916. struct obs_lua_script *data = (struct obs_lua_script *)s;
  917. if (data) {
  918. pthread_mutex_destroy(&data->mutex);
  919. dstr_free(&data->base.path);
  920. dstr_free(&data->base.file);
  921. dstr_free(&data->base.desc);
  922. obs_data_release(data->base.settings);
  923. dstr_free(&data->log_chunk);
  924. dstr_free(&data->dir);
  925. bfree(data);
  926. }
  927. }
  928. void obs_lua_script_update(obs_script_t *s, obs_data_t *settings)
  929. {
  930. struct obs_lua_script *data = (struct obs_lua_script *)s;
  931. lua_State *script = data->script;
  932. if (!s->loaded)
  933. return;
  934. if (data->update == LUA_REFNIL)
  935. return;
  936. if (settings)
  937. obs_data_apply(s->settings, settings);
  938. current_lua_script = data;
  939. pthread_mutex_lock(&data->mutex);
  940. ls_push_libobs_obj(obs_data_t, s->settings, false);
  941. call_func_(script, data->update, 1, 0, "script_update", __FUNCTION__);
  942. pthread_mutex_unlock(&data->mutex);
  943. current_lua_script = NULL;
  944. }
  945. obs_properties_t *obs_lua_script_get_properties(obs_script_t *s)
  946. {
  947. struct obs_lua_script *data = (struct obs_lua_script *)s;
  948. lua_State *script = data->script;
  949. obs_properties_t *props = NULL;
  950. if (!s->loaded)
  951. return NULL;
  952. if (data->get_properties == LUA_REFNIL)
  953. return NULL;
  954. current_lua_script = data;
  955. pthread_mutex_lock(&data->mutex);
  956. call_func_(script, data->get_properties, 0, 1, "script_properties",
  957. __FUNCTION__);
  958. ls_get_libobs_obj(obs_properties_t, -1, &props);
  959. pthread_mutex_unlock(&data->mutex);
  960. current_lua_script = NULL;
  961. return props;
  962. }
  963. void obs_lua_script_save(obs_script_t *s)
  964. {
  965. struct obs_lua_script *data = (struct obs_lua_script *)s;
  966. lua_State *script = data->script;
  967. if (!s->loaded)
  968. return;
  969. if (data->save == LUA_REFNIL)
  970. return;
  971. current_lua_script = data;
  972. pthread_mutex_lock(&data->mutex);
  973. ls_push_libobs_obj(obs_data_t, s->settings, false);
  974. call_func_(script, data->save, 1, 0, "script_save", __FUNCTION__);
  975. pthread_mutex_unlock(&data->mutex);
  976. current_lua_script = NULL;
  977. }
  978. /* -------------------------------------------- */
  979. void obs_lua_load(void)
  980. {
  981. struct dstr dep_paths = {0};
  982. struct dstr tmp = {0};
  983. pthread_mutexattr_t attr;
  984. pthread_mutexattr_init(&attr);
  985. pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
  986. pthread_mutex_init(&tick_mutex, NULL);
  987. pthread_mutex_init(&timer_mutex, &attr);
  988. pthread_mutex_init(&lua_source_def_mutex, NULL);
  989. /* ---------------------------------------------- */
  990. /* Initialize Lua startup script */
  991. dstr_printf(&tmp, startup_script_template, SCRIPT_DIR);
  992. startup_script = tmp.array;
  993. dstr_free(&dep_paths);
  994. obs_add_tick_callback(lua_tick, NULL);
  995. }
  996. void obs_lua_unload(void)
  997. {
  998. obs_remove_tick_callback(lua_tick, NULL);
  999. bfree(startup_script);
  1000. pthread_mutex_destroy(&tick_mutex);
  1001. pthread_mutex_destroy(&timer_mutex);
  1002. pthread_mutex_destroy(&lua_source_def_mutex);
  1003. }