Files
wireplumber/modules/module-lua-scripting/api/pod.c
George Kiagiadakis 4736d56557 log: implement a log topics system, like pipewire
The intention is to make checks for enabled log topics faster.

Every topic has its own structure that is statically defined in the file
where the logs are printed from. The structure is initialized transparently
when it is first used and it contains all the log level flags for the levels
that this topic should print messages. It is then checked on the wp_log()
macro before printing the message.

Topics from SPA/PipeWire are also handled natively, so messages are printed
directly without checking if the topic is enabled, since the PipeWire and SPA
macros do the checking themselves.

Messages coming from GLib are checked inside the handler.

An internal WpLogFields object is used to manage the state of each log
message, populating all the fields appropriately from the place they
are coming from (wp_log, spa_log, glib log), formatting the message and
then printing it. For printing to the journald, we still use the glib
message handler, converting all the needed fields to GLogField on demand.
That message handler does not do any checks for the topic or the level, so
we can just call it to send the message.
2023-05-16 20:42:28 +03:00

1238 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 WP_LOCAL_LOG_TOPIC log_topic_lua_scripting
WP_LOG_TOPIC_EXTERN (log_topic_lua_scripting)
#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);
}