wplua: move under modules/module-lua-scripting
It's unlikely that wplua will ever be useful outside the context of module-lua-scripting, so let's move it to keep all the code in one place
This commit is contained in:
1433
modules/module-lua-scripting/api/api.c
Normal file
1433
modules/module-lua-scripting/api/api.c
Normal file
File diff suppressed because it is too large
Load Diff
212
modules/module-lua-scripting/api/api.lua
Normal file
212
modules/module-lua-scripting/api/api.lua
Normal file
@@ -0,0 +1,212 @@
|
||||
-- WirePlumber
|
||||
--
|
||||
-- This file contains the API that is made available to the Lua scripts
|
||||
--
|
||||
-- Copyright © 2020 Collabora Ltd.
|
||||
-- @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||
--
|
||||
-- SPDX-License-Identifier: MIT
|
||||
|
||||
local function Constraint (spec)
|
||||
assert (type(spec[1]) == "string", "Constraint: expected subject as string");
|
||||
assert (type(spec[2]) == "string", "Constraint: expected verb as string");
|
||||
|
||||
local subject = spec[1]
|
||||
local verb = spec[2]
|
||||
local verbs = {
|
||||
["="] = "equals",
|
||||
["!"] = "not-equals",
|
||||
["c"] = "in-list",
|
||||
["~"] = "in-range",
|
||||
["#"] = "matches",
|
||||
["+"] = "is-present",
|
||||
["-"] = "is-absent"
|
||||
}
|
||||
|
||||
-- check and convert verb to its short version
|
||||
local verb_is_valid = false
|
||||
for k, v in pairs(verbs) do
|
||||
if verb == k or verb == v then
|
||||
verb = k
|
||||
spec[2] = k
|
||||
verb_is_valid = true
|
||||
break
|
||||
end
|
||||
end
|
||||
assert (verb_is_valid, "Constraint: invalid verb '" .. verb .. "'")
|
||||
|
||||
-- check and convert type to its integer value
|
||||
local type = spec["type"]
|
||||
if type then
|
||||
local valid_types = { "pw-global", "pw", "gobject" }
|
||||
local type_is_valid = false
|
||||
|
||||
for i, v in ipairs(valid_types) do
|
||||
if type == v then
|
||||
spec["type"] = i
|
||||
type_is_valid = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
assert(type_is_valid, "Constraint: invalid subject type '" .. type .. "'")
|
||||
end
|
||||
|
||||
-- check if we got the right amount of values
|
||||
if verb == "=" or verb == "!" or verb == "#" then
|
||||
assert (spec[3] ~= nil,
|
||||
"Constraint: " .. verbs[verb] .. ": expected constraint value")
|
||||
elseif verb == "c" then
|
||||
assert (spec[3] ~= nil,
|
||||
"Constraint: " .. verbs[verb] .. ": expected at least one constraint value")
|
||||
elseif verb == "~" then
|
||||
assert (spec[3] ~= nil and spec[4] ~= nil,
|
||||
"Constraint: " .. verbs[verb] .. ": expected two values")
|
||||
else
|
||||
assert (spec[3] == nil,
|
||||
"Constraint: " .. verbs[verb] .. ": expected no value, but there is one")
|
||||
end
|
||||
|
||||
return debug.setmetatable(spec, { __name = "Constraint" })
|
||||
end
|
||||
|
||||
local function dump_table(t, indent)
|
||||
local indent_str = ""
|
||||
indent = indent or 1
|
||||
for i = 1, indent, 1 do
|
||||
indent_str = indent_str .. "\t"
|
||||
end
|
||||
|
||||
local kvpairs = {}
|
||||
for k, v in pairs(t) do
|
||||
table.insert(kvpairs, { k, v })
|
||||
end
|
||||
|
||||
table.sort(kvpairs, function (lhs, rhs)
|
||||
local left_key, right_key = lhs[1], rhs[1]
|
||||
|
||||
-- If the types are different, we sort by the type
|
||||
-- in alphabetical order. This means that numbers
|
||||
-- come before before strings, etc
|
||||
if type(left_key) ~= type(right_key) then
|
||||
return type(left_key) < type(right_key)
|
||||
end
|
||||
|
||||
local key_type = type(left_key)
|
||||
|
||||
-- Only numbers and strings have a well-defined order
|
||||
-- that's guaranteed to fulfill the requirements of
|
||||
-- table.sort (strict weak order)
|
||||
if key_type == "number" or key_type == "string" then
|
||||
return left_key < right_key
|
||||
end
|
||||
|
||||
-- At this point, we have no good way to order the objects.
|
||||
-- We can't just do `left_key < right_key`, because this may fail
|
||||
-- if there's no `__lt` metamethod, and even if there is one,
|
||||
-- it might not be a strict weak order. (The Lua reference does
|
||||
-- not say what happens if the order is not strict weak, so it's
|
||||
-- undefined behaviour)
|
||||
|
||||
-- That said, it's always mathematically "permitted" to return `false`,
|
||||
-- in which case, since both x < y and y < x are false, the elements
|
||||
-- are considered "equivalent" and may appear in any order in relation
|
||||
-- to each other. The elements are still sorted in relation to the
|
||||
-- *other* keys.
|
||||
return false
|
||||
|
||||
-- To be a strict weak order, if x and y are equivalent, and y and z
|
||||
-- are equivalent, then x and z must be equivalent too. Otherwise the
|
||||
-- ordering is only a strict *partial* order.
|
||||
|
||||
-- Note that the Lua 5.3 reference states that the order merely has to
|
||||
-- be a strict *partial* order, but since all weak orders are partial
|
||||
-- orders, this is not a problem.
|
||||
end)
|
||||
|
||||
for _, pair in ipairs(kvpairs) do
|
||||
local k, v = table.unpack(pair)
|
||||
|
||||
if (type(v) == "table") then
|
||||
print (indent_str .. tostring(k) .. ": ")
|
||||
dump_table(v, indent + 1)
|
||||
else
|
||||
print (indent_str .. tostring(k) .. ": " .. tostring(v))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local Debug = {
|
||||
dump_table = dump_table,
|
||||
}
|
||||
|
||||
local Id = {
|
||||
INVALID = 0xffffffff,
|
||||
ANY = 0xffffffff,
|
||||
}
|
||||
|
||||
local Features = {
|
||||
PipewireObject = {
|
||||
MINIMAL = 0x11,
|
||||
},
|
||||
ALL = 0xffffffff,
|
||||
}
|
||||
|
||||
local Feature = {
|
||||
Proxy = {
|
||||
BOUND = 1,
|
||||
},
|
||||
PipewireObject = {
|
||||
INFO = (1 << 4),
|
||||
PARAM_PROPS = (1 << 5),
|
||||
PARAM_FORMAT = (1 << 6),
|
||||
PARAM_PROFILE = (1 << 7),
|
||||
PARAM_PORT_CONFIG = (1 << 8),
|
||||
PARAM_ROUTE = (1 << 9),
|
||||
},
|
||||
SpaDevice = {
|
||||
ENABLED = (1 << 16),
|
||||
},
|
||||
Node = {
|
||||
PORTS = (1 << 16),
|
||||
},
|
||||
Session = {
|
||||
ENDPOINTS = (1 << 16),
|
||||
LINKS = (1 << 17),
|
||||
},
|
||||
Endpoint = {
|
||||
STREAMS = (1 << 16),
|
||||
},
|
||||
Metadata = {
|
||||
DATA = (1 << 16),
|
||||
},
|
||||
SessionItem = {
|
||||
ACTIVE = (1 << 0),
|
||||
EXPORTED = (1 << 1),
|
||||
},
|
||||
}
|
||||
|
||||
SANDBOX_EXPORT = {
|
||||
Debug = Debug,
|
||||
Id = Id,
|
||||
Features = Features,
|
||||
Feature = Feature,
|
||||
GLib = GLib,
|
||||
Log = WpLog,
|
||||
Core = WpCore,
|
||||
Plugin = WpPlugin,
|
||||
ObjectManager = WpObjectManager_new,
|
||||
Interest = WpObjectInterest_new,
|
||||
SessionItem = WpSessionItem_new,
|
||||
Constraint = Constraint,
|
||||
Device = WpDevice_new,
|
||||
SpaDevice = WpSpaDevice_new,
|
||||
Node = WpNode_new,
|
||||
LocalNode = WpImplNode_new,
|
||||
Link = WpLink_new,
|
||||
Pod = WpSpaPod,
|
||||
Json = WpSpaJson,
|
||||
State = WpState_new,
|
||||
LocalModule = WpImplModule_new,
|
||||
ImplMetadata = WpImplMetadata_new,
|
||||
}
|
165
modules/module-lua-scripting/api/config.c
Normal file
165
modules/module-lua-scripting/api/config.c
Normal file
@@ -0,0 +1,165 @@
|
||||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2020 Collabora Ltd.
|
||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <wp/wp.h>
|
||||
#include <wplua/wplua.h>
|
||||
|
||||
static gboolean
|
||||
load_components (lua_State *L, WpCore * core, GError ** error)
|
||||
{
|
||||
lua_getglobal (L, "SANDBOX_COMMON_ENV");
|
||||
|
||||
switch (lua_getfield (L, -1, "components")) {
|
||||
case LUA_TTABLE:
|
||||
break;
|
||||
case LUA_TNIL:
|
||||
wp_debug ("no components specified");
|
||||
goto done;
|
||||
default:
|
||||
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT,
|
||||
"Expected 'components' to be a table");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
lua_pushnil (L);
|
||||
while (lua_next (L, -2)) {
|
||||
/* value must be a table */
|
||||
if (lua_type (L, -1) != LUA_TTABLE) {
|
||||
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT,
|
||||
"'components' must be a table with tables as values");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* record indexes to the current key and value of the components table */
|
||||
int key = lua_absindex (L, -2);
|
||||
int table = lua_absindex (L, -1);
|
||||
|
||||
/* get component */
|
||||
if (lua_geti (L, table, 1) != LUA_TSTRING) {
|
||||
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT,
|
||||
"components['%s'] has a non-string or unspecified component name",
|
||||
lua_tostring (L, key));
|
||||
return FALSE;
|
||||
}
|
||||
const char * component = lua_tostring (L, -1);
|
||||
|
||||
/* get component type */
|
||||
if (lua_getfield (L, table, "type") != LUA_TSTRING) {
|
||||
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT,
|
||||
"components['%s'] has a non-string or unspecified component type",
|
||||
lua_tostring (L, key));
|
||||
return FALSE;
|
||||
}
|
||||
const char * type = lua_tostring (L, -1);
|
||||
|
||||
/* optional component arguments */
|
||||
GVariant *args = NULL;
|
||||
if (lua_getfield (L, table, "args") == LUA_TTABLE) {
|
||||
args = wplua_lua_to_gvariant (L, -1);
|
||||
}
|
||||
|
||||
gboolean optional = FALSE;
|
||||
if (lua_getfield (L, table, "optional") == LUA_TBOOLEAN) {
|
||||
optional = lua_toboolean (L, -1);
|
||||
}
|
||||
|
||||
wp_debug ("load component: %s (%s) optional(%s)",
|
||||
component, type, (optional ? "true" : "false"));
|
||||
|
||||
g_autoptr (GError) load_error = NULL;
|
||||
if (!wp_core_load_component (core, component, type, args, &load_error)) {
|
||||
if (!optional) {
|
||||
g_propagate_error (error, g_steal_pointer (&load_error));
|
||||
return FALSE;
|
||||
} else {
|
||||
wp_message ("%s", load_error->message);
|
||||
}
|
||||
}
|
||||
|
||||
/* clear the stack up to the key */
|
||||
lua_settop (L, key);
|
||||
}
|
||||
|
||||
done:
|
||||
lua_pop (L, 2); /* pop components & SANDBOX_COMMON_ENV */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
load_file (const GValue *item, GValue *ret, gpointer data)
|
||||
{
|
||||
lua_State *L = data;
|
||||
const gchar *path = g_value_get_string (item);
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
if (g_file_test (path, G_FILE_TEST_IS_DIR))
|
||||
return TRUE;
|
||||
|
||||
wp_info ("loading config file: %s", path);
|
||||
if (!wplua_load_path (L, path, 0, 0, &error)) {
|
||||
g_value_unset (ret);
|
||||
g_value_init (ret, G_TYPE_ERROR);
|
||||
g_value_take_boxed (ret, g_steal_pointer (&error));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_value_set_int (ret, g_value_get_int (ret) + 1);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define CONFIG_DIRS_LOOKUP_SET \
|
||||
(WP_LOOKUP_DIR_ENV_CONFIG | \
|
||||
WP_LOOKUP_DIR_XDG_CONFIG_HOME | \
|
||||
WP_LOOKUP_DIR_ETC | \
|
||||
WP_LOOKUP_DIR_PREFIX_SHARE)
|
||||
|
||||
gboolean
|
||||
wp_lua_scripting_load_configuration (const gchar * conf_file,
|
||||
WpCore * core, GError ** error)
|
||||
{
|
||||
g_autoptr (lua_State) L = wplua_new ();
|
||||
g_autofree gchar * path = NULL;
|
||||
g_autoptr (WpIterator) it = NULL;
|
||||
g_auto (GValue) fold_ret = G_VALUE_INIT;
|
||||
gint nfiles = 0;
|
||||
|
||||
wplua_enable_sandbox (L, WP_LUA_SANDBOX_MINIMAL_STD);
|
||||
|
||||
/* load conf_file itself */
|
||||
path = wp_find_file (CONFIG_DIRS_LOOKUP_SET, conf_file, NULL);
|
||||
if (path) {
|
||||
wp_info ("loading config file: %s", path);
|
||||
if (!wplua_load_path (L, path, 0, 0, error))
|
||||
return FALSE;
|
||||
nfiles = 1;
|
||||
}
|
||||
g_clear_pointer (&path, g_free);
|
||||
|
||||
path = g_strdup_printf ("%s.d", conf_file);
|
||||
it = wp_new_files_iterator (CONFIG_DIRS_LOOKUP_SET, path, ".lua");
|
||||
|
||||
g_value_init (&fold_ret, G_TYPE_INT);
|
||||
g_value_set_int (&fold_ret, nfiles);
|
||||
if (!wp_iterator_fold (it, load_file, &fold_ret, L)) {
|
||||
if (error && G_VALUE_HOLDS (&fold_ret, G_TYPE_ERROR))
|
||||
*error = g_value_dup_boxed (&fold_ret);
|
||||
return FALSE;
|
||||
}
|
||||
nfiles = g_value_get_int (&fold_ret);
|
||||
|
||||
if (nfiles == 0) {
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||
"Could not locate configuration file '%s'", conf_file);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!load_components (L, core, error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
6
modules/module-lua-scripting/api/gresource.xml
Normal file
6
modules/module-lua-scripting/api/gresource.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/org/freedesktop/pipewire/wireplumber/m-lua-scripting/">
|
||||
<file compressed="true">api.lua</file>
|
||||
</gresource>
|
||||
</gresources>
|
345
modules/module-lua-scripting/api/json.c
Normal file
345
modules/module-lua-scripting/api/json.c
Normal file
@@ -0,0 +1,345 @@
|
||||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2022 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <wp/wp.h>
|
||||
#include <wplua/wplua.h>
|
||||
|
||||
/* API */
|
||||
|
||||
static int
|
||||
spa_json_get_data (lua_State *L)
|
||||
{
|
||||
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
|
||||
lua_pushstring (L, wp_spa_json_get_data (json));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
spa_json_get_size (lua_State *L)
|
||||
{
|
||||
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
|
||||
lua_pushinteger (L, wp_spa_json_get_size (json));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
spa_json_is_null (lua_State *L)
|
||||
{
|
||||
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
|
||||
lua_pushboolean (L, wp_spa_json_is_null (json));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
spa_json_is_boolean (lua_State *L)
|
||||
{
|
||||
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
|
||||
lua_pushboolean (L, wp_spa_json_is_boolean (json));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
spa_json_is_int (lua_State *L)
|
||||
{
|
||||
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
|
||||
lua_pushboolean (L, wp_spa_json_is_int (json));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
spa_json_is_float (lua_State *L)
|
||||
{
|
||||
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
|
||||
lua_pushboolean (L, wp_spa_json_is_float (json));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
spa_json_is_string (lua_State *L)
|
||||
{
|
||||
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
|
||||
lua_pushboolean (L, wp_spa_json_is_string (json));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
spa_json_is_array (lua_State *L)
|
||||
{
|
||||
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
|
||||
lua_pushboolean (L, wp_spa_json_is_array (json));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
spa_json_is_object (lua_State *L)
|
||||
{
|
||||
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
|
||||
lua_pushboolean (L, wp_spa_json_is_object (json));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
push_luajson (lua_State *L, WpSpaJson *json)
|
||||
{
|
||||
/* Null */
|
||||
if (wp_spa_json_is_null (json)) {
|
||||
lua_pushnil (L);
|
||||
}
|
||||
|
||||
/* Boolean */
|
||||
else if (wp_spa_json_is_boolean (json)) {
|
||||
gboolean value = FALSE;
|
||||
g_warn_if_fail (wp_spa_json_parse_boolean (json, &value));
|
||||
lua_pushboolean (L, value);
|
||||
}
|
||||
|
||||
/* Int */
|
||||
else if (wp_spa_json_is_int (json)) {
|
||||
gint value = 0;
|
||||
g_warn_if_fail (wp_spa_json_parse_int (json, &value));
|
||||
lua_pushinteger (L, value);
|
||||
}
|
||||
|
||||
/* Float */
|
||||
else if (wp_spa_json_is_float (json)) {
|
||||
float value = 0;
|
||||
g_warn_if_fail (wp_spa_json_parse_float (json, &value));
|
||||
lua_pushnumber (L, value);
|
||||
}
|
||||
|
||||
/* String */
|
||||
else if (wp_spa_json_is_string (json)) {
|
||||
g_autofree gchar *value = wp_spa_json_parse_string (json);
|
||||
g_warn_if_fail (value);
|
||||
lua_pushstring (L, value);
|
||||
}
|
||||
|
||||
/* Array */
|
||||
else if (wp_spa_json_is_array (json)) {
|
||||
g_auto (GValue) item = G_VALUE_INIT;
|
||||
g_autoptr (WpIterator) it = wp_spa_json_new_iterator (json);
|
||||
guint i = 1;
|
||||
lua_newtable (L);
|
||||
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
|
||||
WpSpaJson *j = g_value_get_boxed (&item);
|
||||
push_luajson (L, j);
|
||||
lua_rawseti (L, -2, i++);
|
||||
}
|
||||
}
|
||||
|
||||
/* Object */
|
||||
else if (wp_spa_json_is_object (json)) {
|
||||
g_auto (GValue) item = G_VALUE_INIT;
|
||||
g_autoptr (WpIterator) it = wp_spa_json_new_iterator (json);
|
||||
lua_newtable (L);
|
||||
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
|
||||
WpSpaJson *key = g_value_get_boxed (&item);
|
||||
g_autofree gchar *key_str = NULL;
|
||||
WpSpaJson *value = NULL;
|
||||
g_warn_if_fail (wp_spa_json_is_string (key));
|
||||
key_str = wp_spa_json_parse_string (key);
|
||||
g_warn_if_fail (key_str);
|
||||
g_value_unset (&item);
|
||||
if (!wp_iterator_next (it, &item))
|
||||
break;
|
||||
value = g_value_get_boxed (&item);
|
||||
push_luajson (L, value);
|
||||
lua_setfield (L, -2, key_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
spa_json_parse (lua_State *L)
|
||||
{
|
||||
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
|
||||
push_luajson (L, json);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Raw */
|
||||
|
||||
static int
|
||||
spa_json_raw_new (lua_State *L)
|
||||
{
|
||||
const gchar *value = lua_tostring (L, 1);
|
||||
wplua_pushboxed (L, WP_TYPE_SPA_JSON, wp_spa_json_new_from_string (value));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* None */
|
||||
|
||||
static int
|
||||
spa_json_null_new (lua_State *L)
|
||||
{
|
||||
wplua_pushboxed (L, WP_TYPE_SPA_JSON, wp_spa_json_new_null ());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Boolean */
|
||||
|
||||
static int
|
||||
spa_json_boolean_new (lua_State *L)
|
||||
{
|
||||
gboolean value = lua_toboolean (L, 1);
|
||||
wplua_pushboxed (L, WP_TYPE_SPA_JSON, wp_spa_json_new_boolean (value));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Int */
|
||||
|
||||
static int
|
||||
spa_json_int_new (lua_State *L)
|
||||
{
|
||||
gint64 value = lua_tointeger (L, 1);
|
||||
wplua_pushboxed (L, WP_TYPE_SPA_JSON, wp_spa_json_new_int (value));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Float */
|
||||
|
||||
static int
|
||||
spa_json_float_new (lua_State *L)
|
||||
{
|
||||
float value = lua_tonumber (L, 1);
|
||||
wplua_pushboxed (L, WP_TYPE_SPA_JSON, wp_spa_json_new_float (value));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* String */
|
||||
|
||||
static int
|
||||
spa_json_string_new (lua_State *L)
|
||||
{
|
||||
const gchar *value = lua_tostring (L, 1);
|
||||
wplua_pushboxed (L, WP_TYPE_SPA_JSON, wp_spa_json_new_string (value));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Array */
|
||||
|
||||
static int
|
||||
spa_json_array_new (lua_State *L)
|
||||
{
|
||||
g_autoptr (WpSpaJsonBuilder) builder = wp_spa_json_builder_new_array ();
|
||||
|
||||
luaL_checktype (L, 1, LUA_TTABLE);
|
||||
|
||||
lua_pushnil (L);
|
||||
while (lua_next (L, 1)) {
|
||||
switch (lua_type (L, -1)) {
|
||||
case LUA_TBOOLEAN:
|
||||
wp_spa_json_builder_add_boolean (builder, lua_toboolean (L, -1));
|
||||
break;
|
||||
case LUA_TNUMBER:
|
||||
if (lua_isinteger (L, -1))
|
||||
wp_spa_json_builder_add_int (builder, lua_tointeger (L, -1));
|
||||
else
|
||||
wp_spa_json_builder_add_float (builder, lua_tonumber (L, -1));
|
||||
break;
|
||||
case LUA_TSTRING:
|
||||
wp_spa_json_builder_add_string (builder, lua_tostring (L, -1));
|
||||
break;
|
||||
case LUA_TUSERDATA: {
|
||||
WpSpaJson *json = wplua_checkboxed (L, -1, WP_TYPE_SPA_JSON);
|
||||
wp_spa_json_builder_add_json (builder, json);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
luaL_error (L, "Json does not support lua type ",
|
||||
lua_typename(L, lua_type(L, -1)));
|
||||
break;
|
||||
}
|
||||
|
||||
lua_pop (L, 1);
|
||||
}
|
||||
|
||||
wplua_pushboxed (L, WP_TYPE_SPA_JSON, wp_spa_json_builder_end (builder));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Object */
|
||||
|
||||
static int
|
||||
spa_json_object_new (lua_State *L)
|
||||
{
|
||||
g_autoptr (WpSpaJsonBuilder) builder = wp_spa_json_builder_new_object ();
|
||||
|
||||
luaL_checktype (L, 1, LUA_TTABLE);
|
||||
|
||||
lua_pushnil (L);
|
||||
while (lua_next (L, -2)) {
|
||||
wp_spa_json_builder_add_property (builder, lua_tostring (L, -2));
|
||||
|
||||
switch (lua_type (L, -1)) {
|
||||
case LUA_TBOOLEAN:
|
||||
wp_spa_json_builder_add_boolean (builder, lua_toboolean (L, -1));
|
||||
break;
|
||||
case LUA_TNUMBER:
|
||||
if (lua_isinteger (L, -1))
|
||||
wp_spa_json_builder_add_int (builder, lua_tointeger (L, -1));
|
||||
else
|
||||
wp_spa_json_builder_add_float (builder, lua_tonumber (L, -1));
|
||||
break;
|
||||
case LUA_TSTRING:
|
||||
wp_spa_json_builder_add_string (builder, lua_tostring (L, -1));
|
||||
break;
|
||||
case LUA_TUSERDATA: {
|
||||
WpSpaJson *json = wplua_checkboxed (L, -1, WP_TYPE_SPA_JSON);
|
||||
wp_spa_json_builder_add_json (builder, json);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
luaL_error (L, "Json does not support lua type ",
|
||||
lua_typename(L, lua_type(L, -1)));
|
||||
break;
|
||||
}
|
||||
|
||||
lua_pop (L, 1);
|
||||
}
|
||||
|
||||
wplua_pushboxed (L, WP_TYPE_SPA_JSON, wp_spa_json_builder_end (builder));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Init */
|
||||
|
||||
static const luaL_Reg spa_json_methods[] = {
|
||||
{ "get_data", spa_json_get_data },
|
||||
{ "get_size", spa_json_get_size },
|
||||
{ "is_null", spa_json_is_null },
|
||||
{ "is_boolean", spa_json_is_boolean },
|
||||
{ "is_int", spa_json_is_int },
|
||||
{ "is_float", spa_json_is_float },
|
||||
{ "is_string", spa_json_is_string },
|
||||
{ "is_array", spa_json_is_array },
|
||||
{ "is_object", spa_json_is_object },
|
||||
{ "parse", spa_json_parse },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const luaL_Reg spa_json_constructors[] = {
|
||||
{ "Raw", spa_json_raw_new },
|
||||
{ "Null", spa_json_null_new },
|
||||
{ "Boolean", spa_json_boolean_new },
|
||||
{ "Int", spa_json_int_new },
|
||||
{ "Float", spa_json_float_new },
|
||||
{ "String", spa_json_string_new },
|
||||
{ "Array", spa_json_array_new },
|
||||
{ "Object", spa_json_object_new },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
void
|
||||
wp_lua_scripting_json_init (lua_State *L)
|
||||
{
|
||||
luaL_newlib (L, spa_json_constructors);
|
||||
lua_setglobal (L, "WpSpaJson");
|
||||
|
||||
wplua_register_type_methods (L, WP_TYPE_SPA_JSON, NULL, spa_json_methods);
|
||||
}
|
5
modules/module-lua-scripting/api/meson.build
Normal file
5
modules/module-lua-scripting/api/meson.build
Normal file
@@ -0,0 +1,5 @@
|
||||
m_lua_scripting_resources = gnome.compile_resources(
|
||||
'm-lua-scripting-resources',
|
||||
'gresource.xml',
|
||||
source_dir: meson.current_source_dir(),
|
||||
c_name: '_m_lua_scripting_resources')
|
1234
modules/module-lua-scripting/api/pod.c
Normal file
1234
modules/module-lua-scripting/api/pod.c
Normal file
File diff suppressed because it is too large
Load Diff
201
modules/module-lua-scripting/api/require.c
Normal file
201
modules/module-lua-scripting/api/require.c
Normal file
@@ -0,0 +1,201 @@
|
||||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2021 Collabora Ltd.
|
||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <wp/wp.h>
|
||||
#include <wplua/wplua.h>
|
||||
|
||||
struct _WpRequireApiTransition
|
||||
{
|
||||
WpTransition parent;
|
||||
GPtrArray *apis;
|
||||
guint pending_plugins;
|
||||
};
|
||||
|
||||
enum {
|
||||
STEP_LOAD_MODULES = WP_TRANSITION_STEP_CUSTOM_START,
|
||||
STEP_ACTIVATE_PLUGINS,
|
||||
};
|
||||
|
||||
G_DECLARE_FINAL_TYPE (WpRequireApiTransition, wp_require_api_transition,
|
||||
WP, REQUIRE_API_TRANSITION, WpTransition)
|
||||
G_DEFINE_TYPE (WpRequireApiTransition, wp_require_api_transition, WP_TYPE_TRANSITION)
|
||||
|
||||
static void
|
||||
wp_require_api_transition_init (WpRequireApiTransition * self)
|
||||
{
|
||||
self->apis = g_ptr_array_new_with_free_func (g_free);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_require_api_transition_finalize (GObject * object)
|
||||
{
|
||||
WpRequireApiTransition *self = WP_REQUIRE_API_TRANSITION (object);
|
||||
|
||||
g_clear_pointer (&self->apis, g_ptr_array_unref);
|
||||
|
||||
G_OBJECT_CLASS (wp_require_api_transition_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static guint
|
||||
wp_require_api_transition_get_next_step (WpTransition * transition, guint step)
|
||||
{
|
||||
WpRequireApiTransition *self = WP_REQUIRE_API_TRANSITION (transition);
|
||||
|
||||
switch (step) {
|
||||
case WP_TRANSITION_STEP_NONE: return STEP_LOAD_MODULES;
|
||||
case STEP_LOAD_MODULES: return STEP_ACTIVATE_PLUGINS;
|
||||
case STEP_ACTIVATE_PLUGINS:
|
||||
return (self->pending_plugins > 0) ?
|
||||
STEP_ACTIVATE_PLUGINS : WP_TRANSITION_STEP_NONE;
|
||||
default:
|
||||
g_return_val_if_reached (WP_TRANSITION_STEP_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_plugin_activated (WpObject * p, GAsyncResult * res,
|
||||
WpRequireApiTransition *self)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!wp_object_activate_finish (p, res, &error)) {
|
||||
wp_transition_return_error (WP_TRANSITION (self), error);
|
||||
return;
|
||||
}
|
||||
|
||||
--self->pending_plugins;
|
||||
wp_transition_advance (WP_TRANSITION (self));
|
||||
}
|
||||
|
||||
static void
|
||||
wp_require_api_transition_execute_step (WpTransition * transition, guint step)
|
||||
{
|
||||
WpRequireApiTransition *self = WP_REQUIRE_API_TRANSITION (transition);
|
||||
WpCore *core = wp_transition_get_source_object (transition);
|
||||
|
||||
switch (step) {
|
||||
case STEP_LOAD_MODULES:
|
||||
{
|
||||
for (guint i = 0; i < self->apis->len; i++) {
|
||||
const gchar *api_name = g_ptr_array_index (self->apis, i);
|
||||
g_autoptr (WpPlugin) plugin = wp_plugin_find (core, api_name);
|
||||
if (!plugin) {
|
||||
GError *error = NULL;
|
||||
gchar module_name[50];
|
||||
|
||||
g_snprintf (module_name, sizeof (module_name),
|
||||
"libwireplumber-module-%s", api_name);
|
||||
|
||||
if (!wp_core_load_component (core, module_name, "module", NULL, &error)) {
|
||||
wp_transition_return_error (transition, error);
|
||||
return;
|
||||
}
|
||||
|
||||
plugin = wp_plugin_find (core, api_name);
|
||||
if (!plugin) {
|
||||
wp_transition_return_error (transition, g_error_new (
|
||||
WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT,
|
||||
"API '%s' was not found in module '%s'", api_name, module_name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
wp_transition_advance (transition);
|
||||
break;
|
||||
}
|
||||
|
||||
case STEP_ACTIVATE_PLUGINS:
|
||||
wp_debug_object (self, "Activating plugins...");
|
||||
|
||||
for (guint i = 0; i < self->apis->len; i++) {
|
||||
const gchar *api_name = g_ptr_array_index (self->apis, i);
|
||||
g_autoptr (WpPlugin) plugin = wp_plugin_find (core, api_name);
|
||||
|
||||
self->pending_plugins++;
|
||||
wp_object_activate (WP_OBJECT (plugin), WP_PLUGIN_FEATURE_ENABLED, NULL,
|
||||
(GAsyncReadyCallback) on_plugin_activated, self);
|
||||
}
|
||||
wp_transition_advance (transition);
|
||||
break;
|
||||
|
||||
case WP_TRANSITION_STEP_ERROR:
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_require_api_transition_class_init (WpRequireApiTransitionClass * klass)
|
||||
{
|
||||
GObjectClass * object_class = (GObjectClass *) klass;
|
||||
WpTransitionClass * transition_class = (WpTransitionClass *) klass;
|
||||
|
||||
object_class->finalize = wp_require_api_transition_finalize;
|
||||
|
||||
transition_class->get_next_step = wp_require_api_transition_get_next_step;
|
||||
transition_class->execute_step = wp_require_api_transition_execute_step;
|
||||
}
|
||||
|
||||
static void
|
||||
on_require_api_transition_done (WpCore * core, GAsyncResult * res, gpointer data)
|
||||
{
|
||||
g_autoptr (GClosure) closure = data;
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
if (!wp_transition_finish (res, &error)) {
|
||||
wp_warning ("Core.require_api failed: %s", error->message);
|
||||
wp_core_idle_add (core, NULL, G_SOURCE_FUNC (core_disconnect), core, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
WpRequireApiTransition *t = WP_REQUIRE_API_TRANSITION (res);
|
||||
g_autoptr (GArray) params = g_array_new (FALSE, TRUE, sizeof (GValue));
|
||||
|
||||
g_array_set_clear_func (params, (GDestroyNotify) g_value_unset);
|
||||
g_array_set_size (params, t->apis->len);
|
||||
|
||||
for (guint i = 0; i < t->apis->len; i++) {
|
||||
const gchar *api_name = g_ptr_array_index (t->apis, i);
|
||||
g_autoptr (WpPlugin) plugin = wp_plugin_find (core, api_name);
|
||||
g_value_init_from_instance (&g_array_index (params, GValue, i), plugin);
|
||||
}
|
||||
|
||||
g_closure_invoke (closure, NULL,
|
||||
params->len, (const GValue *) params->data, NULL);
|
||||
g_closure_invalidate (closure);
|
||||
}
|
||||
|
||||
static int
|
||||
wp_require_api_transition_new_from_lua (lua_State *L, WpCore * core)
|
||||
{
|
||||
int n_args = lua_gettop (L);
|
||||
|
||||
wp_info("n_args = %d", n_args);
|
||||
|
||||
for (int i = 1; i < n_args; i++)
|
||||
luaL_checktype (L, i, LUA_TSTRING);
|
||||
luaL_checktype (L, n_args, LUA_TFUNCTION);
|
||||
|
||||
GClosure *closure = wplua_function_to_closure (L, n_args);
|
||||
g_closure_ref (closure);
|
||||
g_closure_sink (closure);
|
||||
|
||||
WpRequireApiTransition *t = (WpRequireApiTransition *)
|
||||
wp_transition_new (wp_require_api_transition_get_type (), core, NULL,
|
||||
(GAsyncReadyCallback) on_require_api_transition_done, closure);
|
||||
|
||||
for (int i = 1; i < n_args; i++) {
|
||||
const char * api_name = lua_tostring (L, i);
|
||||
g_ptr_array_add (t->apis, g_strdup_printf ("%s-api", api_name));
|
||||
}
|
||||
|
||||
wp_transition_advance (WP_TRANSITION (t));
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user