obs-studio/deps/obs-scripting/obs-scripting-lua-source.c
jp9000 f53df7da64 clang-format: Apply formatting
Code submissions have continually suffered from formatting
inconsistencies that constantly have to be addressed.  Using
clang-format simplifies this by making code formatting more consistent,
and allows automation of the code formatting so that maintainers can
focus more on the code itself instead of code formatting.
2019-06-23 23:49:10 -07:00

743 lines
17 KiB
C

/******************************************************************************
Copyright (C) 2017 by Hugh Bailey <jim@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(&ls.definition_mutex, NULL);
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);
}