Files
wireplumber/modules/module-lua-scripting/wplua/object.c
George Kiagiadakis 17a257ddbe wplua: move under modules/module-lua-scripting
It's unlikely that wplua will ever be useful outside the context
of module-lua-scripting, so let's move it to keep all the code in one place
2022-03-24 09:47:59 +02:00

247 lines
6.6 KiB
C

/* WirePlumber
*
* Copyright © 2020 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include "wplua.h"
#include "private.h"
#include <wp/wp.h>
static int
_wplua_gobject_call (lua_State *L)
{
GObject *obj = wplua_checkobject (L, 1, G_TYPE_OBJECT);
const char *sig_name = lua_tostring (L, 2);
guint n_params = lua_gettop (L) - 2;
GSignalQuery query;
guint sig_id = 0;
GQuark detail = 0;
if (G_UNLIKELY (!g_signal_parse_name (sig_name, G_TYPE_FROM_INSTANCE (obj),
&sig_id, &detail, FALSE)))
luaL_error (L, "unknown signal '%s::%s'", G_OBJECT_TYPE_NAME (obj),
sig_name);
g_signal_query (sig_id, &query);
if (G_UNLIKELY (!(query.signal_flags & G_SIGNAL_ACTION)))
luaL_error (L, "lua code is not allowed to emit non-action signal '%s::%s'",
G_OBJECT_TYPE_NAME (obj), sig_name);
if (G_UNLIKELY (query.n_params > n_params))
luaL_error (L, "not enough arguments for '%s::%s': expected %d, got %d",
G_OBJECT_TYPE_NAME (obj), sig_name, query.n_params, n_params);
GValue ret = G_VALUE_INIT;
GValue *vals = g_newa (GValue, n_params + 1);
memset (vals, 0, sizeof (GValue) * (n_params + 1));
if (query.return_type != G_TYPE_NONE)
g_value_init (&ret, query.return_type);
g_value_init_from_instance (&vals[0], obj);
for (guint i = 0; i < n_params; i++) {
g_value_init (&vals[i+1], query.param_types[i]);
wplua_lua_to_gvalue (L, i+3, &vals[i+1]);
}
g_signal_emitv (vals, sig_id, detail, &ret);
for (guint i = 0; i < n_params + 1; i++) {
g_value_unset (&vals[i]);
}
int n_ret = 0;
if (query.return_type != G_TYPE_NONE)
n_ret = wplua_gvalue_to_lua (L, &ret);
g_value_unset (&ret);
return n_ret;
}
static int
_wplua_gobject_connect (lua_State *L)
{
GObject *obj = wplua_checkobject (L, 1, G_TYPE_OBJECT);
const char *sig_name = luaL_checkstring (L, 2);
luaL_checktype (L, 3, LUA_TFUNCTION);
guint sig_id = 0;
GQuark detail = 0;
if (G_UNLIKELY (!g_signal_parse_name (sig_name, G_TYPE_FROM_INSTANCE (obj),
&sig_id, &detail, FALSE)))
luaL_error (L, "unknown signal '%s::%s'", G_OBJECT_TYPE_NAME (obj),
sig_name);
GClosure *closure = wplua_function_to_closure (L, 3);
gulong handler =
g_signal_connect_closure_by_id (obj, sig_id, detail, closure, FALSE);
lua_pushinteger (L, handler);
return 1;
}
static lua_CFunction
find_method_in_luaL_Reg (luaL_Reg *reg, const gchar *method)
{
if (reg) {
while (reg->name) {
if (!g_strcmp0 (method, reg->name))
return reg->func;
reg++;
}
}
return NULL;
}
static int
_wplua_gobject___index (lua_State *L)
{
GObject *obj = wplua_checkobject (L, 1, G_TYPE_OBJECT);
const gchar *key = luaL_checkstring (L, 2);
lua_CFunction func = NULL;
GHashTable *vtables;
lua_pushliteral (L, "wplua_vtables");
lua_gettable (L, LUA_REGISTRYINDEX);
vtables = wplua_toboxed (L, -1);
lua_pop (L, 1);
if (!g_strcmp0 (key, "call"))
func = _wplua_gobject_call;
else if (!g_strcmp0 (key, "connect"))
func = _wplua_gobject_connect;
/* search in registered vtables */
if (!func) {
GType type = G_TYPE_FROM_INSTANCE (obj);
while (!func && type) {
luaL_Reg *reg = g_hash_table_lookup (vtables, GUINT_TO_POINTER (type));
func = find_method_in_luaL_Reg (reg, key);
type = g_type_parent (type);
}
}
/* search in registered vtables of interfaces */
if (!func) {
g_autofree GType *interfaces =
g_type_interfaces (G_TYPE_FROM_INSTANCE (obj), NULL);
GType *type = interfaces;
while (!func && *type) {
luaL_Reg *reg = g_hash_table_lookup (vtables, GUINT_TO_POINTER (*type));
func = find_method_in_luaL_Reg (reg, key);
type++;
}
}
if (func) {
lua_pushcfunction (L, func);
return 1;
}
else {
/* search in properties */
GObjectClass *klass = G_OBJECT_GET_CLASS (obj);
GParamSpec *pspec = g_object_class_find_property (klass, key);
if (pspec && (pspec->flags & G_PARAM_READABLE)) {
g_auto (GValue) v = G_VALUE_INIT;
g_value_init (&v, pspec->value_type);
g_object_get_property (obj, key, &v);
return wplua_gvalue_to_lua (L, &v);
}
}
return 0;
}
static int
_wplua_gobject___newindex (lua_State *L)
{
GObject *obj = wplua_checkobject (L, 1, G_TYPE_OBJECT);
const gchar *key = luaL_checkstring (L, 2);
/* search in properties */
GObjectClass *klass = G_OBJECT_GET_CLASS (obj);
GParamSpec *pspec = g_object_class_find_property (klass, key);
if (pspec && (pspec->flags & G_PARAM_WRITABLE)) {
g_auto (GValue) v = G_VALUE_INIT;
g_value_init (&v, pspec->value_type);
wplua_lua_to_gvalue (L, 3, &v);
g_object_set_property (obj, key, &v);
} else {
luaL_error (L, "attempted to assign unknown or non-writable property '%s'",
key);
}
return 0;
}
static int
_wplua_gobject__tostring (lua_State *L)
{
GObject *obj;
gchar *str;
obj = wplua_checkobject (L, 1, G_TYPE_OBJECT);
str = g_strdup_printf (WP_OBJECT_FORMAT, WP_OBJECT_ARGS (obj));
lua_pushstring (L, str);
g_free (str);
return 1;
}
void
_wplua_init_gobject (lua_State *L)
{
static const luaL_Reg gobject_meta[] = {
{ "__gc", _wplua_gvalue_userdata___gc },
{ "__eq", _wplua_gvalue_userdata___eq },
{ "__index", _wplua_gobject___index },
{ "__newindex", _wplua_gobject___newindex },
{ "__tostring", _wplua_gobject__tostring },
{ NULL, NULL }
};
luaL_newmetatable (L, "GObject");
luaL_setfuncs (L, gobject_meta, 0);
lua_pop (L, 1);
}
void
wplua_pushobject (lua_State * L, gpointer object)
{
g_return_if_fail (G_IS_OBJECT (object));
GValue *v = _wplua_pushgvalue_userdata (L, G_TYPE_FROM_INSTANCE (object));
wp_trace_object (object, "pushing to Lua, v=%p", v);
g_value_take_object (v, object);
luaL_getmetatable (L, "GObject");
lua_setmetatable (L, -2);
}
gpointer
wplua_toobject (lua_State *L, int idx)
{
g_return_val_if_fail (_wplua_isgvalue_userdata (L, idx, G_TYPE_OBJECT), NULL);
return g_value_get_object ((GValue *) lua_touserdata (L, idx));
}
gpointer
wplua_checkobject (lua_State *L, int idx, GType type)
{
if (G_UNLIKELY (!_wplua_isgvalue_userdata (L, idx, type))) {
wp_critical ("expected userdata storing GValue<%s>", g_type_name (type));
luaL_argerror (L, idx, "expected userdata storing GValue<GObject>");
}
return g_value_get_object ((GValue *) lua_touserdata (L, idx));
}
gboolean
wplua_isobject (lua_State *L, int idx, GType type)
{
if (!g_type_is_a (type, G_TYPE_OBJECT)) return FALSE;
return _wplua_isgvalue_userdata (L, idx, type);
}