1
0

obs-scripting-lua.c 33 KB

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