mirror of
https://gitlab.freedesktop.org/pipewire/wireplumber.git
synced 2026-05-05 14:48:02 +02:00
wplua: new simple engine to integrate GObject with Lua
This commit is contained in:
parent
404d016852
commit
7d692e0246
13 changed files with 1454 additions and 1 deletions
|
|
@ -1,2 +1,3 @@
|
|||
subdir('wptoml')
|
||||
subdir('wp')
|
||||
subdir('wplua')
|
||||
subdir('wptoml')
|
||||
|
|
|
|||
106
lib/wplua/boxed.c
Normal file
106
lib/wplua/boxed.c
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/* 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 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_gboxed___index (lua_State *L)
|
||||
{
|
||||
luaL_argcheck (L, wplua_isboxed (L, 1), 1,
|
||||
"expected userdata storing GValue<GBoxed>");
|
||||
GValue *obj_v = lua_touserdata (L, 1);
|
||||
const gchar *key = luaL_checkstring (L, 2);
|
||||
lua_CFunction func = NULL;
|
||||
GHashTable *vtables;
|
||||
|
||||
lua_getglobal (L, "__wplua_vtables");
|
||||
vtables = wplua_toboxed (L, -1);
|
||||
lua_pop (L, 1);
|
||||
|
||||
/* search in registered vtables */
|
||||
if (!func) {
|
||||
GType type = G_VALUE_TYPE (obj_v);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (func) {
|
||||
lua_pushcfunction (L, func);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
_wplua_init_gboxed (lua_State *L)
|
||||
{
|
||||
static const luaL_Reg gboxed_meta[] = {
|
||||
{ "__gc", _wplua_gvalue_userdata___gc },
|
||||
{ "__eq", _wplua_gvalue_userdata___eq },
|
||||
{ "__index", _wplua_gboxed___index },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
luaL_newmetatable (L, "GBoxed");
|
||||
luaL_setfuncs (L, gboxed_meta, 0);
|
||||
lua_pop (L, 1);
|
||||
}
|
||||
|
||||
void
|
||||
wplua_pushboxed (lua_State * L, GType type, gpointer object)
|
||||
{
|
||||
g_return_if_fail (G_TYPE_FUNDAMENTAL (type) == G_TYPE_BOXED);
|
||||
|
||||
GValue *v = _wplua_pushgvalue_userdata (L, type);
|
||||
wp_trace_boxed (type, object, "pushing to Lua, v=%p", v);
|
||||
g_value_take_boxed (v, object);
|
||||
|
||||
luaL_getmetatable (L, "GBoxed");
|
||||
lua_setmetatable (L, -2);
|
||||
}
|
||||
|
||||
gpointer
|
||||
wplua_toboxed (lua_State *L, int idx)
|
||||
{
|
||||
g_return_val_if_fail (_wplua_isgvalue_userdata (L, idx, G_TYPE_BOXED), NULL);
|
||||
return g_value_get_boxed ((GValue *) lua_touserdata (L, idx));
|
||||
}
|
||||
|
||||
gpointer
|
||||
wplua_checkboxed (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<GBoxed>");
|
||||
}
|
||||
return g_value_get_boxed ((GValue *) lua_touserdata (L, idx));
|
||||
}
|
||||
|
||||
gboolean
|
||||
wplua_isboxed (lua_State *L, int idx)
|
||||
{
|
||||
return _wplua_isgvalue_userdata (L, idx, G_TYPE_BOXED);
|
||||
}
|
||||
154
lib/wplua/closure.c
Normal file
154
lib/wplua/closure.c
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2020 Collabora Ltd.
|
||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "wplua.h"
|
||||
#include <wp/wp.h>
|
||||
|
||||
typedef struct _WpLuaClosure WpLuaClosure;
|
||||
struct _WpLuaClosure
|
||||
{
|
||||
GClosure closure;
|
||||
int func_ref;
|
||||
};
|
||||
|
||||
static int
|
||||
_wplua_closure_errhandler (lua_State *L)
|
||||
{
|
||||
wp_warning ("%s", lua_tostring (L, -1));
|
||||
lua_pop (L, 1);
|
||||
|
||||
luaL_traceback (L, L, "traceback:\n", 1);
|
||||
wp_warning ("%s", lua_tostring (L, -1));
|
||||
lua_pop (L, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
_wplua_closure_pcall (lua_State *L, int nargs, int nret)
|
||||
{
|
||||
int hpos = lua_gettop (L) - nargs;
|
||||
int ret = LUA_OK;
|
||||
|
||||
lua_pushcfunction (L, _wplua_closure_errhandler);
|
||||
lua_insert (L, hpos);
|
||||
|
||||
ret = lua_pcall (L, nargs, nret, hpos);
|
||||
switch (ret) {
|
||||
case LUA_ERRMEM:
|
||||
wp_critical ("not enough memory");
|
||||
break;
|
||||
case LUA_ERRERR:
|
||||
wp_critical ("error running the message handler");
|
||||
break;
|
||||
case LUA_ERRGCMM:
|
||||
wp_critical ("error running __gc");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
lua_remove (L, hpos);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
_wplua_closure_marshal (GClosure *closure, GValue *return_value,
|
||||
guint n_param_values, const GValue *param_values,
|
||||
gpointer invocation_hint, gpointer marshal_data)
|
||||
{
|
||||
lua_State *L = closure->data;
|
||||
int func_ref = ((WpLuaClosure *) closure)->func_ref;
|
||||
|
||||
/* invalid closure, skip it */
|
||||
if (func_ref == LUA_NOREF || func_ref == LUA_REFNIL)
|
||||
return;
|
||||
|
||||
/* clear the stack and stop the garbage collector for now */
|
||||
lua_settop (L, 0);
|
||||
lua_gc (L, LUA_GCSTOP, 0);
|
||||
|
||||
/* push the function */
|
||||
lua_rawgeti (L, LUA_REGISTRYINDEX, func_ref);
|
||||
|
||||
/* push arguments */
|
||||
for (guint i = 0; i < n_param_values; i++)
|
||||
wplua_gvalue_to_lua (L, ¶m_values[i]);
|
||||
|
||||
/* call in protected mode */
|
||||
int res = _wplua_closure_pcall (L, n_param_values, return_value ? 1 : 0);
|
||||
|
||||
/* handle the result */
|
||||
if (res == LUA_OK && return_value && lua_gettop (L) >= 1)
|
||||
wplua_lua_to_gvalue (L, 1, return_value);
|
||||
|
||||
/* clear the stack and clean up */
|
||||
lua_settop (L, 0);
|
||||
lua_gc (L, LUA_GCCOLLECT, 0);
|
||||
lua_gc (L, LUA_GCRESTART, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
_wplua_closure_invalidate (lua_State *L, WpLuaClosure *c)
|
||||
{
|
||||
wp_trace_boxed (G_TYPE_CLOSURE, c, "invalidated");
|
||||
luaL_unref (L, LUA_REGISTRYINDEX, c->func_ref);
|
||||
c->func_ref = LUA_NOREF;
|
||||
}
|
||||
|
||||
/**
|
||||
* wplua_function_to_closure:
|
||||
*
|
||||
* Make a GClosure out of a Lua function at index @idx
|
||||
*
|
||||
* Returns: (transfer floating): the new closure
|
||||
*/
|
||||
GClosure *
|
||||
wplua_function_to_closure (lua_State *L, int idx)
|
||||
{
|
||||
g_return_val_if_fail (lua_isfunction(L, idx), NULL);
|
||||
|
||||
GClosure *c = g_closure_new_simple (sizeof (WpLuaClosure), L);
|
||||
WpLuaClosure *wlc = (WpLuaClosure *) c;
|
||||
GPtrArray *closures;
|
||||
|
||||
lua_getglobal (L, "__wplua_closures");
|
||||
closures = wplua_toboxed (L, -1);
|
||||
lua_pop (L, 1);
|
||||
|
||||
lua_pushvalue (L, idx);
|
||||
wlc->func_ref = luaL_ref (L, LUA_REGISTRYINDEX);
|
||||
|
||||
wp_trace_boxed (G_TYPE_CLOSURE, c, "created, func_ref = %d", wlc->func_ref);
|
||||
|
||||
g_closure_set_marshal (c, _wplua_closure_marshal);
|
||||
g_closure_add_invalidate_notifier (c, L,
|
||||
(GClosureNotify) _wplua_closure_invalidate);
|
||||
|
||||
/* keep a ref in lua, so that we can invalidate
|
||||
the closure when lua_State closes */
|
||||
g_ptr_array_add (closures, g_closure_ref (c));
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static void
|
||||
_wplua_closure_destroy (GClosure * c)
|
||||
{
|
||||
g_closure_invalidate (c);
|
||||
g_closure_unref (c);
|
||||
}
|
||||
|
||||
void
|
||||
_wplua_init_closure (lua_State *L)
|
||||
{
|
||||
GPtrArray *a = g_ptr_array_new_with_free_func (
|
||||
(GDestroyNotify) _wplua_closure_destroy);
|
||||
wplua_pushboxed (L, G_TYPE_PTR_ARRAY, a);
|
||||
lua_setglobal (L, "__wplua_closures");
|
||||
}
|
||||
26
lib/wplua/meson.build
Normal file
26
lib/wplua/meson.build
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
wplua_lib_sources = [
|
||||
'boxed.c',
|
||||
'closure.c',
|
||||
'object.c',
|
||||
'userdata.c',
|
||||
'value.c',
|
||||
'wplua.c',
|
||||
]
|
||||
|
||||
wplua_lib = static_library('wplua-' + wireplumber_api_version,
|
||||
wplua_lib_sources,
|
||||
c_args : [
|
||||
'-D_GNU_SOURCE',
|
||||
'-DG_LOG_USE_STRUCTURED',
|
||||
'-DG_LOG_DOMAIN="wplua"',
|
||||
],
|
||||
install: false,
|
||||
include_directories: wp_lib_include_dir,
|
||||
dependencies : [wp_dep, lua_dep],
|
||||
)
|
||||
|
||||
wplua_dep = declare_dependency(
|
||||
link_with: wplua_lib,
|
||||
include_directories: wp_lib_include_dir,
|
||||
dependencies: [wp_dep, lua_dep],
|
||||
)
|
||||
224
lib/wplua/object.c
Normal file
224
lib/wplua/object.c
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
/* 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);
|
||||
|
||||
if (query.return_type != G_TYPE_NONE)
|
||||
return wplua_gvalue_to_lua (L, &ret);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_getglobal (L, "__wplua_vtables");
|
||||
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;
|
||||
}
|
||||
|
||||
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 },
|
||||
{ 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)
|
||||
{
|
||||
return _wplua_isgvalue_userdata (L, idx, G_TYPE_OBJECT);
|
||||
}
|
||||
34
lib/wplua/private.h
Normal file
34
lib/wplua/private.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2020 Collabora Ltd.
|
||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef __WPLUA_PRIVATE_H__
|
||||
#define __WPLUA_PRIVATE_H__
|
||||
|
||||
#include "wplua.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* boxed.c */
|
||||
void _wplua_init_gboxed (lua_State *L);
|
||||
|
||||
/* closure.c */
|
||||
void _wplua_init_closure (lua_State *L);
|
||||
|
||||
/* object.c */
|
||||
void _wplua_init_gobject (lua_State *L);
|
||||
|
||||
/* userdata.c */
|
||||
GValue * _wplua_pushgvalue_userdata (lua_State * L, GType type);
|
||||
gboolean _wplua_isgvalue_userdata (lua_State *L, int idx, GType type);
|
||||
|
||||
int _wplua_gvalue_userdata___gc (lua_State *L);
|
||||
int _wplua_gvalue_userdata___eq (lua_State *L);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
63
lib/wplua/userdata.c
Normal file
63
lib/wplua/userdata.c
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/* 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>
|
||||
|
||||
GValue *
|
||||
_wplua_pushgvalue_userdata (lua_State * L, GType type)
|
||||
{
|
||||
GValue *v = lua_newuserdata (L, sizeof (GValue));
|
||||
memset (v, 0, sizeof (GValue));
|
||||
g_value_init (v, type);
|
||||
return v;
|
||||
}
|
||||
|
||||
gboolean
|
||||
_wplua_isgvalue_userdata (lua_State *L, int idx, GType type)
|
||||
{
|
||||
GValue *v;
|
||||
|
||||
if (!lua_isuserdata (L, idx))
|
||||
return FALSE;
|
||||
if (lua_rawlen (L, idx) != sizeof (GValue))
|
||||
return FALSE;
|
||||
if (!(v = lua_touserdata (L, idx)))
|
||||
return FALSE;
|
||||
if (type != G_TYPE_NONE && !g_type_is_a (G_VALUE_TYPE (v), type))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
_wplua_gvalue_userdata___gc (lua_State *L)
|
||||
{
|
||||
GValue *v = lua_touserdata (L, 1);
|
||||
wp_trace_boxed (G_VALUE_TYPE (v), g_value_peek_pointer (v),
|
||||
"collected, v=%p", v);
|
||||
g_value_unset (v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_wplua_gvalue_userdata___eq (lua_State *L)
|
||||
{
|
||||
if (_wplua_isgvalue_userdata (L, 1, G_TYPE_NONE) &&
|
||||
_wplua_isgvalue_userdata (L, 2, G_TYPE_NONE)) {
|
||||
GValue *v1 = lua_touserdata (L, 1);
|
||||
GValue *v2 = lua_touserdata (L, 2);
|
||||
gpointer p1 = g_value_peek_pointer (v1);
|
||||
gpointer p2 = g_value_peek_pointer (v2);
|
||||
lua_pushboolean (L, (p1 == p2));
|
||||
} else {
|
||||
lua_pushboolean (L, FALSE);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
161
lib/wplua/value.c
Normal file
161
lib/wplua/value.c
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2020 Collabora Ltd.
|
||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "wplua.h"
|
||||
#include "private.h"
|
||||
|
||||
void
|
||||
wplua_lua_to_gvalue (lua_State *L, int idx, GValue *v)
|
||||
{
|
||||
switch (g_type_fundamental (G_VALUE_TYPE (v))) {
|
||||
case G_TYPE_CHAR:
|
||||
if (lua_type (L, idx) == LUA_TSTRING)
|
||||
g_value_set_schar (v, *lua_tostring (L, idx));
|
||||
else
|
||||
g_value_set_schar (v, lua_tointeger (L, idx));
|
||||
break;
|
||||
case G_TYPE_UCHAR:
|
||||
g_value_set_uchar (v, lua_tointeger (L, idx));
|
||||
break;
|
||||
case G_TYPE_INT:
|
||||
g_value_set_int (v, lua_tointeger (L, idx));
|
||||
break;
|
||||
case G_TYPE_UINT:
|
||||
g_value_set_uint (v, lua_tointeger (L, idx));
|
||||
break;
|
||||
case G_TYPE_LONG:
|
||||
g_value_set_long (v, lua_tointeger (L, idx));
|
||||
break;
|
||||
case G_TYPE_ULONG:
|
||||
g_value_set_ulong (v, lua_tointeger (L, idx));
|
||||
break;
|
||||
case G_TYPE_INT64:
|
||||
g_value_set_int64 (v, lua_tointeger (L, idx));
|
||||
break;
|
||||
case G_TYPE_UINT64:
|
||||
g_value_set_uint64 (v, lua_tonumber (L, idx));
|
||||
break;
|
||||
case G_TYPE_FLOAT:
|
||||
g_value_set_float (v, lua_tonumber (L, idx));
|
||||
break;
|
||||
case G_TYPE_DOUBLE:
|
||||
g_value_set_double (v, lua_tonumber (L, idx));
|
||||
break;
|
||||
case G_TYPE_BOOLEAN:
|
||||
g_value_set_boolean (v, lua_toboolean (L, idx));
|
||||
break;
|
||||
case G_TYPE_STRING:
|
||||
g_value_set_string (v, lua_tostring (L, idx));
|
||||
break;
|
||||
case G_TYPE_POINTER:
|
||||
if (lua_type (L, idx) == LUA_TLIGHTUSERDATA)
|
||||
g_value_set_pointer (v, lua_touserdata (L, idx));
|
||||
break;
|
||||
case G_TYPE_BOXED:
|
||||
if (_wplua_isgvalue_userdata (L, idx, G_VALUE_TYPE (v)))
|
||||
g_value_set_boxed (v, wplua_toboxed (L, idx));
|
||||
break;
|
||||
case G_TYPE_OBJECT:
|
||||
case G_TYPE_INTERFACE:
|
||||
if (_wplua_isgvalue_userdata (L, idx, G_VALUE_TYPE (v)))
|
||||
g_value_set_object (v, wplua_toobject (L, idx));
|
||||
break;
|
||||
case G_TYPE_ENUM:
|
||||
if (lua_type (L, idx) == LUA_TSTRING) {
|
||||
GEnumClass *klass = g_type_class_peek (G_VALUE_TYPE (v));
|
||||
GEnumValue *value = g_enum_get_value_by_nick (klass, lua_tostring (L, idx));
|
||||
if (value)
|
||||
g_value_set_enum (v, value->value);
|
||||
} else {
|
||||
g_value_set_enum (v, lua_tointeger (L, idx));
|
||||
}
|
||||
break;
|
||||
case G_TYPE_FLAGS:
|
||||
g_value_set_flags (v, lua_tointeger (L, idx));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
wplua_gvalue_to_lua (lua_State *L, const GValue *v)
|
||||
{
|
||||
switch (g_type_fundamental (G_VALUE_TYPE (v))) {
|
||||
case G_TYPE_CHAR:
|
||||
lua_pushinteger (L, g_value_get_schar (v));
|
||||
break;
|
||||
case G_TYPE_UCHAR:
|
||||
lua_pushinteger (L, g_value_get_uchar (v));
|
||||
break;
|
||||
case G_TYPE_INT:
|
||||
lua_pushinteger (L, g_value_get_int (v));
|
||||
break;
|
||||
case G_TYPE_UINT:
|
||||
lua_pushinteger (L, g_value_get_uint (v));
|
||||
break;
|
||||
case G_TYPE_LONG:
|
||||
lua_pushinteger (L, g_value_get_long (v));
|
||||
break;
|
||||
case G_TYPE_ULONG:
|
||||
lua_pushinteger (L, g_value_get_ulong (v));
|
||||
break;
|
||||
case G_TYPE_INT64:
|
||||
lua_pushinteger (L, g_value_get_int64 (v));
|
||||
break;
|
||||
case G_TYPE_UINT64:
|
||||
lua_pushnumber (L, g_value_get_uint64 (v));
|
||||
break;
|
||||
case G_TYPE_FLOAT:
|
||||
lua_pushnumber (L, g_value_get_float (v));
|
||||
break;
|
||||
case G_TYPE_DOUBLE:
|
||||
lua_pushnumber (L, g_value_get_double (v));
|
||||
break;
|
||||
case G_TYPE_BOOLEAN:
|
||||
lua_pushboolean (L, g_value_get_boolean (v));
|
||||
break;
|
||||
case G_TYPE_STRING:
|
||||
lua_pushstring (L, g_value_get_string (v));
|
||||
break;
|
||||
case G_TYPE_POINTER:
|
||||
lua_pushlightuserdata (L, g_value_get_pointer (v));
|
||||
break;
|
||||
case G_TYPE_BOXED:
|
||||
wplua_pushboxed (L, G_VALUE_TYPE (v), g_value_dup_boxed (v));
|
||||
break;
|
||||
case G_TYPE_OBJECT:
|
||||
case G_TYPE_INTERFACE:
|
||||
wplua_pushobject (L, g_value_dup_object (v));
|
||||
break;
|
||||
case G_TYPE_ENUM: {
|
||||
GEnumClass *klass = g_type_class_peek (G_VALUE_TYPE (v));
|
||||
GEnumValue *value = g_enum_get_value (klass, g_value_get_enum (v));
|
||||
if (value)
|
||||
lua_pushstring (L, value->value_nick);
|
||||
else
|
||||
lua_pushinteger (L, g_value_get_enum (v));
|
||||
break;
|
||||
}
|
||||
case G_TYPE_FLAGS:
|
||||
/* FIXME: push as userdata with methods */
|
||||
lua_pushinteger (L, g_value_get_flags (v));
|
||||
break;
|
||||
case G_TYPE_PARAM: {
|
||||
GParamSpec *pspec = g_value_get_param (v);
|
||||
lua_pushstring (L, pspec->name);
|
||||
break;
|
||||
}
|
||||
case G_TYPE_VARIANT:
|
||||
default:
|
||||
/* FIXME implement */
|
||||
lua_pushnil (L);
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
201
lib/wplua/wplua.c
Normal file
201
lib/wplua/wplua.c
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
/* 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 void
|
||||
_wplua_openlibs (lua_State *L)
|
||||
{
|
||||
/* http://www.lua.org/manual/5.3/manual.html#luaL_requiref
|
||||
* http://www.lua.org/source/5.3/linit.c.html */
|
||||
static const luaL_Reg loadedlibs[] = {
|
||||
{"_G", luaopen_base},
|
||||
/* {LUA_LOADLIBNAME, luaopen_package}, */
|
||||
/* {LUA_COLIBNAME, luaopen_coroutine}, */
|
||||
{LUA_TABLIBNAME, luaopen_table},
|
||||
/* {LUA_IOLIBNAME, luaopen_io}, */
|
||||
/* {LUA_OSLIBNAME, luaopen_os}, */
|
||||
{LUA_STRLIBNAME, luaopen_string},
|
||||
{LUA_MATHLIBNAME, luaopen_math},
|
||||
{LUA_UTF8LIBNAME, luaopen_utf8},
|
||||
{LUA_DBLIBNAME, luaopen_debug},
|
||||
{NULL, NULL}
|
||||
};
|
||||
const luaL_Reg *lib;
|
||||
|
||||
for (lib = loadedlibs; lib->func; lib++) {
|
||||
luaL_requiref (L, lib->name, lib->func, 1);
|
||||
lua_pop (L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
_wplua_typeclass___call (lua_State *L)
|
||||
{
|
||||
luaL_checktype (L, 1, LUA_TTABLE);
|
||||
lua_pushliteral (L, "new");
|
||||
if (lua_rawget (L, 1) != LUA_TFUNCTION) {
|
||||
luaL_error (L, "class has no constructor");
|
||||
return 0;
|
||||
}
|
||||
lua_replace (L, 1);
|
||||
lua_call (L, lua_gettop (L) - 1, LUA_MULTRET);
|
||||
return lua_gettop (L);
|
||||
}
|
||||
|
||||
lua_State *
|
||||
wplua_new (void)
|
||||
{
|
||||
lua_State *L = luaL_newstate ();
|
||||
|
||||
wp_debug ("initializing lua_State %p", L);
|
||||
|
||||
_wplua_openlibs (L);
|
||||
_wplua_init_gboxed (L);
|
||||
_wplua_init_gobject (L);
|
||||
_wplua_init_closure (L);
|
||||
|
||||
{
|
||||
static const luaL_Reg typeclass_meta[] = {
|
||||
{ "__call", _wplua_typeclass___call },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
luaL_newmetatable (L, "TypeClass");
|
||||
luaL_setfuncs (L, typeclass_meta, 0);
|
||||
lua_pop (L, 1);
|
||||
}
|
||||
|
||||
{
|
||||
GHashTable *t = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||||
wplua_pushboxed (L, G_TYPE_HASH_TABLE, t);
|
||||
lua_setglobal (L, "__wplua_vtables");
|
||||
}
|
||||
|
||||
return L;
|
||||
}
|
||||
|
||||
void
|
||||
wplua_free (lua_State * L)
|
||||
{
|
||||
wp_debug ("closing lua_State %p", L);
|
||||
lua_close (L);
|
||||
}
|
||||
|
||||
void
|
||||
wplua_register_type_methods (lua_State * L, GType type,
|
||||
lua_CFunction constructor, const luaL_Reg * methods)
|
||||
{
|
||||
g_return_if_fail (L != NULL);
|
||||
g_return_if_fail (G_TYPE_FUNDAMENTAL (type) == G_TYPE_OBJECT ||
|
||||
G_TYPE_FUNDAMENTAL (type) == G_TYPE_BOXED);
|
||||
|
||||
/* register methods */
|
||||
if (methods) {
|
||||
GHashTable *vtables;
|
||||
|
||||
lua_getglobal (L, "__wplua_vtables");
|
||||
vtables = wplua_toboxed (L, -1);
|
||||
lua_pop (L, 1);
|
||||
|
||||
wp_debug ("Registering methods for '%s'", g_type_name (type));
|
||||
|
||||
if (G_UNLIKELY (g_hash_table_contains (vtables, GUINT_TO_POINTER (type)))) {
|
||||
wp_critical ("type '%s' was already registered", g_type_name (type));
|
||||
return;
|
||||
}
|
||||
|
||||
g_hash_table_insert (vtables, GUINT_TO_POINTER (type), (gpointer) methods);
|
||||
}
|
||||
|
||||
/* register constructor */
|
||||
if (constructor) {
|
||||
wp_debug ("Registering class for '%s'", g_type_name (type));
|
||||
|
||||
lua_newtable (L);
|
||||
luaL_setmetatable (L, "TypeClass");
|
||||
lua_pushliteral (L, "new");
|
||||
lua_pushcfunction (L, constructor);
|
||||
lua_settable (L, -3);
|
||||
lua_setglobal (L, g_type_name (type));
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
_wplua_load_buffer (lua_State * L, const gchar *buf, gsize size,
|
||||
const gchar * name, GError **error)
|
||||
{
|
||||
int ret;ret = luaL_loadbuffer (L, buf, size, name);
|
||||
if (ret != LUA_OK) {
|
||||
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
"Failed to compile: %s", lua_tostring (L, -1));
|
||||
lua_pop (L, 1);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ret = lua_pcall (L, 0, 0, 0);
|
||||
if (ret != LUA_OK) {
|
||||
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
"Failed to run: %s", lua_tostring (L, -1));
|
||||
lua_pop (L, 1);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
wplua_load_buffer (lua_State * L, const gchar *buf, gsize size, GError **error)
|
||||
{
|
||||
g_return_val_if_fail (L != NULL, FALSE);
|
||||
g_return_val_if_fail (buf != NULL, FALSE);
|
||||
g_return_val_if_fail (size != 0, FALSE);
|
||||
|
||||
g_autofree gchar *name =
|
||||
g_strdup_printf ("buffer@%p;size=%" G_GSIZE_FORMAT, buf, size);
|
||||
return _wplua_load_buffer (L, buf, size, name, error);
|
||||
}
|
||||
|
||||
gboolean
|
||||
wplua_load_uri (lua_State * L, const gchar *uri, GError **error)
|
||||
{
|
||||
g_autoptr (GFile) file = NULL;
|
||||
g_autoptr (GBytes) bytes = NULL;
|
||||
g_autoptr (GError) err = NULL;
|
||||
gconstpointer data;
|
||||
gsize size;
|
||||
|
||||
g_return_val_if_fail (L != NULL, FALSE);
|
||||
g_return_val_if_fail (uri != NULL, FALSE);
|
||||
|
||||
file = g_file_new_for_uri (uri);
|
||||
if (!(bytes = g_file_load_bytes (file, NULL, NULL, &err))) {
|
||||
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
"Failed to load '%s': %s", uri, err->message);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
data = g_bytes_get_data (bytes, &size);
|
||||
return _wplua_load_buffer (L, data, size, uri, error);
|
||||
}
|
||||
|
||||
gboolean
|
||||
wplua_load_path (lua_State * L, const gchar *path, GError **error)
|
||||
{
|
||||
g_autofree gchar *uri = NULL;
|
||||
|
||||
g_return_val_if_fail (L != NULL, FALSE);
|
||||
g_return_val_if_fail (path != NULL, FALSE);
|
||||
|
||||
if (!(uri = g_filename_to_uri (path, NULL, error)))
|
||||
return FALSE;
|
||||
|
||||
return wplua_load_uri (L, uri, error);
|
||||
}
|
||||
50
lib/wplua/wplua.h
Normal file
50
lib/wplua/wplua.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2020 Collabora Ltd.
|
||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef __WPLUA_H__
|
||||
#define __WPLUA_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
lua_State * wplua_new (void);
|
||||
void wplua_free (lua_State * L);
|
||||
|
||||
void wplua_register_type_methods (lua_State * L, GType type,
|
||||
lua_CFunction constructor, const luaL_Reg * methods);
|
||||
|
||||
/* push -> transfer full; get -> transfer none */
|
||||
void wplua_pushobject (lua_State * L, gpointer object);
|
||||
gpointer wplua_toobject (lua_State *L, int idx);
|
||||
gpointer wplua_checkobject (lua_State *L, int idx, GType type);
|
||||
gboolean wplua_isobject (lua_State *L, int idx);
|
||||
|
||||
/* push -> transfer full; get -> transfer none */
|
||||
void wplua_pushboxed (lua_State * L, GType type, gpointer object);
|
||||
gpointer wplua_toboxed (lua_State *L, int idx);
|
||||
gpointer wplua_checkboxed (lua_State *L, int idx, GType type);
|
||||
gboolean wplua_isboxed (lua_State *L, int idx);
|
||||
|
||||
/* transfer floating */
|
||||
GClosure * wplua_function_to_closure (lua_State *L, int idx);
|
||||
void wplua_lua_to_gvalue (lua_State *L, int idx, GValue *v);
|
||||
int wplua_gvalue_to_lua (lua_State *L, const GValue *v);
|
||||
|
||||
gboolean wplua_load_buffer (lua_State * L, const gchar *buf, gsize size,
|
||||
GError **error);
|
||||
gboolean wplua_load_uri (lua_State * L, const gchar *uri, GError **error);
|
||||
gboolean wplua_load_path (lua_State * L, const gchar *path, GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
subdir('wp')
|
||||
subdir('wplua')
|
||||
subdir('wptoml')
|
||||
subdir('modules')
|
||||
subdir('examples')
|
||||
|
|
|
|||
13
tests/wplua/meson.build
Normal file
13
tests/wplua/meson.build
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
common_deps = [wplua_dep]
|
||||
common_env = [
|
||||
'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()),
|
||||
'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()),
|
||||
'WIREPLUMBER_DEBUG=7',
|
||||
]
|
||||
|
||||
test(
|
||||
'test-wplua',
|
||||
executable('test-toml', 'wplua.c', dependencies: common_deps),
|
||||
env: common_env,
|
||||
workdir : meson.current_source_dir(),
|
||||
)
|
||||
419
tests/wplua/wplua.c
Normal file
419
tests/wplua/wplua.c
Normal file
|
|
@ -0,0 +1,419 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2020 Collabora Ltd.
|
||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "lua.h"
|
||||
#include <wplua/wplua.h>
|
||||
#include <wp/wp.h>
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_TEST_STRING,
|
||||
PROP_TEST_INT,
|
||||
PROP_TEST_UINT,
|
||||
PROP_TEST_INT64,
|
||||
PROP_TEST_UINT64,
|
||||
PROP_TEST_FLOAT,
|
||||
PROP_TEST_DOUBLE,
|
||||
PROP_TEST_BOOLEAN,
|
||||
};
|
||||
|
||||
typedef struct _TestObject TestObject;
|
||||
struct _TestObject
|
||||
{
|
||||
GObject parent;
|
||||
gchar *test_string;
|
||||
gint test_int;
|
||||
guint test_uint;
|
||||
gint64 test_int64;
|
||||
guint64 test_uint64;
|
||||
gfloat test_float;
|
||||
gdouble test_double;
|
||||
gboolean test_boolean;
|
||||
};
|
||||
|
||||
typedef struct _TestObjectClass TestObjectClass;
|
||||
struct _TestObjectClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (*change) (TestObject * self, const gchar * str, gint integer);
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT)
|
||||
|
||||
#define TEST_TYPE_OBJECT (test_object_get_type ())
|
||||
_GLIB_DEFINE_AUTOPTR_CHAINUP (TestObject, GObject)
|
||||
|
||||
static inline TestObject * TEST_OBJECT (gpointer ptr) {
|
||||
return G_TYPE_CHECK_INSTANCE_CAST (ptr, TEST_TYPE_OBJECT, TestObject);
|
||||
}
|
||||
|
||||
static void
|
||||
test_object_init (TestObject * self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
test_object_finalize (GObject * object)
|
||||
{
|
||||
TestObject *self = TEST_OBJECT (object);
|
||||
g_free (self->test_string);
|
||||
G_OBJECT_CLASS (test_object_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
test_object_get_property (GObject * object, guint id, GValue * value,
|
||||
GParamSpec * pspec)
|
||||
{
|
||||
TestObject *self = TEST_OBJECT (object);
|
||||
|
||||
switch (id) {
|
||||
case PROP_TEST_STRING:
|
||||
g_value_set_string (value, self->test_string);
|
||||
break;
|
||||
case PROP_TEST_INT:
|
||||
g_value_set_int (value, self->test_int);
|
||||
break;
|
||||
case PROP_TEST_UINT:
|
||||
g_value_set_uint (value, self->test_uint);
|
||||
break;
|
||||
case PROP_TEST_INT64:
|
||||
g_value_set_int64 (value, self->test_int64);
|
||||
break;
|
||||
case PROP_TEST_UINT64:
|
||||
g_value_set_uint64 (value, self->test_uint64);
|
||||
break;
|
||||
case PROP_TEST_FLOAT:
|
||||
g_value_set_float (value, self->test_float);
|
||||
break;
|
||||
case PROP_TEST_DOUBLE:
|
||||
g_value_set_double (value, self->test_double);
|
||||
break;
|
||||
case PROP_TEST_BOOLEAN:
|
||||
g_value_set_boolean (value, self->test_boolean);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_object_set_property (GObject * object, guint id, const GValue * value,
|
||||
GParamSpec * pspec)
|
||||
{
|
||||
TestObject *self = TEST_OBJECT (object);
|
||||
|
||||
switch (id) {
|
||||
case PROP_TEST_STRING:
|
||||
g_free (self->test_string);
|
||||
self->test_string = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_TEST_INT:
|
||||
self->test_int = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_TEST_UINT:
|
||||
self->test_uint = g_value_get_uint (value);
|
||||
break;
|
||||
case PROP_TEST_INT64:
|
||||
self->test_int64 = g_value_get_int64 (value);
|
||||
break;
|
||||
case PROP_TEST_UINT64:
|
||||
self->test_uint64 = g_value_get_uint64 (value);
|
||||
break;
|
||||
case PROP_TEST_FLOAT:
|
||||
self->test_float = g_value_get_float (value);
|
||||
break;
|
||||
case PROP_TEST_DOUBLE:
|
||||
self->test_double = g_value_get_double (value);
|
||||
break;
|
||||
case PROP_TEST_BOOLEAN:
|
||||
self->test_boolean = g_value_get_boolean (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_object_change (TestObject * self, const gchar * str, gint integer)
|
||||
{
|
||||
g_free (self->test_string);
|
||||
self->test_string = g_strdup_printf ("changed: %s", str);
|
||||
g_object_notify (G_OBJECT (self), "test-string");
|
||||
self->test_int = integer;
|
||||
g_object_notify (G_OBJECT (self), "test-int");
|
||||
|
||||
gint ret = 0;
|
||||
g_signal_emit_by_name (self, "acquire", &ret);
|
||||
self->test_int64 = ret;
|
||||
g_object_notify (G_OBJECT (self), "test-int64");
|
||||
}
|
||||
|
||||
static void
|
||||
test_object_class_init (TestObjectClass * klass)
|
||||
{
|
||||
GObjectClass *obj_class = (GObjectClass *) klass;
|
||||
|
||||
obj_class->finalize = test_object_finalize;
|
||||
obj_class->get_property = test_object_get_property;
|
||||
obj_class->set_property = test_object_set_property;
|
||||
|
||||
g_object_class_install_property (obj_class, PROP_TEST_STRING,
|
||||
g_param_spec_string ("test-string", "test-string", "blurb", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (obj_class, PROP_TEST_INT,
|
||||
g_param_spec_int ("test-int", "test-int", "blurb",
|
||||
G_MININT, G_MAXINT, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (obj_class, PROP_TEST_UINT,
|
||||
g_param_spec_uint ("test-uint", "test-uint", "blurb",
|
||||
0, G_MAXUINT, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (obj_class, PROP_TEST_INT64,
|
||||
g_param_spec_int64 ("test-int64", "test-int64", "blurb",
|
||||
G_MININT64, G_MAXINT64, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (obj_class, PROP_TEST_UINT64,
|
||||
g_param_spec_uint64 ("test-uint64", "test-uint64", "blurb",
|
||||
0, G_MAXUINT64, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (obj_class, PROP_TEST_FLOAT,
|
||||
g_param_spec_float ("test-float", "test-float", "blurb",
|
||||
-20.0f, 20.0f, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (obj_class, PROP_TEST_DOUBLE,
|
||||
g_param_spec_double ("test-double", "test-double", "blurb",
|
||||
-20.0, 20.0, 0.0,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (obj_class, PROP_TEST_BOOLEAN,
|
||||
g_param_spec_boolean ("test-boolean", "test-boolean", "blurb", FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_signal_new ("change", G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_ACTION | G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (TestObjectClass, change), NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT);
|
||||
klass->change = test_object_change;
|
||||
|
||||
g_signal_new ("acquire", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
|
||||
0, NULL, NULL, NULL, G_TYPE_INT, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
test_object_toggle (TestObject * self)
|
||||
{
|
||||
self->test_boolean = !self->test_boolean;
|
||||
g_object_notify (G_OBJECT (self), "test-boolean");
|
||||
}
|
||||
|
||||
static int
|
||||
l_test_object_toggle (lua_State * L)
|
||||
{
|
||||
TestObject * self = wplua_checkobject (L, 1, TEST_TYPE_OBJECT);
|
||||
test_object_toggle (self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const luaL_Reg l_test_object_methods[] = {
|
||||
{ "toggle", l_test_object_toggle },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static int
|
||||
l_test_object_new (lua_State * L)
|
||||
{
|
||||
wplua_pushobject (L, g_object_new (TEST_TYPE_OBJECT, NULL));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
test_wplua_basic ()
|
||||
{
|
||||
lua_State *L = wplua_new ();
|
||||
wplua_free (L);
|
||||
}
|
||||
|
||||
static void
|
||||
test_wplua_construct ()
|
||||
{
|
||||
g_autoptr (GObject) obj = NULL;
|
||||
g_autoptr (GError) error = NULL;
|
||||
lua_State *L = wplua_new ();
|
||||
|
||||
wplua_register_type_methods(L, TEST_TYPE_OBJECT,
|
||||
l_test_object_new, l_test_object_methods);
|
||||
|
||||
const gchar code[] =
|
||||
"o = TestObject.new()\n"
|
||||
"assert (type(o) == 'userdata')\n";
|
||||
wplua_load_buffer (L, code, sizeof (code) - 1, &error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
g_assert_cmpint (lua_getglobal (L, "o"), ==, LUA_TUSERDATA);
|
||||
g_assert_true (wplua_checkobject (L, -1, TEST_TYPE_OBJECT));
|
||||
g_assert_nonnull ((obj = wplua_toobject (L, -1)));
|
||||
g_object_ref (obj);
|
||||
g_assert_cmpint (obj->ref_count, ==, 2);
|
||||
|
||||
wplua_free (L);
|
||||
g_assert_cmpint (obj->ref_count, ==, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
test_wplua_properties ()
|
||||
{
|
||||
TestObject *obj = NULL;
|
||||
g_autoptr (GError) error = NULL;
|
||||
lua_State *L = wplua_new ();
|
||||
|
||||
wplua_register_type_methods(L, TEST_TYPE_OBJECT,
|
||||
l_test_object_new, l_test_object_methods);
|
||||
|
||||
const gchar code[] =
|
||||
"o = TestObject.new()\n"
|
||||
"o['test-string'] = 'string from lua'\n"
|
||||
"o['test-int'] = -15\n"
|
||||
"o['test-uint'] = 1123456789\n"
|
||||
"o['test-int64'] = -5123456789\n"
|
||||
"o['test-uint64'] = 15123456789\n"
|
||||
"o['test-float'] = 3.1415\n"
|
||||
"o['test-double'] = 0.123456789\n"
|
||||
"o['test-boolean'] = true\n";
|
||||
wplua_load_buffer (L, code, sizeof (code) - 1, &error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
g_assert_cmpint (lua_getglobal (L, "o"), ==, LUA_TUSERDATA);
|
||||
g_assert_true (wplua_checkobject (L, -1, TEST_TYPE_OBJECT));
|
||||
g_assert_nonnull ((obj = wplua_toobject (L, -1)));
|
||||
|
||||
g_assert_cmpstr (obj->test_string, ==, "string from lua");
|
||||
g_assert_cmpint (obj->test_int, ==, -15);
|
||||
g_assert_cmpuint (obj->test_uint, ==, 1123456789);
|
||||
g_assert_cmpint (obj->test_int64, ==, -5123456789);
|
||||
g_assert_cmpuint (obj->test_uint64, ==, 15123456789);
|
||||
g_assert_cmpfloat_with_epsilon (obj->test_float, 3.1415, 0.00001);
|
||||
g_assert_cmpfloat_with_epsilon (obj->test_double, 0.123456789, 0.0000000001);
|
||||
g_assert_true (obj->test_boolean);
|
||||
|
||||
const gchar code2[] =
|
||||
"assert (o['test-string'] == 'string from lua')\n"
|
||||
"assert (o['test-int'] == -15)\n"
|
||||
"assert (o['test-uint'] == 1123456789)\n"
|
||||
"assert (o['test-int64'] == -5123456789)\n"
|
||||
"assert (o['test-uint64'] == 15123456789)\n"
|
||||
"assert (math.abs (o['test-float'] - 3.1415) < 0.00001)\n"
|
||||
"assert (math.abs (o['test-double'] - 0.123456789) < 0.0000000001)\n"
|
||||
"assert (o['test-boolean'] == true)\n";
|
||||
wplua_load_buffer (L, code2, sizeof (code2) - 1, &error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
wplua_free (L);
|
||||
}
|
||||
|
||||
static void
|
||||
test_wplua_closure ()
|
||||
{
|
||||
GClosure *closure;
|
||||
g_autoptr (GError) error = NULL;
|
||||
lua_State *L = wplua_new ();
|
||||
|
||||
lua_pushstring (L, "some string");
|
||||
lua_setglobal (L, "expected_str");
|
||||
|
||||
const gchar code[] =
|
||||
"f_was_called = false\n"
|
||||
"function f(s)\n"
|
||||
" assert(s == expected_str)\n"
|
||||
" f_was_called = true\n"
|
||||
"end\n";
|
||||
wplua_load_buffer (L, code, sizeof (code) - 1, &error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
lua_getglobal (L, "f");
|
||||
closure = wplua_function_to_closure (L, -1);
|
||||
g_assert_nonnull (closure);
|
||||
g_closure_ref (closure);
|
||||
g_closure_sink (closure);
|
||||
lua_pop (L, 1);
|
||||
|
||||
{
|
||||
GValue s = G_VALUE_INIT;
|
||||
g_value_init (&s, G_TYPE_STRING);
|
||||
g_value_set_static_string (&s, "some string");
|
||||
g_closure_invoke (closure, NULL, 1, &s, NULL);
|
||||
}
|
||||
|
||||
lua_getglobal (L, "f_was_called");
|
||||
g_assert_true (lua_isboolean (L, -1));
|
||||
g_assert_true (lua_toboolean (L, -1));
|
||||
|
||||
wplua_free (L);
|
||||
|
||||
g_assert_true (closure->is_invalid);
|
||||
g_closure_unref (closure);
|
||||
}
|
||||
|
||||
static void
|
||||
test_wplua_signals ()
|
||||
{
|
||||
g_autoptr (GError) error = NULL;
|
||||
lua_State *L = wplua_new ();
|
||||
|
||||
wplua_register_type_methods(L, TEST_TYPE_OBJECT,
|
||||
l_test_object_new, l_test_object_methods);
|
||||
|
||||
const gchar code[] =
|
||||
"o = TestObject.new()\n"
|
||||
"\n"
|
||||
"o:connect('acquire', function (obj)\n"
|
||||
" assert(obj == o)\n"
|
||||
" return 42\n"
|
||||
" end)\n"
|
||||
"\n"
|
||||
"o:connect('notify::test-string', function (obj, pspec)\n"
|
||||
" assert(pspec == 'test-string')\n"
|
||||
" assert(obj[pspec] == 'changed: by Lua')\n"
|
||||
" end)\n"
|
||||
"\n"
|
||||
"o:call('change', 'by Lua', 55)\n"
|
||||
"\n"
|
||||
"assert(o['test-string'] == 'changed: by Lua')\n"
|
||||
"assert(o['test-int'] == 55)\n"
|
||||
"assert(o['test-int64'] == 42)\n"
|
||||
"\n"
|
||||
"o['test-boolean'] = true\n"
|
||||
"o:toggle()\n"
|
||||
"assert(o['test-boolean'] == false)\n";
|
||||
wplua_load_buffer (L, code, sizeof (code) - 1, &error);
|
||||
g_assert_no_error (error);
|
||||
wplua_free (L);
|
||||
}
|
||||
|
||||
gint
|
||||
main (gint argc, gchar *argv[])
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
wp_init (WP_INIT_ALL);
|
||||
|
||||
g_test_add_func ("/wplua/basic", test_wplua_basic);
|
||||
g_test_add_func ("/wplua/construct", test_wplua_construct);
|
||||
g_test_add_func ("/wplua/properties", test_wplua_properties);
|
||||
g_test_add_func ("/wplua/closure", test_wplua_closure);
|
||||
g_test_add_func ("/wplua/signals", test_wplua_signals);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue