Files
wireplumber/modules/module-lua-scripting/api/pod.c
2022-12-14 10:51:52 +01:00

1235 lines
32 KiB
C

/* WirePlumber
*
* Copyright © 2021 Collabora Ltd.
* @author Julian Bouzas <julian.bouzas@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include <wp/wp.h>
#include <wplua/wplua.h>
#include <spa/utils/type.h>
#define MAX_LUA_TYPES 9
/* Builder */
typedef gboolean (*primitive_lua_add_func) (WpSpaPodBuilder *, WpSpaIdValue,
lua_State *, int);
struct primitive_lua_type {
WpSpaType primitive_type;
primitive_lua_add_func primitive_lua_add_funcs[MAX_LUA_TYPES];
};
static inline gboolean
builder_add_boolean_lua_boolean (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
wp_spa_pod_builder_add_boolean (b, lua_toboolean (L, idx));
return TRUE;
}
static inline gboolean
builder_add_boolean_lua_number (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
if (lua_isinteger (L, idx)) {
wp_spa_pod_builder_add_boolean (b, lua_tointeger (L, idx) > 0);
return TRUE;
}
return FALSE;
}
static inline gboolean
builder_add_boolean_lua_string (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
const gchar *value = lua_tostring (L, idx);
wp_spa_pod_builder_add_boolean (b,
g_strcmp0 (value, "true") == 0 || g_strcmp0 (value, "1") == 0);
return TRUE;
}
static inline gboolean
builder_add_id_lua_number (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
if (lua_isinteger (L, idx)) {
wp_spa_pod_builder_add_id (b, lua_tointeger (L, idx));
return TRUE;
}
return FALSE;
}
static inline gboolean
builder_add_id_lua_string (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
const gchar *value = lua_tostring (L, idx);
WpSpaIdTable id_table = NULL;
WpSpaIdValue id_val = NULL;
if (key_id) {
wp_spa_id_value_get_value_type (key_id, &id_table);
if (id_table) {
id_val = wp_spa_id_table_find_value_from_short_name (id_table, value);
wp_spa_pod_builder_add_id (b, wp_spa_id_value_number (id_val));
return TRUE;
}
}
return FALSE;
}
static inline gboolean
builder_add_int_lua_boolean (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
gboolean value = lua_toboolean (L, idx);
wp_spa_pod_builder_add_int (b, value ? 1 : 0);
return TRUE;
}
static inline gboolean
builder_add_int_lua_number (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
if (lua_isinteger (L, idx)) {
wp_spa_pod_builder_add_int (b, lua_tointeger (L, idx));
return TRUE;
}
return FALSE;
}
static inline gboolean
builder_add_int_lua_string (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
const gchar *value = lua_tostring (L, idx);
wp_spa_pod_builder_add_int (b, strtol (value, NULL, 10));
return TRUE;
}
static inline gboolean
builder_add_long_lua_boolean (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
gboolean value = lua_toboolean (L, idx);
wp_spa_pod_builder_add_long (b, value ? 1 : 0);
return TRUE;
}
static inline gboolean
builder_add_long_lua_number (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
if (lua_isinteger (L, idx)) {
wp_spa_pod_builder_add_long (b, lua_tointeger (L, idx));
return TRUE;
}
return FALSE;
}
static inline gboolean
builder_add_long_lua_string (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
const gchar *value = lua_tostring (L, idx);
wp_spa_pod_builder_add_long (b, strtol (value, NULL, 10));
return TRUE;
}
static inline gboolean
builder_add_float_lua_boolean (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
gboolean value = lua_toboolean (L, idx);
wp_spa_pod_builder_add_float (b, value ? 1.0f : 0.0f);
return TRUE;
}
static inline gboolean
builder_add_float_lua_number (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
if (lua_isnumber (L, idx) && !lua_isinteger (L, idx)) {
wp_spa_pod_builder_add_float (b, lua_tonumber (L, idx));
return TRUE;
}
return FALSE;
}
static inline gboolean
builder_add_double_lua_boolean (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
gboolean value = lua_toboolean (L, idx);
wp_spa_pod_builder_add_double (b, value ? 1.0f : 0.0f);
return TRUE;
}
static inline gboolean
builder_add_double_lua_number (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
if (lua_isnumber (L, idx) && !lua_isinteger (L, idx)) {
wp_spa_pod_builder_add_double (b, lua_tonumber (L, idx));
return TRUE;
}
return FALSE;
}
static inline gboolean
builder_add_string_lua_boolean (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
gboolean value = lua_toboolean (L, idx);
wp_spa_pod_builder_add_string (b, value ? "true" : "false");
return TRUE;
}
static inline gboolean
builder_add_string_lua_number (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
g_autofree gchar *value = NULL;
value = lua_isinteger (L, idx) ?
g_strdup_printf ("%lld", lua_tointeger (L, idx)) :
g_strdup_printf ("%f", lua_tonumber (L, idx));
wp_spa_pod_builder_add_string (b, value);
return TRUE;
}
static inline gboolean
builder_add_string_lua_string (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
const gchar *value = lua_tostring (L, idx);
wp_spa_pod_builder_add_string (b, value);
return TRUE;
}
static inline gboolean
builder_add_bytes_lua_number (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
if (lua_isinteger (L, idx)) {
gint64 value = lua_tointeger (L, idx);
wp_spa_pod_builder_add_bytes (b, (gconstpointer)&value, sizeof (value));
} else {
double value = lua_tonumber (L, idx);
wp_spa_pod_builder_add_bytes (b, (gconstpointer)&value, sizeof (value));
}
return TRUE;
}
static inline gboolean
builder_add_bytes_lua_string (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
const gchar *value = lua_tostring (L, idx);
wp_spa_pod_builder_add_bytes (b, (gconstpointer)value, strlen (value));
return TRUE;
}
static inline gboolean
builder_add_fd_lua_number (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
if (lua_isinteger (L, idx)) {
wp_spa_pod_builder_add_fd (b, lua_tointeger (L, idx));
return TRUE;
}
return FALSE;
}
static inline gboolean
builder_add_fd_lua_string (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
const gchar *value = lua_tostring (L, idx);
wp_spa_pod_builder_add_fd (b, strtol (value, NULL, 10));
return TRUE;
}
static inline gboolean
is_pod_type_compatible (WpSpaType type, WpSpaPod *pod)
{
/* Check if the pod type matches */
if (type == wp_spa_pod_get_spa_type (pod))
return TRUE;
/* Otherwise, check if the child type of Choice pod matches */
if (wp_spa_pod_is_choice (pod)) {
g_autoptr (WpSpaPod) child = wp_spa_pod_get_choice_child (pod);
WpSpaType child_type = wp_spa_pod_get_spa_type (child);
if (type == child_type)
return TRUE;
}
return FALSE;
}
static inline gboolean
builder_add_lua_userdata (WpSpaPodBuilder *b, WpSpaIdValue key_id,
lua_State *L, int idx)
{
WpSpaPod *pod = wplua_checkboxed (L, idx, WP_TYPE_SPA_POD);
if (pod) {
if (key_id) {
WpSpaType prop_type = wp_spa_id_value_get_value_type (key_id, NULL);
if (is_pod_type_compatible (prop_type, pod)) {
wp_spa_pod_builder_add_pod (b, pod);
return TRUE;
}
} else {
wp_spa_pod_builder_add_pod (b, pod);
return TRUE;
}
}
return FALSE;
}
static const struct primitive_lua_type primitive_lua_types[] = {
{SPA_TYPE_Bool, {
[LUA_TBOOLEAN] = builder_add_boolean_lua_boolean,
[LUA_TNUMBER] = builder_add_boolean_lua_number,
[LUA_TSTRING] = builder_add_boolean_lua_string,
}},
{SPA_TYPE_Id, {
[LUA_TNUMBER] = builder_add_id_lua_number,
[LUA_TSTRING] = builder_add_id_lua_string,
}},
{SPA_TYPE_Int, {
[LUA_TBOOLEAN] = builder_add_int_lua_boolean,
[LUA_TNUMBER] = builder_add_int_lua_number,
[LUA_TSTRING] = builder_add_int_lua_string,
}},
{SPA_TYPE_Long, {
[LUA_TBOOLEAN] = builder_add_long_lua_boolean,
[LUA_TNUMBER] = builder_add_long_lua_number,
[LUA_TSTRING] = builder_add_long_lua_string,
}},
{SPA_TYPE_Float, {
[LUA_TBOOLEAN] = builder_add_float_lua_boolean,
[LUA_TNUMBER] = builder_add_float_lua_number,
}},
{SPA_TYPE_Double, {
[LUA_TBOOLEAN] = builder_add_double_lua_boolean,
[LUA_TNUMBER] = builder_add_double_lua_number,
}},
{SPA_TYPE_String, {
[LUA_TBOOLEAN] = builder_add_string_lua_boolean,
[LUA_TNUMBER] = builder_add_string_lua_number,
[LUA_TSTRING] = builder_add_string_lua_string,
}},
{SPA_TYPE_Bytes, {
[LUA_TNUMBER] = builder_add_bytes_lua_number,
[LUA_TSTRING] = builder_add_bytes_lua_string,
}},
{SPA_TYPE_Fd, {
[LUA_TNUMBER] = builder_add_fd_lua_number,
[LUA_TSTRING] = builder_add_fd_lua_string,
}},
{0, {}},
};
static gboolean
builder_add_key (WpSpaPodBuilder *b, WpSpaIdTable table, lua_State *L, int idx)
{
/* Number */
if (lua_type (L, -1) == LUA_TNUMBER) {
wp_spa_pod_builder_add_id (b, lua_tonumber (L, idx));
return TRUE;
}
/* String */
else if (lua_type (L, -1) == LUA_TSTRING) {
const gchar *key = lua_tostring (L, idx);
WpSpaIdValue val = wp_spa_id_table_find_value_from_short_name (table, key);
if (val) {
wp_spa_pod_builder_add_id (b, wp_spa_id_value_number (val));
return TRUE;
}
}
return FALSE;
}
static gboolean
builder_add_value (WpSpaPodBuilder *b, WpSpaType array_type, lua_State *L,
int idx)
{
int ltype = lua_type (L, idx);
guint i;
if (ltype < 0 || ltype >= MAX_LUA_TYPES)
return FALSE;
for (i = 0; primitive_lua_types[i].primitive_type; i++) {
const struct primitive_lua_type *t = primitive_lua_types + i;
if (t->primitive_type == array_type) {
primitive_lua_add_func f = t->primitive_lua_add_funcs[ltype];
if (f) {
return f (b, NULL, L, idx);
}
}
}
return FALSE;
}
static void
builder_add_table (lua_State *L, WpSpaPodBuilder *builder)
{
const gchar *type_name = NULL;
WpSpaType type = WP_SPA_TYPE_INVALID;
WpSpaIdTable table = NULL;
luaL_checktype (L, 1, LUA_TTABLE);
lua_pushnil (L);
while (lua_next (L, 1)) {
/* First filed is always the item type or table */
if (type == WP_SPA_TYPE_INVALID && !table) {
if (lua_type (L, -1) == LUA_TSTRING) {
type_name = lua_tostring (L, -1);
type = wp_spa_type_from_name (type_name);
if (type == WP_SPA_TYPE_INVALID) {
table = wp_spa_id_table_from_name (type_name);
if (!table)
luaL_error (L, "Unknown type '%s'", type_name);
}
} else {
luaL_error (L,
"must have the item type or table on its first field");
}
}
/* Add remaining table key elements */
else if (table) {
if (!builder_add_key (builder, table, L, -1))
luaL_error (L, "key could not be added");
}
/* Add remaining value elements */
else if (type != WP_SPA_TYPE_INVALID) {
if (!builder_add_value (builder, type, L, -1))
luaL_error (L, "value could not be added");
}
lua_pop (L, 1);
}
}
/* None */
static int
spa_pod_none_new (lua_State *L)
{
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_new_none ());
return 1;
}
/* Boolean */
static int
spa_pod_boolean_new (lua_State *L)
{
gboolean value = lua_toboolean (L, 1);
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_new_boolean (value));
return 1;
}
/* Id */
static int
spa_pod_id_new (lua_State *L)
{
/* Id number */
if (lua_type (L, 1) == LUA_TNUMBER) {
gint64 value = lua_tointeger (L, 1);
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_new_id (value));
return 1;
}
/* Table name and key name */
else if (lua_type (L, 1) == LUA_TSTRING) {
const gchar *table_name = lua_tostring (L, 1);
const gchar *key_name = lua_tostring (L, 2);
WpSpaIdTable table = NULL;
WpSpaIdValue value = NULL;
table = wp_spa_id_table_from_name (table_name);
if (!table)
luaL_error (L, "table '%s' does not exist", table_name);
value = wp_spa_id_table_find_value_from_short_name (table, key_name);
if (!value)
luaL_error (L, "key '%s' does not exist in '%s'", key_name, table_name);
wplua_pushboxed (L, WP_TYPE_SPA_POD,
wp_spa_pod_new_id (wp_spa_id_value_number (value)));
return 1;
}
/* Error */
luaL_error (L, "Invalid parameters");
return 0;
}
/* Int */
static int
spa_pod_int_new (lua_State *L)
{
gint64 value = lua_tointeger (L, 1);
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_new_int (value));
return 1;
}
/* Long */
static int
spa_pod_long_new (lua_State *L)
{
gint64 value = lua_tointeger (L, 1);
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_new_long (value));
return 1;
}
/* Float */
static int
spa_pod_float_new (lua_State *L)
{
float value = lua_tonumber (L, 1);
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_new_float (value));
return 1;
}
/* Double */
static int
spa_pod_double_new (lua_State *L)
{
double value = lua_tonumber (L, 1);
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_new_double (value));
return 1;
}
/* String */
static int
spa_pod_string_new (lua_State *L)
{
const gchar *value = lua_tostring (L, 1);
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_new_string (value));
return 1;
}
/* Bytes */
static int
spa_pod_bytes_new (lua_State *L)
{
switch (lua_type (L, 1)) {
case LUA_TNUMBER: {
if (lua_isinteger (L, 1)) {
guint64 value = lua_tointeger (L, 1);
wplua_pushboxed (L, WP_TYPE_SPA_POD,
wp_spa_pod_new_bytes (&value, sizeof (guint64)));
} else {
double value = lua_tonumber (L, 1);
wplua_pushboxed (L, WP_TYPE_SPA_POD,
wp_spa_pod_new_bytes (&value, sizeof (double)));
}
return 1;
}
case LUA_TSTRING: {
const gchar *str = lua_tostring (L, 1);
wplua_pushboxed (L, WP_TYPE_SPA_POD,
wp_spa_pod_new_bytes (str, strlen (str)));
return 1;
}
default:
luaL_error (L, "Only number and strings are valid for bytes pod");
break;
}
return 0;
}
/* Pointer */
static int
spa_pod_pointer_new (lua_State *L)
{
const gchar *type = lua_tostring (L, 1);
gconstpointer value = lua_touserdata (L, 2);
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_new_pointer (type, value));
return 1;
}
/* Fd */
static int
spa_pod_fd_new (lua_State *L)
{
gint64 value = lua_tointeger (L, 1);
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_new_fd (value));
return 1;
}
/* Rectangle */
static int
spa_pod_rectangle_new (lua_State *L)
{
gint64 width = lua_tointeger (L, 1);
gint64 height = lua_tointeger (L, 2);
wplua_pushboxed (L, WP_TYPE_SPA_POD,
wp_spa_pod_new_rectangle (width, height));
return 1;
}
/* Fraction */
static int
spa_pod_fraction_new (lua_State *L)
{
gint64 num = lua_tointeger (L, 1);
gint64 denom = lua_tointeger (L, 2);
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_new_fraction (num, denom));
return 1;
}
/* Object */
static gboolean
object_add_property (WpSpaPodBuilder *b, WpSpaIdTable table,
const gchar *key, lua_State *L, int idx)
{
guint i;
WpSpaIdValue prop_id = NULL;
int ltype = lua_type (L, idx);
if (ltype < 0 || ltype >= MAX_LUA_TYPES)
return FALSE;
/* Check if we can add primitive property directly from LUA type */
prop_id = wp_spa_id_table_find_value_from_short_name (table, key);
if (prop_id) {
WpSpaType prop_type = wp_spa_id_value_get_value_type (prop_id, NULL);
if (prop_type != WP_SPA_TYPE_INVALID) {
for (i = 0; primitive_lua_types[i].primitive_type; i++) {
const struct primitive_lua_type *t = primitive_lua_types + i;
if (t->primitive_type == prop_type) {
primitive_lua_add_func f = t->primitive_lua_add_funcs[ltype];
if (f) {
wp_spa_pod_builder_add_property (b, key);
return f (b, prop_id, L, idx);
}
}
}
}
}
/* Otherwise just add pod property */
if (lua_type (L, idx) == LUA_TUSERDATA) {
wp_spa_pod_builder_add_property (b, key);
return builder_add_lua_userdata (b, prop_id, L, idx);
}
return FALSE;
}
static int
spa_pod_object_new (lua_State *L)
{
g_autoptr (WpSpaPodBuilder) builder = NULL;
const gchar *fields[2] = { NULL, NULL }; // type_name, name_id
WpSpaType object_type = WP_SPA_TYPE_INVALID;
WpSpaIdTable table = NULL;
luaL_checktype (L, 1, LUA_TTABLE);
lua_geti (L, 1, 1);
fields[0] = lua_tostring (L, -1);
lua_geti (L, 1, 2);
fields[1] = lua_tostring (L, -1);
object_type = wp_spa_type_from_name (fields[0]);
if (object_type == WP_SPA_TYPE_INVALID)
luaL_error (L, "Invalid object type '%s'", fields[0]);
table = wp_spa_type_get_values_table (object_type);
if (!table)
luaL_error (L, "Object type '%s' has incomplete type information",
fields[0]);
builder = wp_spa_pod_builder_new_object (fields[0], fields[1]);
if (!builder)
luaL_error (L, "Could not create pod object");
lua_pop (L, 2);
lua_pushnil(L);
while (lua_next (L, -2)) {
/* Remaining fields with string keys are the object properties */
if (lua_type (L, -2) == LUA_TSTRING) {
const gchar *key = lua_tostring (L, -2);
if (!object_add_property (builder, table, key, L, -1))
luaL_error (L, "Property '%s' could not be added", key);
}
lua_pop (L, 1);
}
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_builder_end (builder));
return 1;
}
/* Struct */
static int
spa_pod_struct_new (lua_State *L)
{
g_autoptr (WpSpaPodBuilder) builder = NULL;
luaL_checktype (L, 1, LUA_TTABLE);
builder = wp_spa_pod_builder_new_struct ();
lua_pushnil (L);
while (lua_next (L, 1)) {
switch (lua_type (L, -1)) {
case LUA_TBOOLEAN:
wp_spa_pod_builder_add_boolean (builder, lua_toboolean (L, -1));
break;
case LUA_TNUMBER:
if (lua_isinteger (L, -1))
wp_spa_pod_builder_add_long (builder, lua_tointeger (L, -1));
else
wp_spa_pod_builder_add_double (builder, lua_tonumber (L, -1));
break;
case LUA_TSTRING:
wp_spa_pod_builder_add_string (builder, lua_tostring (L, -1));
break;
case LUA_TUSERDATA: {
WpSpaPod *pod = wplua_checkboxed (L, -1, WP_TYPE_SPA_POD);
wp_spa_pod_builder_add_pod (builder, pod);
break;
}
default:
luaL_error (L, "Struct does not support lua type ",
lua_typename(L, lua_type(L, -1)));
break;
}
lua_pop (L, 1);
}
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_builder_end (builder));
return 1;
}
/* Sequence */
static int
spa_pod_sequence_new (lua_State *L)
{
g_autoptr (WpSpaPodBuilder) builder = NULL;
luaL_checktype (L, 1, LUA_TTABLE);
builder = wp_spa_pod_builder_new_sequence (0);
lua_pushnil (L);
while (lua_next (L, -2)) {
guint32 offset = 0;
const gchar *type_name = NULL;
WpSpaPod *value = NULL;
/* Read Control */
if (lua_istable(L, -1)) {
lua_pushnil (L);
while (lua_next (L, -2)) {
const gchar *key = lua_tostring (L, -2);
if (g_strcmp0 (key, "offset") == 0) {
offset = lua_tointeger (L, -1);
} else if (!type_name && g_strcmp0 (key, "typename") == 0) {
type_name = lua_tostring (L, -1);
} else if (!value && g_strcmp0 (key, "value") == 0) {
switch (lua_type (L, -1)) {
case LUA_TBOOLEAN:
value = wp_spa_pod_new_boolean (lua_toboolean (L, -1));
break;
case LUA_TNUMBER:
if (lua_isinteger (L, -1))
value = wp_spa_pod_new_long (lua_tointeger (L, -1));
else
value = wp_spa_pod_new_double (lua_tonumber (L, -1));
break;
case LUA_TSTRING:
value = wp_spa_pod_new_string (lua_tostring (L, -1));
break;
case LUA_TUSERDATA: {
value = wplua_checkboxed (L, -1, WP_TYPE_SPA_POD);
break;
}
default: {
luaL_error (L, "Control value does not support lua type ",
lua_typename(L, lua_type(L, -1)));
value = NULL;
break;
}
}
}
lua_pop(L, 1);
}
}
/* Add control */
if (type_name && value) {
wp_spa_pod_builder_add_control (builder, offset, type_name);
wp_spa_pod_builder_add_pod (builder, value);
wp_spa_pod_unref (value);
}
lua_pop(L, 1);
}
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_builder_end (builder));
return 1;
}
/* Array */
static int
spa_pod_array_new (lua_State *L)
{
g_autoptr (WpSpaPodBuilder) builder = wp_spa_pod_builder_new_array ();
builder_add_table (L, builder);
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_builder_end (builder));
return 1;
}
/* Choice */
static int
spa_pod_choice_none_new (lua_State *L)
{
g_autoptr (WpSpaPodBuilder) builder = wp_spa_pod_builder_new_choice ("None");
builder_add_table (L, builder);
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_builder_end (builder));
return 1;
}
static int
spa_pod_choice_range_new (lua_State *L)
{
g_autoptr (WpSpaPodBuilder) builder = wp_spa_pod_builder_new_choice ("Range");
builder_add_table (L, builder);
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_builder_end (builder));
return 1;
}
static int
spa_pod_choice_step_new (lua_State *L)
{
g_autoptr (WpSpaPodBuilder) builder = wp_spa_pod_builder_new_choice ("Step");
builder_add_table (L, builder);
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_builder_end (builder));
return 1;
}
static int
spa_pod_choice_enum_new (lua_State *L)
{
g_autoptr (WpSpaPodBuilder) builder = wp_spa_pod_builder_new_choice ("Enum");
builder_add_table (L, builder);
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_builder_end (builder));
return 1;
}
static int
spa_pod_choice_flags_new (lua_State *L)
{
g_autoptr (WpSpaPodBuilder) builder = wp_spa_pod_builder_new_choice ("Flags");
builder_add_table (L, builder);
wplua_pushboxed (L, WP_TYPE_SPA_POD, wp_spa_pod_builder_end (builder));
return 1;
}
/* API */
static int
spa_pod_get_type_name (lua_State *L)
{
WpSpaPod *pod = wplua_checkboxed (L, 1, WP_TYPE_SPA_POD);
lua_pushstring (L, wp_spa_type_name (wp_spa_pod_get_spa_type (pod)));
return 1;
}
static void
push_primitive_values (lua_State *L, WpSpaPod *pod, WpSpaType type,
guint start_index, WpSpaIdTable idtable)
{
g_auto (GValue) item = G_VALUE_INIT;
g_autoptr (WpIterator) it = wp_spa_pod_new_iterator (pod);
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
gpointer p = g_value_get_pointer (&item);
if (!p)
continue;
switch (type) {
case SPA_TYPE_Bool:
lua_pushboolean (L, *(gboolean *)p);
break;
case SPA_TYPE_Id: {
WpSpaIdValue idval = NULL;
if (idtable)
idval = wp_spa_id_table_find_value (idtable, *(guint32 *)p);
if (idval)
lua_pushstring (L, wp_spa_id_value_short_name (idval));
else
lua_pushinteger (L, *(guint32 *)p);
break;
}
case SPA_TYPE_Int:
lua_pushinteger (L, *(gint *)p);
break;
case SPA_TYPE_Long:
lua_pushinteger (L, *(long *)p);
break;
case SPA_TYPE_Float:
lua_pushnumber (L, *(float *)p);
break;
case SPA_TYPE_Double:
lua_pushnumber (L, *(double *)p);
break;
case SPA_TYPE_Fd:
lua_pushnumber (L, *(gint64 *)p);
break;
default:
continue;
}
lua_rawseti (L, -2, start_index++);
}
}
static void
push_luapod (lua_State *L, WpSpaPod *pod, WpSpaIdValue field_idval)
{
/* None */
if (wp_spa_pod_is_none (pod)) {
lua_pushnil (L);
}
/* Boolean */
else if (wp_spa_pod_is_boolean (pod)) {
gboolean value = FALSE;
g_warn_if_fail (wp_spa_pod_get_boolean (pod, &value));
lua_pushboolean (L, value);
}
/* Id */
else if (wp_spa_pod_is_id (pod)) {
guint32 value = 0;
WpSpaIdTable idtable = NULL;
WpSpaIdValue idval = NULL;
g_warn_if_fail (wp_spa_pod_get_id (pod, &value));
if (field_idval && SPA_TYPE_Id ==
wp_spa_id_value_get_value_type (field_idval, &idtable)) {
idval = wp_spa_id_table_find_value (idtable, value);
}
if (idval)
lua_pushstring (L, wp_spa_id_value_short_name (idval));
else
lua_pushinteger (L, value);
}
/* Int */
else if (wp_spa_pod_is_int (pod)) {
gint value = 0;
g_warn_if_fail (wp_spa_pod_get_int (pod, &value));
lua_pushinteger (L, value);
}
/* Long */
else if (wp_spa_pod_is_long (pod)) {
gint64 value = 0;
wp_spa_pod_get_long (pod, &value);
lua_pushinteger (L, value);
}
/* Float */
else if (wp_spa_pod_is_float (pod)) {
float value = 0;
g_warn_if_fail (wp_spa_pod_get_float (pod, &value));
lua_pushnumber (L, value);
}
/* Double */
else if (wp_spa_pod_is_double (pod)) {
double value = 0;
g_warn_if_fail (wp_spa_pod_get_double (pod, &value));
lua_pushnumber (L, value);
}
/* String */
else if (wp_spa_pod_is_string (pod)) {
const gchar *value = NULL;
g_warn_if_fail (wp_spa_pod_get_string (pod, &value));
lua_pushstring (L, value);
}
/* Bytes */
else if (wp_spa_pod_is_bytes (pod)) {
gconstpointer value = NULL;
guint32 size = 0;
g_warn_if_fail (wp_spa_pod_get_bytes (pod, &value, &size));
char str[size + 1];
for (guint i = 0; i < size; i++)
str[i] = ((const gchar *)value)[i];
str[size] = '\0';
lua_pushstring (L, str);
}
/* Pointer */
else if (wp_spa_pod_is_pointer (pod)) {
gconstpointer value = NULL;
g_warn_if_fail (wp_spa_pod_get_pointer (pod, &value));
if (!value)
lua_pushnil (L);
else
lua_pushlightuserdata (L, (gpointer)value);
}
/* Fd */
else if (wp_spa_pod_is_fd (pod)) {
gint64 value = 0;
g_warn_if_fail (wp_spa_pod_get_fd (pod, &value));
lua_pushinteger (L, value);
}
/* Rectangle */
else if (wp_spa_pod_is_rectangle (pod)) {
guint32 width = 0, height = 0;
g_warn_if_fail (wp_spa_pod_get_rectangle (pod, &width, &height));
lua_newtable (L);
lua_pushstring (L, "Rectangle");
lua_setfield (L, -2, "pod_type");
lua_pushinteger (L, width);
lua_setfield (L, -2, "width");
lua_pushinteger (L, height);
lua_setfield (L, -2, "height");
}
/* Fraction */
else if (wp_spa_pod_is_fraction (pod)) {
guint32 num = 0, denom = 0;
g_warn_if_fail (wp_spa_pod_get_fraction (pod, &num, &denom));
lua_newtable (L);
lua_pushstring (L, "Fraction");
lua_setfield (L, -2, "pod_type");
lua_pushinteger (L, num);
lua_setfield (L, -2, "num");
lua_pushinteger (L, denom);
lua_setfield (L, -2, "denom");
}
/* Object */
else if (wp_spa_pod_is_object (pod)) {
WpSpaType type = wp_spa_pod_get_spa_type (pod);
WpSpaIdTable values_table = wp_spa_type_get_values_table (type);
const gchar *id_name = NULL;
g_auto (GValue) item = G_VALUE_INIT;
g_autoptr (WpIterator) it = NULL;
g_warn_if_fail (wp_spa_pod_get_object (pod, &id_name, NULL));
lua_newtable (L);
lua_pushstring (L, "Object");
lua_setfield (L, -2, "pod_type");
lua_pushstring (L, id_name);
lua_setfield (L, -2, "object_id");
it = wp_spa_pod_new_iterator (pod);
lua_newtable (L);
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
WpSpaPod *prop = g_value_get_boxed (&item);
const gchar *key = NULL;
g_autoptr (WpSpaPod) val = NULL;
//FIXME: this is suboptimal because _get_property() converts
// the key to a short name and we convert it back
g_warn_if_fail (wp_spa_pod_get_property (prop, &key, &val));
if (key) {
push_luapod (L, val,
wp_spa_id_table_find_value_from_short_name (values_table, key));
lua_setfield (L, -2, key);
}
}
lua_setfield (L, -2, "properties");
}
/* Struct */
else if (wp_spa_pod_is_struct (pod)) {
g_auto (GValue) item = G_VALUE_INIT;
g_autoptr (WpIterator) it = wp_spa_pod_new_iterator (pod);
guint i = 1;
lua_newtable (L);
lua_pushstring (L, "Struct");
lua_setfield (L, -2, "pod_type");
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
WpSpaPod *val = g_value_get_boxed (&item);
push_luapod (L, val, NULL);
lua_rawseti (L, -2, i++);
}
}
/* Sequence */
else if (wp_spa_pod_is_sequence (pod)) {
g_auto (GValue) item = G_VALUE_INIT;
g_autoptr (WpIterator) it = wp_spa_pod_new_iterator (pod);
guint i = 1;
lua_newtable (L);
lua_pushstring (L, "Sequence");
lua_setfield (L, -2, "pod_type");
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
WpSpaPod *control = g_value_get_boxed (&item);
guint32 offset = 0;
const char *type_name = NULL;
g_autoptr (WpSpaPod) val = NULL;
g_warn_if_fail (wp_spa_pod_get_control (control, &offset, &type_name,
&val));
lua_newtable (L);
lua_pushinteger (L, offset);
lua_setfield (L, -2, "offset");
lua_pushstring (L, type_name);
lua_setfield (L, -2, "typename");
push_luapod (L, val, NULL);
lua_setfield (L, -2, "value");
lua_rawseti (L, -2, i++);
}
}
/* Array */
else if (wp_spa_pod_is_array (pod)) {
g_autoptr (WpSpaPod) child = wp_spa_pod_get_array_child (pod);
WpSpaType type = wp_spa_pod_get_spa_type (child);
WpSpaIdTable idtable = NULL;
if (field_idval && type == SPA_TYPE_Id && SPA_TYPE_Array ==
wp_spa_id_value_get_value_type (field_idval, &idtable))
wp_spa_id_value_array_get_item_type (field_idval, &idtable);
lua_newtable (L);
lua_pushstring (L, "Array");
lua_setfield (L, -2, "pod_type");
lua_pushstring (L, wp_spa_type_name (type));
lua_setfield (L, -2, "value_type");
push_primitive_values (L, pod, type, 1, idtable);
}
/* Choice */
else if (wp_spa_pod_is_choice (pod)) {
g_autoptr (WpSpaPod) child = wp_spa_pod_get_choice_child (pod);
WpSpaType type = wp_spa_pod_get_spa_type (child);
g_autofree const gchar *choice_type = g_strdup_printf ("Choice.%s",
wp_spa_id_value_short_name (wp_spa_pod_get_choice_type (pod)));
WpSpaIdTable idtable = NULL;
if (field_idval && type == SPA_TYPE_Id)
wp_spa_id_value_get_value_type (field_idval, &idtable);
lua_newtable (L);
lua_pushstring (L, choice_type);
lua_setfield (L, -2, "pod_type");
lua_pushstring (L, wp_spa_type_name (type));
lua_setfield (L, -2, "value_type");
push_primitive_values (L, pod, type, 1, idtable);
}
/* Error */
else {
luaL_error (L, "Unsupported pod type ",
wp_spa_type_name (wp_spa_pod_get_spa_type (pod)));
}
}
static int
spa_pod_parse (lua_State *L)
{
WpSpaPod *pod = wplua_checkboxed (L, 1, WP_TYPE_SPA_POD);
push_luapod (L, pod, NULL);
return 1;
}
static int
spa_pod_fixate (lua_State *L)
{
WpSpaPod *pod = wplua_checkboxed (L, 1, WP_TYPE_SPA_POD);
gboolean res = wp_spa_pod_fixate (pod);
lua_pushboolean (L, res);
return 1;
}
static inline WpSpaPod *
_checkpod (lua_State *L, int n)
{
return wplua_checkboxed (L, n, WP_TYPE_SPA_POD);
}
static int
spa_pod_filter (lua_State *L)
{
WpSpaPod *pod = wplua_checkboxed (L, 1, WP_TYPE_SPA_POD);
WpSpaPod *filter = luaL_opt (L, _checkpod, 2, NULL);
WpSpaPod *result = wp_spa_pod_filter (pod, filter);
if (result) {
wplua_pushboxed (L, WP_TYPE_SPA_POD, result);
return 1;
}
return 0;
}
static const luaL_Reg spa_pod_methods[] = {
{ "get_type_name", spa_pod_get_type_name },
{ "parse", spa_pod_parse },
{ "fixate", spa_pod_fixate },
{ "filter", spa_pod_filter },
{ NULL, NULL }
};
static const luaL_Reg spa_pod_constructors[] = {
{ "None", spa_pod_none_new },
{ "Boolean", spa_pod_boolean_new },
{ "Id", spa_pod_id_new },
{ "Int", spa_pod_int_new },
{ "Long", spa_pod_long_new },
{ "Float", spa_pod_float_new },
{ "Double", spa_pod_double_new },
{ "String", spa_pod_string_new },
{ "Bytes", spa_pod_bytes_new },
{ "Pointer", spa_pod_pointer_new },
{ "Fd", spa_pod_fd_new },
{ "Rectangle", spa_pod_rectangle_new },
{ "Fraction", spa_pod_fraction_new },
{ "Object", spa_pod_object_new },
{ "Struct", spa_pod_struct_new },
{ "Sequence", spa_pod_sequence_new },
{ "Array", spa_pod_array_new },
{ NULL, NULL }
};
static const luaL_Reg spa_pod_choice_constructors[] = {
{ "None", spa_pod_choice_none_new },
{ "Range", spa_pod_choice_range_new },
{ "Step", spa_pod_choice_step_new },
{ "Enum", spa_pod_choice_enum_new },
{ "Flags", spa_pod_choice_flags_new },
{ NULL, NULL }
};
/* Init */
void
wp_lua_scripting_pod_init (lua_State *L)
{
luaL_newlib (L, spa_pod_constructors);
luaL_newlib (L, spa_pod_choice_constructors);
lua_setfield (L, -2, "Choice");
lua_setglobal (L, "WpSpaPod");
wplua_register_type_methods (L, WP_TYPE_SPA_POD, NULL, spa_pod_methods);
}