123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724 |
- /******************************************************************************
- Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- ******************************************************************************/
- #include "obs-scripting-lua.h"
- #include "cstrcache.h"
- #include <obs-module.h>
- /* ========================================================================= */
- static inline const char *get_table_string_(lua_State *script, int idx, const char *name, const char *func)
- {
- const char *str = "";
- lua_pushstring(script, name);
- lua_gettable(script, idx - 1);
- if (!lua_isstring(script, -1))
- warn("%s: no item '%s' of type %s", func, name, "string");
- else
- str = cstrcache_get(lua_tostring(script, -1));
- lua_pop(script, 1);
- return str;
- }
- static inline int get_table_int_(lua_State *script, int idx, const char *name, const char *func)
- {
- int val = 0;
- lua_pushstring(script, name);
- lua_gettable(script, idx - 1);
- val = (int)lua_tointeger(script, -1);
- lua_pop(script, 1);
- UNUSED_PARAMETER(func);
- return val;
- }
- static inline void get_callback_from_table_(lua_State *script, int idx, const char *name, int *p_reg_idx,
- const char *func)
- {
- *p_reg_idx = LUA_REFNIL;
- lua_pushstring(script, name);
- lua_gettable(script, idx - 1);
- if (!lua_isfunction(script, -1)) {
- if (!lua_isnil(script, -1)) {
- warn("%s: item '%s' is not a function", func, name);
- }
- lua_pop(script, 1);
- } else {
- *p_reg_idx = luaL_ref(script, LUA_REGISTRYINDEX);
- }
- }
- #define get_table_string(script, idx, name) get_table_string_(script, idx, name, __FUNCTION__)
- #define get_table_int(script, idx, name) get_table_int_(script, idx, name, __FUNCTION__)
- #define get_callback_from_table(script, idx, name, p_reg_idx) \
- get_callback_from_table_(script, idx, name, p_reg_idx, __FUNCTION__)
- bool ls_get_libobs_obj_(lua_State *script, const char *type, int lua_idx, void *libobs_out, const char *id,
- const char *func, int line)
- {
- swig_type_info *info = SWIG_TypeQuery(script, type);
- if (info == NULL) {
- warn("%s:%d: SWIG could not find type: %s%s%s", func, line, id ? id : "", id ? "::" : "", type);
- return false;
- }
- int ret = SWIG_ConvertPtr(script, lua_idx, libobs_out, info, 0);
- if (!SWIG_IsOK(ret)) {
- warn("%s:%d: SWIG failed to convert lua object to obs "
- "object: %s%s%s",
- func, line, id ? id : "", id ? "::" : "", type);
- return false;
- }
- return true;
- }
- #define ls_get_libobs_obj(type, lua_index, obs_obj) \
- ls_get_libobs_obj_(ls->script, #type " *", lua_index, obs_obj, ls->id, __FUNCTION__, __LINE__)
- bool ls_push_libobs_obj_(lua_State *script, const char *type, void *libobs_in, bool ownership, const char *id,
- const char *func, int line)
- {
- swig_type_info *info = SWIG_TypeQuery(script, type);
- if (info == NULL) {
- warn("%s:%d: SWIG could not find type: %s%s%s", func, line, id ? id : "", id ? "::" : "", type);
- return false;
- }
- SWIG_NewPointerObj(script, libobs_in, info, (int)ownership);
- return true;
- }
- #define ls_push_libobs_obj(type, obs_obj, ownership) \
- ls_push_libobs_obj_(ls->script, #type " *", obs_obj, ownership, ls->id, __FUNCTION__, __LINE__)
- /* ========================================================================= */
- struct obs_lua_data;
- struct obs_lua_source {
- struct obs_lua_script *data;
- lua_State *script;
- const char *id;
- const char *display_name;
- int func_create;
- int func_destroy;
- int func_get_width;
- int func_get_height;
- int func_get_defaults;
- int func_get_properties;
- int func_update;
- int func_activate;
- int func_deactivate;
- int func_show;
- int func_hide;
- int func_video_tick;
- int func_video_render;
- int func_save;
- int func_load;
- pthread_mutex_t definition_mutex;
- struct obs_lua_data *first_source;
- struct obs_lua_source *next;
- struct obs_lua_source **p_prev_next;
- };
- extern pthread_mutex_t lua_source_def_mutex;
- struct obs_lua_source *first_source_def = NULL;
- struct obs_lua_data {
- obs_source_t *source;
- struct obs_lua_source *ls;
- int lua_data_ref;
- struct obs_lua_data *next;
- struct obs_lua_data **p_prev_next;
- };
- #define call_func(name, args, rets) call_func_(ls->script, ls->func_##name, args, rets, #name, ls->display_name)
- #define have_func(name) (ls->func_##name != LUA_REFNIL)
- #define ls_push_data() lua_rawgeti(ls->script, LUA_REGISTRYINDEX, ld->lua_data_ref)
- #define ls_pop(count) lua_pop(ls->script, count)
- #define lock_script() \
- struct obs_lua_script *__data = ls->data; \
- struct obs_lua_script *__prev_script = current_lua_script; \
- current_lua_script = __data; \
- pthread_mutex_lock(&__data->mutex);
- #define unlock_script() \
- pthread_mutex_unlock(&__data->mutex); \
- current_lua_script = __prev_script;
- static const char *obs_lua_source_get_name(void *type_data)
- {
- struct obs_lua_source *ls = type_data;
- return ls->display_name;
- }
- static void *obs_lua_source_create(obs_data_t *settings, obs_source_t *source)
- {
- struct obs_lua_source *ls = obs_source_get_type_data(source);
- struct obs_lua_data *data = NULL;
- pthread_mutex_lock(&ls->definition_mutex);
- if (!ls->script)
- goto fail;
- if (!have_func(create))
- goto fail;
- lock_script();
- ls_push_libobs_obj(obs_data_t, settings, false);
- ls_push_libobs_obj(obs_source_t, source, false);
- call_func(create, 2, 1);
- int lua_data_ref = luaL_ref(ls->script, LUA_REGISTRYINDEX);
- if (lua_data_ref != LUA_REFNIL) {
- data = bmalloc(sizeof(*data));
- data->source = source;
- data->ls = ls;
- data->lua_data_ref = lua_data_ref;
- }
- unlock_script();
- if (data) {
- struct obs_lua_data *next = ls->first_source;
- data->next = next;
- data->p_prev_next = &ls->first_source;
- if (next)
- next->p_prev_next = &data->next;
- ls->first_source = data;
- }
- fail:
- pthread_mutex_unlock(&ls->definition_mutex);
- return data;
- }
- static void call_destroy(struct obs_lua_data *ld)
- {
- struct obs_lua_source *ls = ld->ls;
- ls_push_data();
- call_func(destroy, 1, 0);
- luaL_unref(ls->script, LUA_REGISTRYINDEX, ld->lua_data_ref);
- ld->lua_data_ref = LUA_REFNIL;
- }
- static void obs_lua_source_destroy(void *data)
- {
- struct obs_lua_data *ld = data;
- struct obs_lua_source *ls = ld->ls;
- struct obs_lua_data *next;
- pthread_mutex_lock(&ls->definition_mutex);
- if (!ls->script)
- goto fail;
- if (!have_func(destroy))
- goto fail;
- lock_script();
- call_destroy(ld);
- unlock_script();
- fail:
- next = ld->next;
- *ld->p_prev_next = next;
- if (next)
- next->p_prev_next = ld->p_prev_next;
- bfree(data);
- pthread_mutex_unlock(&ls->definition_mutex);
- }
- static uint32_t obs_lua_source_get_width(void *data)
- {
- struct obs_lua_data *ld = data;
- struct obs_lua_source *ls = ld->ls;
- uint32_t width = 0;
- pthread_mutex_lock(&ls->definition_mutex);
- if (!ls->script)
- goto fail;
- if (!have_func(get_width))
- goto fail;
- lock_script();
- ls_push_data();
- if (call_func(get_width, 1, 1)) {
- width = (uint32_t)lua_tointeger(ls->script, -1);
- ls_pop(1);
- }
- unlock_script();
- fail:
- pthread_mutex_unlock(&ls->definition_mutex);
- return width;
- }
- static uint32_t obs_lua_source_get_height(void *data)
- {
- struct obs_lua_data *ld = data;
- struct obs_lua_source *ls = ld->ls;
- uint32_t height = 0;
- pthread_mutex_lock(&ls->definition_mutex);
- if (!ls->script)
- goto fail;
- if (!have_func(get_height))
- goto fail;
- lock_script();
- ls_push_data();
- if (call_func(get_height, 1, 1)) {
- height = (uint32_t)lua_tointeger(ls->script, -1);
- ls_pop(1);
- }
- unlock_script();
- fail:
- pthread_mutex_unlock(&ls->definition_mutex);
- return height;
- }
- static void obs_lua_source_get_defaults(void *type_data, obs_data_t *settings)
- {
- struct obs_lua_source *ls = type_data;
- pthread_mutex_lock(&ls->definition_mutex);
- if (!ls->script)
- goto fail;
- if (!have_func(get_defaults))
- goto fail;
- lock_script();
- ls_push_libobs_obj(obs_data_t, settings, false);
- call_func(get_defaults, 1, 0);
- unlock_script();
- fail:
- pthread_mutex_unlock(&ls->definition_mutex);
- }
- static obs_properties_t *obs_lua_source_get_properties(void *data)
- {
- struct obs_lua_data *ld = data;
- struct obs_lua_source *ls = ld->ls;
- obs_properties_t *props = NULL;
- pthread_mutex_lock(&ls->definition_mutex);
- if (!ls->script)
- goto fail;
- if (!have_func(get_properties))
- goto fail;
- lock_script();
- ls_push_data();
- if (call_func(get_properties, 1, 1)) {
- ls_get_libobs_obj(obs_properties_t, -1, &props);
- ls_pop(1);
- }
- unlock_script();
- fail:
- pthread_mutex_unlock(&ls->definition_mutex);
- return props;
- }
- static void obs_lua_source_update(void *data, obs_data_t *settings)
- {
- struct obs_lua_data *ld = data;
- struct obs_lua_source *ls = ld->ls;
- pthread_mutex_lock(&ls->definition_mutex);
- if (!ls->script)
- goto fail;
- if (!have_func(update))
- goto fail;
- lock_script();
- ls_push_data();
- ls_push_libobs_obj(obs_data_t, settings, false);
- call_func(update, 2, 0);
- unlock_script();
- fail:
- pthread_mutex_unlock(&ls->definition_mutex);
- }
- #define DEFINE_VOID_DATA_CALLBACK(name) \
- static void obs_lua_source_##name(void *data) \
- { \
- struct obs_lua_data *ld = data; \
- struct obs_lua_source *ls = ld->ls; \
- if (!have_func(name)) \
- return; \
- lock_script(); \
- ls_push_data(); \
- call_func(name, 1, 0); \
- unlock_script(); \
- }
- DEFINE_VOID_DATA_CALLBACK(activate)
- DEFINE_VOID_DATA_CALLBACK(deactivate)
- DEFINE_VOID_DATA_CALLBACK(show)
- DEFINE_VOID_DATA_CALLBACK(hide)
- #undef DEFINE_VOID_DATA_CALLBACK
- static void obs_lua_source_video_tick(void *data, float seconds)
- {
- struct obs_lua_data *ld = data;
- struct obs_lua_source *ls = ld->ls;
- pthread_mutex_lock(&ls->definition_mutex);
- if (!ls->script)
- goto fail;
- if (!have_func(video_tick))
- goto fail;
- lock_script();
- ls_push_data();
- lua_pushnumber(ls->script, (double)seconds);
- call_func(video_tick, 2, 0);
- unlock_script();
- fail:
- pthread_mutex_unlock(&ls->definition_mutex);
- }
- static void obs_lua_source_video_render(void *data, gs_effect_t *effect)
- {
- struct obs_lua_data *ld = data;
- struct obs_lua_source *ls = ld->ls;
- pthread_mutex_lock(&ls->definition_mutex);
- if (!ls->script)
- goto fail;
- if (!have_func(video_render))
- goto fail;
- lock_script();
- ls_push_data();
- ls_push_libobs_obj(gs_effect_t, effect, false);
- call_func(video_render, 2, 0);
- unlock_script();
- fail:
- pthread_mutex_unlock(&ls->definition_mutex);
- }
- static void obs_lua_source_save(void *data, obs_data_t *settings)
- {
- struct obs_lua_data *ld = data;
- struct obs_lua_source *ls = ld->ls;
- pthread_mutex_lock(&ls->definition_mutex);
- if (!ls->script)
- goto fail;
- if (!have_func(save))
- goto fail;
- lock_script();
- ls_push_data();
- ls_push_libobs_obj(obs_data_t, settings, false);
- call_func(save, 2, 0);
- unlock_script();
- fail:
- pthread_mutex_unlock(&ls->definition_mutex);
- }
- static void obs_lua_source_load(void *data, obs_data_t *settings)
- {
- struct obs_lua_data *ld = data;
- struct obs_lua_source *ls = ld->ls;
- pthread_mutex_lock(&ls->definition_mutex);
- if (!ls->script)
- goto fail;
- if (!have_func(load))
- goto fail;
- lock_script();
- ls_push_data();
- ls_push_libobs_obj(obs_data_t, settings, false);
- call_func(load, 2, 0);
- unlock_script();
- fail:
- pthread_mutex_unlock(&ls->definition_mutex);
- }
- static void source_type_unload(struct obs_lua_source *ls)
- {
- #define unref(name) \
- luaL_unref(ls->script, LUA_REGISTRYINDEX, name); \
- name = LUA_REFNIL
- unref(ls->func_create);
- unref(ls->func_destroy);
- unref(ls->func_get_width);
- unref(ls->func_get_height);
- unref(ls->func_get_defaults);
- unref(ls->func_get_properties);
- unref(ls->func_update);
- unref(ls->func_activate);
- unref(ls->func_deactivate);
- unref(ls->func_show);
- unref(ls->func_hide);
- unref(ls->func_video_tick);
- unref(ls->func_video_render);
- unref(ls->func_save);
- unref(ls->func_load);
- #undef unref
- }
- static void obs_lua_source_free_type_data(void *type_data)
- {
- struct obs_lua_source *ls = type_data;
- pthread_mutex_lock(&ls->definition_mutex);
- if (ls->script) {
- lock_script();
- source_type_unload(ls);
- unlock_script();
- ls->script = NULL;
- }
- pthread_mutex_unlock(&ls->definition_mutex);
- pthread_mutex_destroy(&ls->definition_mutex);
- bfree(ls);
- }
- EXPORT void obs_enable_source_type(const char *name, bool enable);
- static inline struct obs_lua_source *find_existing(const char *id)
- {
- struct obs_lua_source *existing = NULL;
- pthread_mutex_lock(&lua_source_def_mutex);
- struct obs_lua_source *ls = first_source_def;
- while (ls) {
- /* can compare pointers here due to string table */
- if (ls->id == id) {
- existing = ls;
- break;
- }
- ls = ls->next;
- }
- pthread_mutex_unlock(&lua_source_def_mutex);
- return existing;
- }
- static int obs_lua_register_source(lua_State *script)
- {
- struct obs_lua_source ls = {0};
- struct obs_lua_source *existing = NULL;
- struct obs_lua_source *v = NULL;
- struct obs_source_info info = {0};
- const char *id;
- if (!verify_args1(script, is_table))
- goto fail;
- id = get_table_string(script, -1, "id");
- if (!id || !*id)
- goto fail;
- /* redefinition */
- existing = find_existing(id);
- if (existing) {
- if (existing->script) {
- existing = NULL;
- goto fail;
- }
- pthread_mutex_lock(&existing->definition_mutex);
- }
- v = existing ? existing : &ls;
- v->script = script;
- v->id = id;
- info.id = v->id;
- info.type = (enum obs_source_type)get_table_int(script, -1, "type");
- info.output_flags = get_table_int(script, -1, "output_flags");
- lua_pushstring(script, "get_name");
- lua_gettable(script, -2);
- if (lua_pcall(script, 0, 1, 0) == 0) {
- v->display_name = cstrcache_get(lua_tostring(script, -1));
- lua_pop(script, 1);
- }
- if (!v->display_name || !*v->display_name || !*info.id || !info.output_flags)
- goto fail;
- #define get_callback(val) \
- do { \
- get_callback_from_table(script, -1, #val, &v->func_##val); \
- info.val = obs_lua_source_##val; \
- } while (false)
- get_callback(create);
- get_callback(destroy);
- get_callback(get_width);
- get_callback(get_height);
- get_callback(get_properties);
- get_callback(update);
- get_callback(activate);
- get_callback(deactivate);
- get_callback(show);
- get_callback(hide);
- get_callback(video_tick);
- get_callback(video_render);
- get_callback(save);
- get_callback(load);
- #undef get_callback
- get_callback_from_table(script, -1, "get_defaults", &v->func_get_defaults);
- if (!existing) {
- ls.data = current_lua_script;
- pthread_mutex_init_recursive(&ls.definition_mutex);
- info.type_data = bmemdup(&ls, sizeof(ls));
- info.free_type_data = obs_lua_source_free_type_data;
- info.get_name = obs_lua_source_get_name;
- info.get_defaults2 = obs_lua_source_get_defaults;
- obs_register_source(&info);
- pthread_mutex_lock(&lua_source_def_mutex);
- v = info.type_data;
- struct obs_lua_source *next = first_source_def;
- v->next = next;
- if (next)
- next->p_prev_next = &v->next;
- v->p_prev_next = &first_source_def;
- first_source_def = v;
- pthread_mutex_unlock(&lua_source_def_mutex);
- } else {
- existing->script = script;
- existing->data = current_lua_script;
- obs_enable_source_type(id, true);
- struct obs_lua_data *ld = v->first_source;
- while (ld) {
- struct obs_lua_source *ls = v;
- if (have_func(create)) {
- obs_source_t *source = ld->source;
- obs_data_t *settings = obs_source_get_settings(source);
- ls_push_libobs_obj(obs_data_t, settings, false);
- ls_push_libobs_obj(obs_source_t, source, false);
- call_func(create, 2, 1);
- ld->lua_data_ref = luaL_ref(ls->script, LUA_REGISTRYINDEX);
- obs_data_release(settings);
- }
- ld = ld->next;
- }
- }
- fail:
- if (existing) {
- pthread_mutex_unlock(&existing->definition_mutex);
- }
- return 0;
- }
- /* ========================================================================= */
- void add_lua_source_functions(lua_State *script)
- {
- lua_getglobal(script, "obslua");
- lua_pushstring(script, "obs_register_source");
- lua_pushcfunction(script, obs_lua_register_source);
- lua_rawset(script, -3);
- lua_pop(script, 1);
- }
- static inline void undef_source_type(struct obs_lua_script *data, struct obs_lua_source *ls)
- {
- pthread_mutex_lock(&ls->definition_mutex);
- pthread_mutex_lock(&data->mutex);
- obs_enable_source_type(ls->id, false);
- struct obs_lua_data *ld = ls->first_source;
- while (ld) {
- call_destroy(ld);
- ld = ld->next;
- }
- source_type_unload(ls);
- ls->script = NULL;
- pthread_mutex_unlock(&data->mutex);
- pthread_mutex_unlock(&ls->definition_mutex);
- }
- void undef_lua_script_sources(struct obs_lua_script *data)
- {
- pthread_mutex_lock(&lua_source_def_mutex);
- struct obs_lua_source *def = first_source_def;
- while (def) {
- if (def->script == data->script)
- undef_source_type(data, def);
- def = def->next;
- }
- pthread_mutex_unlock(&lua_source_def_mutex);
- }
|