/* WirePlumber * * Copyright © 2020 Collabora Ltd. * @author George Kiagiadakis * * SPDX-License-Identifier: MIT */ #include #include #define URI_API "resource:///org/freedesktop/pipewire/wireplumber/m-lua-scripting/api.lua" void wp_lua_scripting_pod_init (lua_State *L); /* helpers */ static WpCore * get_wp_core (lua_State *L) { lua_pushliteral (L, "wireplumber_core"); lua_gettable (L, LUA_REGISTRYINDEX); return lua_touserdata (L, -1); } static WpCore * get_wp_export_core (lua_State *L) { lua_pushliteral (L, "wireplumber_export_core"); lua_gettable (L, LUA_REGISTRYINDEX); return lua_touserdata (L, -1); } /* GSource */ static int source_destroy (lua_State *L) { GSource *source = wplua_checkboxed (L, 1, G_TYPE_SOURCE); g_source_destroy (source); return 0; } static const luaL_Reg source_methods[] = { { "destroy", source_destroy }, { NULL, NULL } }; /* WpCore */ static int core_get_info (lua_State *L) { WpCore * core = get_wp_core (L); g_autoptr (WpProperties) p = wp_core_get_remote_properties (core); lua_newtable (L); lua_pushinteger (L, wp_core_get_remote_cookie (core)); lua_setfield (L, -2, "cookie"); lua_pushstring (L, wp_core_get_remote_name (core)); lua_setfield (L, -2, "name"); lua_pushstring (L, wp_core_get_remote_user_name (core)); lua_setfield (L, -2, "user_name"); lua_pushstring (L, wp_core_get_remote_host_name (core)); lua_setfield (L, -2, "host_name"); lua_pushstring (L, wp_core_get_remote_version (core)); lua_setfield (L, -2, "version"); wplua_properties_to_table (L, p); lua_setfield (L, -2, "properties"); return 1; } static int core_idle_add (lua_State *L) { GSource *source = NULL; luaL_checktype (L, 1, LUA_TFUNCTION); wp_core_idle_add_closure (get_wp_core (L), &source, wplua_function_to_closure (L, 1)); wplua_pushboxed (L, G_TYPE_SOURCE, source); return 1; } static int core_timeout_add (lua_State *L) { GSource *source = NULL; lua_Integer timeout_ms = luaL_checkinteger (L, 1); luaL_checktype (L, 2, LUA_TFUNCTION); wp_core_timeout_add_closure (get_wp_core (L), &source, timeout_ms, wplua_function_to_closure (L, 2)); wplua_pushboxed (L, G_TYPE_SOURCE, source); return 1; } static void on_core_done (WpCore * core, GAsyncResult * res, GClosure * closure) { g_autoptr (GError) error = NULL; GValue val = G_VALUE_INIT; int n_vals = 0; if (!wp_core_sync_finish (core, res, &error)) { g_value_init (&val, G_TYPE_STRING); g_value_set_string (&val, error->message); n_vals = 1; } g_closure_invoke (closure, NULL, n_vals, &val, NULL); g_value_unset (&val); g_closure_invalidate (closure); g_closure_unref (closure); } static int core_sync (lua_State *L) { luaL_checktype (L, 1, LUA_TFUNCTION); GClosure * closure = wplua_function_to_closure (L, 1); g_closure_sink (g_closure_ref (closure)); wp_core_sync (get_wp_core (L), NULL, (GAsyncReadyCallback) on_core_done, closure); return 0; } static gboolean core_disconnect (WpCore * core) { wp_core_disconnect (core); return G_SOURCE_REMOVE; } static int core_quit (lua_State *L) { WpCore * core = get_wp_core (L); g_autoptr (WpProperties) p = wp_core_get_properties (core); const gchar *interactive = wp_properties_get (p, "wireplumber.interactive"); if (!interactive || g_strcmp0 (interactive, "true") != 0) { wp_warning ("script attempted to quit, but wireplumber " "is not running in script interactive mode; ignoring"); return 0; } /* wp_core_disconnect() will immediately destroy the lua plugin and the lua engine, so we cannot call it directly */ wp_core_idle_add (core, NULL, G_SOURCE_FUNC (core_disconnect), core, NULL); return 0; } static const luaL_Reg core_funcs[] = { { "get_info", core_get_info }, { "idle_add", core_idle_add }, { "timeout_add", core_timeout_add }, { "sync", core_sync }, { "quit", core_quit }, { NULL, NULL } }; /* WpDebug */ static int log_log (lua_State *L, GLogLevelFlags lvl) { lua_Debug ar; const gchar *message; gchar line_str[11]; gconstpointer instance = NULL; GType type = G_TYPE_INVALID; int index = 1; if (!wp_log_level_is_enabled (lvl)) return 0; lua_getstack (L, 1, &ar); lua_getinfo (L, "nSl", &ar); if (wplua_isobject (L, 1, G_TYPE_OBJECT)) { instance = wplua_toobject (L, 1); type = G_TYPE_FROM_INSTANCE (instance); index++; } message = luaL_checkstring (L, index); sprintf (line_str, "%d", ar.currentline); wp_log_structured_standard (G_LOG_DOMAIN, lvl, ar.source, line_str, ar.name, type, instance, "%s", message); return 0; } static int log_warning (lua_State *L) { return log_log (L, G_LOG_LEVEL_WARNING); } static int log_message (lua_State *L) { return log_log (L, G_LOG_LEVEL_MESSAGE); } static int log_info (lua_State *L) { return log_log (L, G_LOG_LEVEL_INFO); } static int log_debug (lua_State *L) { return log_log (L, G_LOG_LEVEL_DEBUG); } static int log_trace (lua_State *L) { return log_log (L, WP_LOG_LEVEL_TRACE); } static const luaL_Reg log_funcs[] = { { "warning", log_warning }, { "message", log_message }, { "info", log_info }, { "debug", log_debug }, { "trace", log_trace }, { NULL, NULL } }; /* WpPlugin */ static int plugin_find (lua_State *L) { const char *name = luaL_checkstring (L, 1); WpPlugin *plugin = wp_plugin_find (get_wp_core (L), name); if (plugin) wplua_pushobject (L, plugin); else lua_pushnil (L); return 1; } /* WpObject */ static void object_activate_done (WpObject *o, GAsyncResult *res, gpointer data) { g_autoptr (GError) error = NULL; if (!wp_object_activate_finish (o, res, &error)) { wp_warning_object (o, "failed to activate: %s", error->message); } } static int object_activate (lua_State *L) { WpObject *o = wplua_checkobject (L, 1, WP_TYPE_OBJECT); WpObjectFeatures features = 0; if (lua_type (L, 2) != LUA_TNONE) { features = luaL_checkinteger (L, 2); } else { features = WP_OBJECT_FEATURES_ALL; } wp_object_activate (o, features, NULL, (GAsyncReadyCallback) object_activate_done, NULL); return 0; } static const luaL_Reg object_methods[] = { { "activate", object_activate }, { NULL, NULL } }; /* WpProxy */ static int proxy_get_interface_type (lua_State *L) { WpProxy * p = wplua_checkobject (L, 1, WP_TYPE_PROXY); guint32 version = 0; const gchar *type = wp_proxy_get_interface_type (p, &version); lua_pushstring (L, type); lua_pushinteger (L, version); return 2; } static const luaL_Reg proxy_methods[] = { { "get_interface_type", proxy_get_interface_type }, { NULL, NULL } }; /* WpGlobalProxy */ static int global_proxy_request_destroy (lua_State *L) { WpGlobalProxy * p = wplua_checkobject (L, 1, WP_TYPE_GLOBAL_PROXY); wp_global_proxy_request_destroy (p); return 0; } static const luaL_Reg global_proxy_methods[] = { { "request_destroy", global_proxy_request_destroy }, { NULL, NULL } }; /* WpIterator */ static int iterator_next (lua_State *L) { WpIterator *it = wplua_checkboxed (L, 1, WP_TYPE_ITERATOR); g_auto (GValue) v = G_VALUE_INIT; if (wp_iterator_next (it, &v)) { return wplua_gvalue_to_lua (L, &v); } else { lua_pushnil (L); return 1; } } static int push_wpiterator (lua_State *L, WpIterator *it) { lua_pushcfunction (L, iterator_next); wplua_pushboxed (L, WP_TYPE_ITERATOR, it); return 2; } /* Metadata WpIterator */ static int metadata_iterator_next (lua_State *L) { WpIterator *it = wplua_checkboxed (L, 1, WP_TYPE_ITERATOR); g_auto (GValue) item = G_VALUE_INIT; if (wp_iterator_next (it, &item)) { guint32 s = 0; const gchar *k = NULL, *t = NULL, *v = NULL; wp_metadata_iterator_item_extract (&item, &s, &k, &t, &v); lua_pushinteger (L, s); lua_pushstring (L, k); lua_pushstring (L, t); lua_pushstring (L, v); return 4; } else { lua_pushnil (L); return 1; } } static int push_metadata_wpiterator (lua_State *L, WpIterator *it) { lua_pushcfunction (L, metadata_iterator_next); wplua_pushboxed (L, WP_TYPE_ITERATOR, it); return 2; } /* WpObjectInterest */ static GVariant * constraint_value_to_variant (lua_State *L, int idx) { switch (lua_type (L, idx)) { case LUA_TBOOLEAN: return g_variant_new_boolean (lua_toboolean (L, idx)); case LUA_TSTRING: return g_variant_new_string (lua_tostring (L, idx)); case LUA_TNUMBER: if (lua_isinteger (L, idx)) return g_variant_new_int64 (lua_tointeger (L, idx)); else return g_variant_new_double (lua_tonumber (L, idx)); default: return NULL; } } static void object_interest_new_add_constraint (lua_State *L, GType type, WpObjectInterest *interest) { int constraint_idx; WpConstraintType ctype; const gchar *subject; WpConstraintVerb verb; GVariant *value = NULL; constraint_idx = lua_absindex (L, -1); /* verify this is a Constraint{} */ if (lua_type (L, constraint_idx) != LUA_TTABLE) { luaL_error (L, "Interest: expected Constraint at index %d", lua_tointeger (L, -2)); } if (luaL_getmetafield (L, constraint_idx, "__name") == LUA_TNIL || g_strcmp0 (lua_tostring (L, -1), "Constraint") != 0) { luaL_error (L, "Interest: expected Constraint at index %d", lua_tointeger (L, -2)); } lua_pop (L, 1); /* get the constraint type */ lua_pushliteral (L, "type"); if (lua_gettable (L, constraint_idx) == LUA_TNUMBER) ctype = lua_tointeger (L, -1); else ctype = g_type_is_a (type, WP_TYPE_GLOBAL_PROXY) ? WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY : WP_CONSTRAINT_TYPE_G_PROPERTY; lua_pop (L, 1); /* get t[1] (the subject) and t[2] (the verb) */ lua_geti (L, constraint_idx, 1); subject = lua_tostring (L, -1); lua_geti (L, constraint_idx, 2); verb = lua_tostring (L, -1)[0]; switch (verb) { case WP_CONSTRAINT_VERB_EQUALS: case WP_CONSTRAINT_VERB_MATCHES: { lua_geti (L, constraint_idx, 3); value = constraint_value_to_variant (L, -1); if (G_UNLIKELY (!value)) luaL_error (L, "Constraint: bad value type"); break; } case WP_CONSTRAINT_VERB_IN_RANGE: { GVariant *values[2]; lua_geti (L, constraint_idx, 3); lua_geti (L, constraint_idx, 4); values[0] = constraint_value_to_variant (L, -2); values[1] = constraint_value_to_variant (L, -1); if (G_UNLIKELY (!values[0] || !values[1])) { g_clear_pointer (&values[0], g_variant_unref); g_clear_pointer (&values[1], g_variant_unref); luaL_error (L, "Constraint: bad value type"); } value = g_variant_new_tuple (values, 2); break; } case WP_CONSTRAINT_VERB_IN_LIST: { GPtrArray *values = g_ptr_array_new_with_free_func ((GDestroyNotify) g_variant_unref); int i = 3; while (lua_geti (L, constraint_idx, i++) != LUA_TNIL) { GVariant *tmp = constraint_value_to_variant (L, -1); if (G_UNLIKELY (!tmp)) { g_ptr_array_unref (values); luaL_error (L, "Constraint: bad value type"); } g_ptr_array_add (values, g_variant_ref_sink (tmp)); lua_pop (L, 1); } value = g_variant_new_tuple ((GVariant **) values->pdata, values->len); g_ptr_array_unref (values); break; } default: break; } wp_object_interest_add_constraint (interest, ctype, subject, verb, value); lua_settop (L, constraint_idx); } static int object_interest_new (lua_State *L) { WpObjectInterest *interest = NULL; GType type = 0; gchar *typestr; luaL_checktype (L, 1, LUA_TTABLE); /* type = "string" -> required */ lua_pushliteral (L, "type"); if (lua_gettable (L, -2) != LUA_TSTRING) luaL_error (L, "Interest: expected 'type' as string"); /* "device" -> "WpDevice" */ typestr = g_strdup_printf ("Wp%s", lua_tostring (L, -1)); if (typestr[2] != 0) { typestr[2] = g_ascii_toupper (typestr[2]); type = g_type_from_name (typestr); } g_free (typestr); lua_pop (L, 1); if (!type) luaL_error (L, "Interest: unknown type '%s'", lua_tostring (L, -1)); interest = wp_object_interest_new_type (type); wplua_pushboxed (L, WP_TYPE_OBJECT_INTEREST, interest); /* add constraints */ lua_pushnil (L); while (lua_next (L, 1)) { /* if the key isn't "type" */ if (!(lua_type (L, -2) == LUA_TSTRING && !g_strcmp0 ("type", lua_tostring (L, -2)))) object_interest_new_add_constraint (L, type, interest); lua_pop (L, 1); } return 1; } /* WpObjectManager */ static int object_manager_new (lua_State *L) { WpObjectManager *om; /* validate arguments */ luaL_checktype (L, 1, LUA_TTABLE); /* push to Lua asap to have a way to unref in case of error */ om = wp_object_manager_new (); wplua_pushobject (L, om); lua_pushnil (L); while (lua_next (L, 1)) { if (!wplua_isboxed (L, -1, WP_TYPE_OBJECT_INTEREST)) luaL_error (L, "ObjectManager: expected Interest"); /* steal the interest out of the GValue to avoid doing mem copy */ GValue *v = lua_touserdata (L, -1); wp_object_manager_add_interest_full (om, g_value_get_boxed (v)); memset (v, 0, sizeof (GValue)); g_value_init (v, WP_TYPE_OBJECT_INTEREST); lua_pop (L, 1); } /* request all the features for Lua scripts to make their job easier */ wp_object_manager_request_object_features (om, WP_TYPE_OBJECT, WP_OBJECT_FEATURES_ALL); return 1; } static int object_manager_activate (lua_State *L) { WpObjectManager *om = wplua_checkobject (L, 1, WP_TYPE_OBJECT_MANAGER); wp_core_install_object_manager (get_wp_core (L), om); return 0; } static int object_manager_iterate (lua_State *L) { WpObjectManager *om = wplua_checkobject (L, 1, WP_TYPE_OBJECT_MANAGER); WpIterator *it = wp_object_manager_iterate (om); return push_wpiterator (L, it); } static int object_manager_iterate_filtered (lua_State *L) { WpObjectManager *om = wplua_checkobject (L, 1, WP_TYPE_OBJECT_MANAGER); WpObjectInterest *oi = wplua_checkboxed (L, 2, WP_TYPE_OBJECT_INTEREST); WpIterator *it = wp_object_manager_iterate_filtered_full (om, wp_object_interest_ref (oi)); return push_wpiterator (L, it); } static int object_manager_lookup (lua_State *L) { WpObjectManager *om = wplua_checkobject (L, 1, WP_TYPE_OBJECT_MANAGER); WpObject *o = NULL; if (lua_isuserdata (L, 2)) { WpObjectInterest *oi = wplua_checkboxed (L, 2, WP_TYPE_OBJECT_INTEREST); o = wp_object_manager_lookup_full (om, wp_object_interest_ref (oi)); } else { o = wp_object_manager_lookup (om, WP_TYPE_OBJECT, NULL); } if (o) { wplua_pushobject (L, o); return 1; } return 0; } static const luaL_Reg object_manager_methods[] = { { "activate", object_manager_activate }, { "iterate", object_manager_iterate }, { "iterate_filtered", object_manager_iterate_filtered }, { "lookup", object_manager_lookup }, { NULL, NULL } }; /* WpMetadata */ static int metadata_iterate (lua_State *L) { WpMetadata *metadata = wplua_checkobject (L, 1, WP_TYPE_METADATA); lua_Integer subject = luaL_checkinteger (L, 2); g_autoptr (WpIterator) it = wp_metadata_iterate (metadata, subject); return push_metadata_wpiterator (L, it); } static int metadata_find (lua_State *L) { WpMetadata *metadata = wplua_checkobject (L, 1, WP_TYPE_METADATA); lua_Integer subject = luaL_checkinteger (L, 2); const char *key = luaL_checkstring (L, 3), *v = NULL, *t = NULL; v = wp_metadata_find (metadata, subject, key, &t); lua_pushstring (L, v); lua_pushstring (L, t); return 2; } static const luaL_Reg metadata_methods[] = { { "iterate", metadata_iterate }, { "find", metadata_find }, { NULL, NULL } }; /* WpSession */ static int session_iterate_endpoints (lua_State *L) { WpSession *session = wplua_checkobject (L, 1, WP_TYPE_SESSION); WpIterator *it = wp_session_iterate_endpoints (session); return push_wpiterator (L, it); } static int session_iterate_links (lua_State *L) { WpSession *session = wplua_checkobject (L, 1, WP_TYPE_SESSION); WpIterator *it = wp_session_iterate_links (session); return push_wpiterator (L, it); } static const luaL_Reg session_methods[] = { { "iterate_endpoints", session_iterate_endpoints }, { "iterate_links", session_iterate_links }, { NULL, NULL } }; /* WpImplSession */ static int impl_session_new (lua_State *L) { WpImplSession *session = wp_impl_session_new (get_wp_core (L)); wplua_pushobject (L, session); return 1; } static int impl_session_update_properties (lua_State *L) { WpImplSession *session = wplua_checkobject (L, 1, WP_TYPE_IMPL_SESSION); luaL_checktype (L, 2, LUA_TTABLE); WpProperties *props = wplua_table_to_properties (L, 2); wp_impl_session_update_properties (session, props); return 0; } static const luaL_Reg impl_session_methods[] = { { "update_properties", impl_session_update_properties }, { NULL, NULL } }; /* WpEndpoint */ static int endpoint_iterate_streams (lua_State *L) { WpEndpoint *ep = wplua_checkobject (L, 1, WP_TYPE_ENDPOINT); WpIterator *it = wp_endpoint_iterate_streams (ep); return push_wpiterator (L, it); } static int endpoint_create_link (lua_State *L) { WpEndpoint *ep = wplua_checkobject (L, 1, WP_TYPE_ENDPOINT); luaL_checktype (L, 2, LUA_TTABLE); WpProperties *props = wplua_table_to_properties (L, 2); wp_endpoint_create_link (ep, props); return 0; } static const luaL_Reg endpoint_methods[] = { { "iterate_streams", endpoint_iterate_streams }, { "create_link", endpoint_create_link }, { NULL, NULL } }; /* WpEndpointLink */ static int endpoint_link_get_state (lua_State *L) { WpEndpointLink *eplink = wplua_checkobject (L, 1, WP_TYPE_ENDPOINT_LINK); const gchar *error = NULL; WpEndpointLinkState state = wp_endpoint_link_get_state (eplink, &error); g_autoptr (GEnumClass) state_class = g_type_class_ref (WP_TYPE_ENDPOINT_LINK_STATE); lua_pushstring (L, g_enum_get_value (state_class, state)->value_nick); if (error) lua_pushstring (L, error); return error ? 2 : 1; } static int endpoint_link_request_state (lua_State *L) { WpEndpointLink *eplink = wplua_checkobject (L, 1, WP_TYPE_ENDPOINT_LINK); const gchar *states[] = { "inactive", "active" }; int state = luaL_checkoption (L, 2, NULL, states); wp_endpoint_link_request_state (eplink, (WpEndpointLinkState) (state+1)); return 0; } static int endpoint_link_get_linked_object_ids (lua_State *L) { WpEndpointLink *eplink = wplua_checkobject (L, 1, WP_TYPE_ENDPOINT_LINK); guint32 output_endpoint, output_stream; guint32 input_endpoint, input_stream; wp_endpoint_link_get_linked_object_ids (eplink, &output_endpoint, &output_stream, &input_endpoint, &input_stream); lua_pushinteger (L, output_endpoint); lua_pushinteger (L, output_stream); lua_pushinteger (L, input_endpoint); lua_pushinteger (L, input_stream); return 4; } static const luaL_Reg endpoint_link_methods[] = { { "get_state", endpoint_link_get_state }, { "request_state", endpoint_link_request_state }, { "get_linked_object_ids", endpoint_link_get_linked_object_ids }, { NULL, NULL } }; /* Device */ static int device_new (lua_State *L) { const char *factory = luaL_checkstring (L, 1); WpProperties *properties = NULL; if (lua_type (L, 2) != LUA_TNONE) { luaL_checktype (L, 2, LUA_TTABLE); properties = wplua_table_to_properties (L, 2); } WpDevice *d = wp_device_new_from_factory (get_wp_export_core (L), factory, properties); wplua_pushobject (L, d); return 1; } /* WpSpaDevice */ static int spa_device_new (lua_State *L) { const char *factory = luaL_checkstring (L, 1); WpProperties *properties = NULL; if (lua_type (L, 2) != LUA_TNONE) { luaL_checktype (L, 2, LUA_TTABLE); properties = wplua_table_to_properties (L, 2); } WpSpaDevice *d = wp_spa_device_new_from_spa_factory (get_wp_export_core (L), factory, properties); wplua_pushobject (L, d); return 1; } static int spa_device_get_managed_object (lua_State *L) { WpSpaDevice *device = wplua_checkobject (L, 1, WP_TYPE_SPA_DEVICE); guint id = luaL_checkinteger (L, 2); GObject *obj = wp_spa_device_get_managed_object (device, id); if (obj) wplua_pushobject (L, obj); return obj ? 1 : 0; } static int spa_device_store_managed_object (lua_State *L) { WpSpaDevice *device = wplua_checkobject (L, 1, WP_TYPE_SPA_DEVICE); guint id = luaL_checkinteger (L, 2); GObject *obj = (lua_type (L, 3) != LUA_TNIL) ? g_object_ref (wplua_checkobject (L, 3, G_TYPE_OBJECT)) : NULL; wp_spa_device_store_managed_object (device, id, obj); return 0; } static const luaL_Reg spa_device_methods[] = { { "get_managed_object", spa_device_get_managed_object }, { "store_managed_object", spa_device_store_managed_object }, { NULL, NULL } }; /* Node */ static int node_new (lua_State *L) { const char *factory = luaL_checkstring (L, 1); WpProperties *properties = NULL; if (lua_type (L, 2) != LUA_TNONE) { luaL_checktype (L, 2, LUA_TTABLE); properties = wplua_table_to_properties (L, 2); } WpNode *d = wp_node_new_from_factory (get_wp_export_core (L), factory, properties); wplua_pushobject (L, d); return 1; } static int node_send_command (lua_State *L) { WpNode *node = wplua_checkobject (L, 1, WP_TYPE_NODE); const char *command = luaL_checkstring (L, 2); wp_node_send_command (node, command); return 0; } static const luaL_Reg node_methods[] = { { "send_command", node_send_command }, { NULL, NULL } }; /* ImplNode */ static int impl_node_new (lua_State *L) { const char *factory = luaL_checkstring (L, 1); WpProperties *properties = NULL; if (lua_type (L, 2) != LUA_TNONE) { luaL_checktype (L, 2, LUA_TTABLE); properties = wplua_table_to_properties (L, 2); } WpImplNode *d = wp_impl_node_new_from_pw_factory (get_wp_export_core (L), factory, properties); wplua_pushobject (L, d); return 1; } /* WpSessionItem */ static int session_item_new (lua_State *L) { const char *type = luaL_checkstring (L, 1); WpSessionItem *si = wp_session_item_make (get_wp_core (L), type); wplua_pushobject (L, si); return 1; } static int session_item_reset (lua_State *L) { WpSessionItem *si = wplua_checkobject (L, 1, WP_TYPE_SESSION_ITEM); wp_session_item_reset (si); return 0; } static int session_item_configure (lua_State *L) { WpSessionItem *si = wplua_checkobject (L, 1, WP_TYPE_SESSION_ITEM); g_auto (GVariantBuilder) b = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT); GVariant *config = NULL; gboolean is_key = TRUE; const gchar *key = NULL; /* validate arguments */ luaL_checktype (L, 2, LUA_TTABLE); /* build the configuration */ lua_pushnil (L); while (lua_next (L, 2)) { if (is_key) { is_key = FALSE; key = lua_tostring (L, -1); } else { is_key = TRUE; switch (lua_type (L, -1)) { case LUA_TBOOLEAN: g_variant_builder_add (&b, "{sv}", key, g_variant_new_boolean (lua_toboolean (L, -1))); break; case LUA_TNUMBER: g_variant_builder_add (&b, "{sv}", key, g_variant_new_int64 (lua_tointeger (L, -1))); break; case LUA_TSTRING: g_variant_builder_add (&b, "{sv}", key, g_variant_new_string (lua_tostring (L, -1))); break; case LUA_TUSERDATA: { GValue *v = lua_touserdata (L, -1); gpointer p = NULL; if (G_VALUE_HOLDS_OBJECT (v)) { p = g_value_get_object (v); } else if (G_VALUE_HOLDS_BOXED (v)) { p = g_value_get_boxed (v); } else if (G_VALUE_HOLDS_POINTER (v)) { p = g_value_get_pointer (v); } else { luaL_error (L, "Key '%s' does not hold a valid pointer", key); break; } g_variant_builder_add (&b, "{sv}", key, g_variant_new_uint64 ((guint64)p)); break; } default: luaL_error (L, "Key '%s' with value type '%s' is not supported", key, lua_typename(L, lua_type(L, -1))); break; } } lua_pop (L, 1); } config = g_variant_builder_end (&b); lua_pushboolean (L, wp_session_item_configure (si, config)); return 1; } static int session_item_activate (lua_State *L) { WpSessionItem *si = wplua_checkobject (L, 1, WP_TYPE_SESSION_ITEM); GClosure *closure = wplua_function_to_closure (L, 2); wp_session_item_activate_closure (si, closure); return 0; } static int session_item_deactivate (lua_State *L) { WpSessionItem *si = wplua_checkobject (L, 1, WP_TYPE_SESSION_ITEM); wp_session_item_deactivate (si); return 0; } static int session_item_export (lua_State *L) { WpSessionItem *si = wplua_checkobject (L, 1, WP_TYPE_SESSION_ITEM); WpSession *session = wplua_checkobject (L, 2, WP_TYPE_SESSION); GClosure *closure = wplua_function_to_closure (L, 3); wp_session_item_export_closure (si, session, closure); return 0; } static int session_item_unexport (lua_State *L) { WpSessionItem *si = wplua_checkobject (L, 1, WP_TYPE_SESSION_ITEM); wp_session_item_unexport (si); return 0; } static const luaL_Reg session_item_methods[] = { { "reset", session_item_reset }, { "configure", session_item_configure }, { "activate", session_item_activate }, { "deactivate", session_item_deactivate }, { "export", session_item_export }, { "unexport", session_item_unexport }, { NULL, NULL } }; void wp_lua_scripting_api_init (lua_State *L) { g_autoptr (GError) error = NULL; luaL_newlib (L, log_funcs); lua_setglobal (L, "WpDebug"); luaL_newlib (L, core_funcs); lua_setglobal (L, "WpCore"); lua_pushcfunction (L, plugin_find); lua_setglobal (L, "WpPlugin_find"); wp_lua_scripting_pod_init (L); wplua_register_type_methods (L, G_TYPE_SOURCE, NULL, source_methods); wplua_register_type_methods (L, WP_TYPE_OBJECT, NULL, object_methods); wplua_register_type_methods (L, WP_TYPE_PROXY, NULL, proxy_methods); wplua_register_type_methods (L, WP_TYPE_GLOBAL_PROXY, NULL, global_proxy_methods); wplua_register_type_methods (L, WP_TYPE_OBJECT_INTEREST, object_interest_new, NULL); wplua_register_type_methods (L, WP_TYPE_OBJECT_MANAGER, object_manager_new, object_manager_methods); wplua_register_type_methods (L, WP_TYPE_METADATA, NULL, metadata_methods); wplua_register_type_methods (L, WP_TYPE_SESSION, NULL, session_methods); wplua_register_type_methods (L, WP_TYPE_IMPL_SESSION, impl_session_new, impl_session_methods); wplua_register_type_methods (L, WP_TYPE_ENDPOINT, NULL, endpoint_methods); wplua_register_type_methods (L, WP_TYPE_ENDPOINT_LINK, NULL, endpoint_link_methods); wplua_register_type_methods (L, WP_TYPE_DEVICE, device_new, NULL); wplua_register_type_methods (L, WP_TYPE_SPA_DEVICE, spa_device_new, spa_device_methods); wplua_register_type_methods (L, WP_TYPE_NODE, node_new, node_methods); wplua_register_type_methods (L, WP_TYPE_IMPL_NODE, impl_node_new, NULL); wplua_register_type_methods (L, WP_TYPE_SESSION_ITEM, session_item_new, session_item_methods); wplua_load_uri (L, URI_API, 0, 0, &error); if (G_UNLIKELY (error)) wp_critical ("Failed to load api: %s", error->message); }