/* 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); }