/* WirePlumber * * Copyright © 2020 Collabora Ltd. * @author George Kiagiadakis * * SPDX-License-Identifier: MIT */ #include #include #include #include #include #define WP_LOCAL_LOG_TOPIC log_topic_lua_scripting WP_LOG_TOPIC_EXTERN (log_topic_lua_scripting) #define URI_API "resource:///org/freedesktop/pipewire/wireplumber/m-lua-scripting/api.lua" void wp_lua_scripting_pod_init (lua_State *L); void wp_lua_scripting_json_init (lua_State *L); void push_luajson (lua_State *L, WpSpaJson *json, gint n_recursions); /* helpers */ static WpCore * get_wp_core (lua_State *L) { WpCore *core = NULL; lua_pushliteral (L, "wireplumber_core"); lua_gettable (L, LUA_REGISTRYINDEX); core = lua_touserdata (L, -1); lua_pop (L, 1); return core; } static WpCore * get_wp_export_core (lua_State *L) { WpCore *core = get_wp_core (L); g_autoptr (WpCore) export_core = wp_core_get_export_core (core); return export_core ? export_core : core; } /* GLib */ static int glib_get_monotonic_time (lua_State *L) { lua_pushinteger (L, g_get_monotonic_time ()); return 1; } static int glib_get_real_time (lua_State *L) { lua_pushinteger (L, g_get_real_time ()); return 1; } static gboolean access_parse_mode (const gchar * mode_str, gint *mode) { *mode = 0; if (!mode_str) return FALSE; else { for (guint i = 0; i < strlen (mode_str); i++) { switch (mode_str[i]) { case 'r': *mode |= R_OK; break; case 'w': *mode |= W_OK; break; case 'x': *mode |= X_OK; break; case 'f': *mode |= F_OK; break; case '-': break; default: return FALSE; } } } return TRUE; } static int glib_access (lua_State *L) { const gchar *filename = luaL_checkstring (L, 1); int mode = 0; if (!access_parse_mode (luaL_checkstring (L, 2), &mode)) luaL_error (L, "invalid mode string: '%s'", lua_tostring (L, 2)); lua_pushboolean (L, g_access (filename, mode) >= 0); return 1; } static const luaL_Reg glib_methods[] = { { "get_monotonic_time", glib_get_monotonic_time }, { "get_real_time", glib_get_real_time }, { "access", glib_access }, { NULL, NULL } }; /* 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 } }; /* i18n */ static int i18n_gettext (lua_State *L) { const gchar * msgid = luaL_checkstring (L, 1); lua_pushstring (L, dgettext (GETTEXT_PACKAGE, msgid)); return 1; } static int i18n_ngettext (lua_State *L) { const gchar * msgid = luaL_checkstring (L, 1); const gchar *msgid_plural = luaL_checkstring (L, 2); gulong n = luaL_checkinteger (L, 3); lua_pushstring (L, dngettext (GETTEXT_PACKAGE, msgid, msgid_plural, n)); return 1; } static const luaL_Reg i18n_funcs[] = { { "gettext", i18n_gettext }, { "ngettext", i18n_ngettext }, { NULL, NULL } }; /* WpCore */ static int core_get_properties (lua_State *L) { WpCore * core = get_wp_core (L); g_autoptr (WpProperties) p = wp_core_get_properties (core); wplua_properties_to_table (L, p); return 1; } 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_get_vm_type (lua_State *L) { WpCore * core = get_wp_core (L); g_autofree gchar *vm = wp_core_get_vm_type (core); lua_pushstring (L, vm); return 1; } static int core_get_own_bound_id (lua_State *L) { WpCore * core = get_wp_core (L); guint32 id = wp_core_get_own_bound_id (core); lua_pushinteger (L, id); 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 *daemon = wp_properties_get (p, "wireplumber.daemon"); if (!g_strcmp0 (daemon, "true")) { wp_warning ("script attempted to quit, but the engine is " "running in the wireplumber daemon; 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; } #include "require.c" static int core_require_api (lua_State *L) { WpCore * core = get_wp_core (L); g_autoptr (WpProperties) p = wp_core_get_properties (core); const gchar *daemon = wp_properties_get (p, "wireplumber.daemon"); if (!g_strcmp0 (daemon, "true")) { wp_warning ("script attempted to load an API module, but the engine is " "running in the wireplumber daemon; ignoring"); return 0; } return wp_require_api_transition_new_from_lua (L, core); } static int core_test_feature (lua_State *L) { WpCore *core = get_wp_core(L); const char *f = luaL_checkstring (L, 1); lua_pushboolean (L, wp_core_test_feature (core, f)); return 1; } static const luaL_Reg core_funcs[] = { { "get_properties", core_get_properties }, { "get_info", core_get_info }, { "get_vm_type", core_get_vm_type }, { "get_own_bound_id", core_get_own_bound_id }, { "idle_add", core_idle_add }, { "timeout_add", core_timeout_add }, { "sync", core_sync }, { "quit", core_quit }, { "require_api", core_require_api }, { "test_feature", core_test_feature }, { NULL, NULL } }; /* WpLog */ typedef WpLogTopic WpLuaLogTopic; static WpLuaLogTopic * wp_lua_log_topic_new (const char *name) { WpLuaLogTopic *topic = g_new0 (WpLuaLogTopic, 1); topic->topic_name = g_ref_string_new (name); wp_log_topic_register (topic); return topic; } static WpLuaLogTopic * wp_lua_log_topic_copy (WpLuaLogTopic *topic) { WpLuaLogTopic *copy = g_new0 (WpLuaLogTopic, 1); copy->topic_name = g_ref_string_acquire ((char *) copy->topic_name); wp_log_topic_register (copy); return copy; } static void wp_lua_log_topic_free (WpLuaLogTopic *topic) { wp_log_topic_unregister (topic); g_ref_string_release ((char *) topic->topic_name); g_free (topic); } G_DEFINE_BOXED_TYPE (WpLuaLogTopic, wp_lua_log_topic, wp_lua_log_topic_copy, wp_lua_log_topic_free) static int log_log (lua_State *L, GLogLevelFlags lvl) { lua_Debug ar = {0}; const gchar *message; gchar line_str[11]; gconstpointer instance = NULL; GType type = G_TYPE_INVALID; int index = 1; WpLogTopic *topic = log_topic_lua_scripting; /* if called with log topic object */ if (lua_istable (L, index)) { if (lua_getmetatable (L, index)) { lua_getfield (L, -1, "__topic"); if (wplua_isboxed (L, -1, wp_lua_log_topic_get_type ())) { topic = wplua_toboxed (L, -1); } lua_pop (L, 2); } index++; } if (!wp_log_topic_is_enabled (topic, lvl)) return 0; g_warn_if_fail (lua_getstack (L, 1, &ar) == 1); g_warn_if_fail (lua_getinfo (L, "nSl", &ar) == 1); if (wplua_isobject (L, index, G_TYPE_OBJECT)) { instance = wplua_toobject (L, index); type = G_TYPE_FROM_INSTANCE (instance); index++; } else if (wplua_isboxed (L, index, G_TYPE_BOXED)) { instance = wplua_toboxed (L, index); type = wplua_gvalue_userdata_type (L, index); index++; } message = luaL_checkstring (L, index); snprintf (line_str, 11, "%d", ar.currentline); ar.name = ar.name ? ar.name : "chunk"; wp_log_checked (topic->topic_name, 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_notice (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_obj_funcs[] = { { "warning", log_warning }, { "notice", log_notice }, { "info", log_info }, { "debug", log_debug }, { "trace", log_trace }, { NULL, NULL } }; static int log_open_topic (lua_State *L) { const char *name = luaL_checkstring (L, 1); WpLuaLogTopic *topic = wp_lua_log_topic_new (name); lua_newtable (L); // empty table lua_newtable (L); // metatable luaL_newlib (L, log_obj_funcs); lua_setfield (L, -2, "__index"); wplua_pushboxed (L, wp_lua_log_topic_get_type (), topic); lua_setfield (L, -2, "__topic"); lua_setmetatable (L, -2); return 1; } static const luaL_Reg log_funcs[] = { { "open_topic", log_open_topic }, { "warning", log_warning }, { "notice", log_notice }, { "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; } static const luaL_Reg plugin_funcs[] = { { "find", plugin_find }, { NULL, NULL } }; /* WpObject */ static void object_activate_done (WpObject *o, GAsyncResult * res, GClosure * closure) { g_autoptr (GError) error = NULL; GValue val[2] = { G_VALUE_INIT, G_VALUE_INIT }; int n_vals = 1; if (!wp_object_activate_finish (o, res, &error)) { wp_notice_object (o, "%s", error->message); if (closure) { g_value_init (&val[1], G_TYPE_STRING); g_value_set_string (&val[1], error->message); n_vals = 2; } } if (closure) { g_value_init_from_instance (&val[0], o); g_closure_invoke (closure, NULL, n_vals, val, NULL); g_value_unset (&val[0]); g_value_unset (&val[1]); g_closure_invalidate (closure); g_closure_unref (closure); } } static int object_activate (lua_State *L) { WpObject *o = wplua_checkobject (L, 1, WP_TYPE_OBJECT); WpObjectFeatures features = luaL_checkinteger (L, 2); GClosure * closure = luaL_opt (L, wplua_checkclosure, 3, NULL); if (closure) g_closure_sink (g_closure_ref (closure)); wp_object_activate (o, features, NULL, (GAsyncReadyCallback) object_activate_done, closure); return 0; } static int object_deactivate (lua_State *L) { WpObject *o = wplua_checkobject (L, 1, WP_TYPE_OBJECT); WpObjectFeatures features = luaL_checkinteger (L, 2); wp_object_deactivate (o, features); return 0; } static int object_get_active_features (lua_State *L) { WpObject *o = wplua_checkobject (L, 1, WP_TYPE_OBJECT); WpObjectFeatures features = wp_object_get_active_features (o); lua_pushinteger (L, features); return 1; } static int object_get_supported_features (lua_State *L) { WpObject *o = wplua_checkobject (L, 1, WP_TYPE_OBJECT); WpObjectFeatures features = wp_object_get_supported_features (o); lua_pushinteger (L, features); return 1; } static const luaL_Reg object_methods[] = { { "activate", object_activate }, { "deactivate", object_deactivate }, { "get_active_features", object_get_active_features }, { "get_supported_features", object_get_supported_features }, { 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 (it && 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; } /* Settings WpIterator */ static int settings_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)) { WpSettingsItem *si = g_value_get_boxed (&item); const gchar *k = wp_settings_item_get_key (si); WpSpaJson *v = wp_settings_item_get_value (si); lua_pushstring (L, k); wplua_pushboxed (L, WP_TYPE_SPA_JSON, v); return 2; } else { lua_pushnil (L); return 1; } } static int push_settings_wpiterator (lua_State *L, WpIterator *it) { lua_pushcfunction (L, settings_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)) { WpMetadataItem *mi = g_value_get_boxed (&item); guint32 s = wp_metadata_item_get_subject (mi); const gchar *k = wp_metadata_item_get_key (mi); const gchar *t = wp_metadata_item_get_value_type (mi); const gchar *v = wp_metadata_item_get_value (mi); 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 = WP_CONSTRAINT_TYPE_PW_GLOBAL_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_NOT_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 GType parse_gtype (const gchar *str) { g_autofree gchar *typestr = NULL; GType res = G_TYPE_INVALID; g_return_val_if_fail (str, res); /* "device" -> "WpDevice" */ typestr = g_strdup_printf ("Wp%s", str); if (typestr[2] != 0) { typestr[2] = g_ascii_toupper (typestr[2]); res = g_type_from_name (typestr); } return res; } static int object_interest_new_index (lua_State *L, int idx, GType def_type) { WpObjectInterest *interest = NULL; GType type = def_type; luaL_checktype (L, idx, LUA_TTABLE); /* type = "string" */ lua_pushliteral (L, "type"); if (lua_gettable (L, idx) == LUA_TSTRING) { type = parse_gtype (lua_tostring (L, -1)); if (type == G_TYPE_INVALID) luaL_error (L, "Interest: unknown type '%s'", lua_tostring (L, -1)); } else if (def_type == G_TYPE_INVALID) luaL_error (L, "Interest: expected 'type' as string"); lua_pop (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, idx)) { /* 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; } static int object_interest_new (lua_State *L) { return object_interest_new_index (L, 1, G_TYPE_INVALID); } static int object_interest_matches (lua_State *L) { WpObjectInterest *interest = wplua_checkboxed (L, 1, WP_TYPE_OBJECT_INTEREST); gboolean matches = FALSE; if (wplua_isobject (L, 2, G_TYPE_OBJECT)) { matches = wp_object_interest_matches (interest, wplua_toobject (L, 2)); } else if (lua_istable (L, 2)) { g_autoptr (WpProperties) props = wplua_table_to_properties (L, 2); matches = wp_object_interest_matches (interest, props); } else luaL_argerror (L, 2, "expected GObject or table"); lua_pushboolean (L, matches); return 1; } static const luaL_Reg object_interest_methods[] = { { "matches", object_interest_matches }, { NULL, NULL } }; static WpObjectInterest * get_optional_object_interest (lua_State *L, int idx, GType def_type) { if (lua_isnoneornil (L, idx)) return NULL; else if (lua_isuserdata (L, idx)) return wplua_checkboxed (L, idx, WP_TYPE_OBJECT_INTEREST); else if (lua_istable (L, idx)) { object_interest_new_index (L, idx, def_type); return wplua_toboxed (L, -1); } else { luaL_error (L, "expected Interest or none/nil"); return NULL; } } /* 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)) { WpObjectInterest *interest = wplua_checkboxed (L, -1, WP_TYPE_OBJECT_INTEREST); wp_object_manager_add_interest_full (om, wp_object_interest_ref (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_get_n_objects (lua_State *L) { WpObjectManager *om = wplua_checkobject (L, 1, WP_TYPE_OBJECT_MANAGER); lua_pushinteger (L, wp_object_manager_get_n_objects (om)); return 1; } static int object_manager_iterate (lua_State *L) { WpObjectManager *om = wplua_checkobject (L, 1, WP_TYPE_OBJECT_MANAGER); WpObjectInterest *oi = get_optional_object_interest (L, 2, G_TYPE_OBJECT); WpIterator *it = oi ? wp_object_manager_new_filtered_iterator_full (om, wp_object_interest_ref (oi)) : wp_object_manager_new_iterator (om); return push_wpiterator (L, it); } static int object_manager_lookup (lua_State *L) { WpObjectManager *om = wplua_checkobject (L, 1, WP_TYPE_OBJECT_MANAGER); WpObjectInterest *oi = get_optional_object_interest (L, 2, G_TYPE_OBJECT); WpObject *o = oi ? wp_object_manager_lookup_full (om, wp_object_interest_ref (oi)) : wp_object_manager_lookup (om, G_TYPE_OBJECT, NULL); if (o) { wplua_pushobject (L, o); return 1; } return 0; } static const luaL_Reg object_manager_methods[] = { { "activate", object_manager_activate }, { "get_n_objects", object_manager_get_n_objects }, { "iterate", object_manager_iterate }, { "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); WpIterator *it = wp_metadata_new_iterator (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 int metadata_set (lua_State *L) { WpMetadata *metadata = wplua_checkobject (L, 1, WP_TYPE_METADATA); lua_Integer subject = luaL_checkinteger (L, 2); const char *key = luaL_opt (L, luaL_checkstring, 3, NULL); const char *type = luaL_opt (L, luaL_checkstring, 4, NULL); const char *value = luaL_opt (L, luaL_checkstring, 5, NULL); wp_metadata_set (metadata, subject, key, type, value); return 0; } static const luaL_Reg metadata_methods[] = { { "iterate", metadata_iterate }, { "find", metadata_find }, { "set", metadata_set }, { NULL, NULL } }; /* WpImplMetadata */ static int impl_metadata_new (lua_State *L) { const char *name = luaL_checkstring (L, 1); WpProperties *properties = NULL; if (lua_type (L, 2) != LUA_TNONE && lua_type (L, 2) != LUA_TNIL) { luaL_checktype (L, 2, LUA_TTABLE); properties = wplua_table_to_properties (L, 2); } WpImplMetadata *m = wp_impl_metadata_new_full (get_wp_core (L), name, properties); if (m) wplua_pushobject (L, m); return m ? 1 : 0; } /* 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 && lua_type (L, 2) != LUA_TNIL) { 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); if (d) wplua_pushobject (L, d); return d ? 1 : 0; } /* 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 && lua_type (L, 2) != LUA_TNIL) { 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); if (d) wplua_pushobject (L, d); return d ? 1 : 0; } static int spa_device_iterate_managed_objects (lua_State *L) { WpSpaDevice *device = wplua_checkobject (L, 1, WP_TYPE_SPA_DEVICE); WpIterator *it = wp_spa_device_new_managed_object_iterator (device); return push_wpiterator (L, it); } 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[] = { { "iterate_managed_objects", spa_device_iterate_managed_objects }, { "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 && lua_type (L, 2) != LUA_TNIL) { 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); if (d) wplua_pushobject (L, d); return d ? 1 : 0; } static int node_get_state (lua_State *L) { WpNode *node = wplua_checkobject (L, 1, WP_TYPE_NODE); const gchar *error = NULL; WpNodeState state = wp_node_get_state (node, &error); wplua_enum_to_lua (L, state, WP_TYPE_NODE_STATE); lua_pushstring (L, error ? error : ""); return 2; } static int node_get_n_input_ports (lua_State *L) { WpNode *node = wplua_checkobject (L, 1, WP_TYPE_NODE); guint max = 0; guint ports = wp_node_get_n_input_ports (node, &max); lua_pushinteger (L, ports); lua_pushinteger (L, max); return 2; } static int node_get_n_output_ports (lua_State *L) { WpNode *node = wplua_checkobject (L, 1, WP_TYPE_NODE); guint max = 0; guint ports = wp_node_get_n_output_ports (node, &max); lua_pushinteger (L, ports); lua_pushinteger (L, max); return 2; } static int node_get_n_ports (lua_State *L) { WpNode *node = wplua_checkobject (L, 1, WP_TYPE_NODE); guint ports = wp_node_get_n_ports (node); lua_pushinteger (L, ports); return 1; } static int node_iterate_ports (lua_State *L) { WpNode *node = wplua_checkobject (L, 1, WP_TYPE_NODE); WpObjectInterest *oi = get_optional_object_interest (L, 2, WP_TYPE_PORT); WpIterator *it = oi ? wp_node_new_ports_filtered_iterator_full (node, wp_object_interest_ref (oi)) : wp_node_new_ports_iterator (node); return push_wpiterator (L, it); } static int node_lookup_port (lua_State *L) { WpNode *node = wplua_checkobject (L, 1, WP_TYPE_NODE); WpObjectInterest *oi = get_optional_object_interest (L, 2, WP_TYPE_PORT); WpPort *port = oi ? wp_node_lookup_port_full (node, wp_object_interest_ref (oi)) : wp_node_lookup_port (node, G_TYPE_OBJECT, NULL); if (port) { wplua_pushobject (L, port); return 1; } return 0; } 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[] = { { "get_state", node_get_state }, { "get_n_input_ports", node_get_n_input_ports }, { "get_n_output_ports", node_get_n_output_ports }, { "get_n_ports", node_get_n_ports }, { "iterate_ports", node_iterate_ports }, { "lookup_port", node_lookup_port }, { "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 && lua_type (L, 2) != LUA_TNIL) { 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); if (d) wplua_pushobject (L, d); return d ? 1 : 0; } /* Port */ static int port_get_direction (lua_State *L) { WpPort *port = wplua_checkobject (L, 1, WP_TYPE_PORT); WpDirection direction = wp_port_get_direction (port); wplua_enum_to_lua (L, direction, WP_TYPE_DIRECTION); return 1; } static const luaL_Reg port_methods[] = { { "get_direction", port_get_direction }, { NULL, NULL } }; /* Link */ static int link_new (lua_State *L) { const char *factory = luaL_checkstring (L, 1); WpProperties *properties = NULL; if (lua_type (L, 2) != LUA_TNONE && lua_type (L, 2) != LUA_TNIL) { luaL_checktype (L, 2, LUA_TTABLE); properties = wplua_table_to_properties (L, 2); } WpLink *l = wp_link_new_from_factory (get_wp_core (L), factory, properties); if (l) wplua_pushobject (L, l); return l ? 1 : 0; } /* Client */ static gboolean client_parse_permissions (const gchar * perms_str, guint32 *perms) { *perms = 0; if (!perms_str) return FALSE; else if (g_strcmp0 (perms_str, "all") == 0) *perms = PW_PERM_ALL; else { for (guint i = 0; i < strlen (perms_str); i++) { switch (perms_str[i]) { case 'r': *perms |= PW_PERM_R; break; case 'w': *perms |= PW_PERM_W; break; case 'x': *perms |= PW_PERM_X; break; case 'm': *perms |= PW_PERM_M; break; case 'l': *perms |= PW_PERM_L; break; case '-': break; default: return FALSE; } } } return TRUE; } static int client_update_permissions (lua_State *L) { WpClient *client = wplua_checkobject (L, 1, WP_TYPE_CLIENT); g_autoptr (GArray) arr = NULL; luaL_checktype (L, 2, LUA_TTABLE); lua_pushnil(L); while (lua_next (L, -2)) { struct pw_permission perm = {0}; if (lua_type (L, -2) == LUA_TSTRING && (!g_ascii_strcasecmp (lua_tostring(L, -2), "any") || !g_ascii_strcasecmp (lua_tostring(L, -2), "all"))) perm.id = PW_ID_ANY; else if (lua_isinteger (L, -2)) perm.id = lua_tointeger (L, -2); else luaL_error (L, "invalid key for permissions array"); if (!client_parse_permissions (lua_tostring (L, -1), &perm.permissions)) luaL_error (L, "invalid permission string: '%s'", lua_tostring (L, -1)); if (!arr) arr = g_array_new (FALSE, FALSE, sizeof (struct pw_permission)); g_array_append_val (arr, perm); lua_pop (L, 1); } wp_client_update_permissions_array (client, arr->len, (const struct pw_permission *) arr->data); return 0; } static int client_update_properties (lua_State *L) { WpClient *client = wplua_checkobject (L, 1, WP_TYPE_CLIENT); luaL_checktype (L, 2, LUA_TTABLE); WpProperties *properties = wplua_table_to_properties (L, 2); wp_client_update_properties (client, properties); return 0; } static int client_send_error (lua_State *L) { WpClient *client = wplua_checkobject (L, 1, WP_TYPE_CLIENT); guint id = luaL_checkinteger (L, 2); int res = luaL_checkinteger (L, 3); const char *message = luaL_checkstring (L, 4); wp_client_send_error (client, id, res, message); return 0; } static const luaL_Reg client_methods[] = { { "update_permissions", client_update_permissions }, { "update_properties", client_update_properties }, { "send_error", client_send_error }, { NULL, NULL } }; /* 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); if (si) wplua_pushobject (L, si); return si ? 1 : 0; } static int session_item_get_associated_proxy (lua_State *L) { WpSessionItem *si = wplua_checkobject (L, 1, WP_TYPE_SESSION_ITEM); const char *typestr = luaL_checkstring (L, 2); WpProxy *proxy = wp_session_item_get_associated_proxy (si, parse_gtype (typestr)); if (proxy) wplua_pushobject (L, proxy); return proxy ? 1 : 0; } 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); WpProperties *props = wp_properties_new_empty (); /* validate arguments */ luaL_checktype (L, 2, LUA_TTABLE); /* build the configuration properties */ lua_pushnil (L); while (lua_next (L, 2)) { const gchar *key = NULL; g_autofree gchar *var = NULL; switch (lua_type (L, -1)) { case LUA_TBOOLEAN: var = g_strdup_printf ("%u", lua_toboolean (L, -1)); break; case LUA_TNUMBER: if (lua_isinteger (L, -1)) var = g_strdup_printf ("%lld", lua_tointeger (L, -1)); else var = g_strdup_printf ("%f", lua_tonumber (L, -1)); break; case LUA_TSTRING: var = g_strdup (lua_tostring (L, -1)); break; case LUA_TUSERDATA: { GValue *v = lua_touserdata (L, -1); gpointer p = g_value_peek_pointer (v); var = g_strdup_printf ("%p", p); break; } default: luaL_error (L, "configure does not support lua type ", lua_typename(L, lua_type(L, -1))); break; } key = luaL_tolstring (L, -2, NULL); wp_properties_set (props, key, var); lua_pop (L, 2); } lua_pushboolean (L, wp_session_item_configure (si, props)); return 1; } static int session_item_register (lua_State *L) { WpSessionItem *si = wplua_checkobject (L, 1, WP_TYPE_SESSION_ITEM); wp_session_item_register (g_object_ref (si)); return 0; } static int session_item_remove (lua_State *L) { WpSessionItem *si = wplua_checkobject (L, 1, WP_TYPE_SESSION_ITEM); wp_session_item_remove (si); return 0; } static const luaL_Reg session_item_methods[] = { { "get_associated_proxy", session_item_get_associated_proxy }, { "reset", session_item_reset }, { "configure", session_item_configure }, { "register", session_item_register }, { "remove", session_item_remove }, { NULL, NULL } }; /* WpSiAdapter */ static int si_adapter_get_ports_format (lua_State *L) { WpSiAdapter *adapter = wplua_checkobject (L, 1, WP_TYPE_SI_ADAPTER); const gchar *mode = NULL; WpSpaPod *format = wp_si_adapter_get_ports_format (adapter, &mode); wplua_pushboxed (L, WP_TYPE_SPA_POD, format); lua_pushstring (L, mode); return 2; } static void si_adapter_set_ports_format_done (WpObject *o, GAsyncResult * res, GClosure * closure) { g_autoptr (GError) error = NULL; GValue val[2] = { G_VALUE_INIT, G_VALUE_INIT }; int n_vals = 1; if (!wp_si_adapter_set_ports_format_finish (WP_SI_ADAPTER (o), res, &error)) { wp_notice_object (o, "%s", error->message); if (closure) { g_value_init (&val[1], G_TYPE_STRING); g_value_set_string (&val[1], error->message); n_vals = 2; } } if (closure) { g_value_init_from_instance (&val[0], o); g_closure_invoke (closure, NULL, n_vals, val, NULL); g_value_unset (&val[0]); g_value_unset (&val[1]); g_closure_invalidate (closure); g_closure_unref (closure); } } static int si_adapter_set_ports_format (lua_State *L) { WpSiAdapter *adapter = wplua_checkobject (L, 1, WP_TYPE_SI_ADAPTER); WpSpaPod *format = wplua_checkboxed (L, 2, WP_TYPE_SPA_POD); const gchar *mode = luaL_checkstring (L, 3); GClosure * closure = luaL_opt (L, wplua_checkclosure, 4, NULL); if (closure) g_closure_sink (g_closure_ref (closure)); wp_si_adapter_set_ports_format (adapter, wp_spa_pod_ref (format), mode, (GAsyncReadyCallback) si_adapter_set_ports_format_done, closure); return 0; } static const luaL_Reg si_adapter_methods[] = { { "get_ports_format", si_adapter_get_ports_format }, { "set_ports_format", si_adapter_set_ports_format }, { NULL, NULL } }; /* WpPipewireObject */ static int pipewire_object_iterate_params (lua_State *L) { WpPipewireObject *pwobj = wplua_checkobject (L, 1, WP_TYPE_PIPEWIRE_OBJECT); const gchar *id = luaL_checkstring (L, 2); WpIterator *it = wp_pipewire_object_enum_params_sync (pwobj, id, NULL); return push_wpiterator (L, it); } static int pipewire_object_set_param (lua_State *L) { WpPipewireObject *pwobj = wplua_checkobject (L, 1, WP_TYPE_PIPEWIRE_OBJECT); const gchar *id = luaL_checkstring (L, 2); WpSpaPod *pod = wplua_checkboxed (L, 3, WP_TYPE_SPA_POD); wp_pipewire_object_set_param (pwobj, id, 0, wp_spa_pod_ref (pod)); return 0; } static const luaL_Reg pipewire_object_methods[] = { { "iterate_params", pipewire_object_iterate_params }, { "set_param" , pipewire_object_set_param }, { "set_params" , pipewire_object_set_param }, /* deprecated, compat only */ { NULL, NULL } }; /* WpState */ static int state_new (lua_State *L) { const gchar *name = luaL_checkstring (L, 1); WpState *state = wp_state_new (name); wplua_pushobject (L, state); return 1; } static int state_clear (lua_State *L) { WpState *state = wplua_checkobject (L, 1, WP_TYPE_STATE); wp_state_clear (state); return 0; } static int state_save (lua_State *L) { WpState *state = wplua_checkobject (L, 1, WP_TYPE_STATE); luaL_checktype (L, 2, LUA_TTABLE); g_autoptr (WpProperties) props = wplua_table_to_properties (L, 2); g_autoptr (GError) error = NULL; gboolean saved = wp_state_save (state, props, &error); lua_pushboolean (L, saved); lua_pushstring (L, error ? error->message : ""); return 2; } static int state_save_after_timeout (lua_State *L) { WpState *state = wplua_checkobject (L, 1, WP_TYPE_STATE); luaL_checktype (L, 2, LUA_TTABLE); g_autoptr (WpProperties) props = wplua_table_to_properties (L, 2); wp_state_save_after_timeout (state, get_wp_core (L), props); return 0; } static int state_load (lua_State *L) { WpState *state = wplua_checkobject (L, 1, WP_TYPE_STATE); g_autoptr (WpProperties) props = wp_state_load (state); wplua_properties_to_table (L, props); return 1; } static const luaL_Reg state_methods[] = { { "clear", state_clear }, { "save" , state_save }, { "save_after_timeout", state_save_after_timeout }, { "load" , state_load }, { NULL, NULL } }; /* ImplModule */ static int impl_module_new (lua_State *L) { const char *name, *args = NULL; WpProperties *properties = NULL; name = luaL_checkstring (L, 1); if (lua_type (L, 2) != LUA_TNONE && lua_type (L, 2) != LUA_TNIL) args = luaL_checkstring (L, 2); if (lua_type (L, 3) != LUA_TNONE && lua_type (L, 3) != LUA_TNIL) { luaL_checktype (L, 3, LUA_TTABLE); properties = wplua_table_to_properties (L, 3); } WpImplModule *m = wp_impl_module_load (get_wp_export_core (L), name, args, properties); if (m) { wplua_pushobject (L, m); return 1; } else { return 0; } } /* WpConf */ static int conf_get_section_as_properties (lua_State *L) { const char *section = luaL_checkstring (L, 1); g_autoptr (WpConf) conf = wp_core_get_conf (get_wp_core (L)); g_autoptr (WpSpaJson) s = NULL; g_autoptr (WpProperties) props = NULL; if (lua_istable (L, 2)) props = wplua_table_to_properties (L, 2); else props = wp_properties_new_empty (); if (conf) { s = wp_conf_get_section (conf, section); if (s && wp_spa_json_is_object (s)) wp_properties_update_from_json (props, s); } wplua_properties_to_table (L, props); return 1; } static int conf_get_section_as_object (lua_State *L) { const char *section = luaL_checkstring (L, 1); g_autoptr (WpConf) conf = wp_core_get_conf (get_wp_core (L)); g_autoptr (WpSpaJson) s = NULL; if (conf) { s = wp_conf_get_section (conf, section); if (s && wp_spa_json_is_object (s)) { push_luajson (L, s, INT_MAX); return 1; } } if (lua_istable (L, 2)) lua_pushvalue (L, 2); else lua_newtable (L); return 1; } static int conf_get_section_as_array (lua_State *L) { const char *section = luaL_checkstring (L, 1); g_autoptr (WpConf) conf = wp_core_get_conf (get_wp_core (L)); g_autoptr (WpSpaJson) s = NULL; if (conf) { s = wp_conf_get_section (conf, section); if (s && wp_spa_json_is_array (s)) { push_luajson (L, s, INT_MAX); return 1; } } if (lua_istable (L, 2)) lua_pushvalue (L, 2); else lua_newtable (L); return 1; } static int conf_get_section_as_json (lua_State *L) { const char *section = luaL_checkstring (L, 1); g_autoptr (WpConf) conf = NULL; g_autoptr (WpSpaJson) s = NULL; WpSpaJson *fb = NULL; if (lua_isuserdata (L, 2)) fb = wplua_checkboxed (L, 2, WP_TYPE_SPA_JSON); conf = wp_core_get_conf (get_wp_core (L)); if (conf) { s = wp_conf_get_section (conf, section); if (!s && fb) s = wp_spa_json_ref (fb); if (s) { wplua_pushboxed (L, WP_TYPE_SPA_JSON, wp_spa_json_ensure_unique_owner (g_steal_pointer (&s))); return 1; } } lua_pushnil (L); return 1; } static const luaL_Reg conf_methods[] = { { "get_section_as_properties", conf_get_section_as_properties }, { "get_section_as_object", conf_get_section_as_object }, { "get_section_as_array", conf_get_section_as_array }, { "get_section_as_json", conf_get_section_as_json }, { NULL, NULL } }; /* JsonUtils */ static gboolean json_utils_match_rules_cb (gpointer data, const gchar * action, WpSpaJson * value, GError ** error) { lua_State *L = data; g_autoptr (GError) pcall_err = NULL; gboolean ret = TRUE; int top = lua_gettop (L); /* this callback is called within the context of json_utils_match_rules() * and the Lua callback function is at the top of the stack at this point */ /* re-push the function because lua_call pops it; then, push arguments */ lua_pushvalue (L, -1); lua_pushstring (L, action); wplua_pushboxed (L, WP_TYPE_SPA_JSON, wp_spa_json_ref (value)); lua_call (L, 2, 2); ret = lua_toboolean (L, -2); if (!ret) { g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED, "%s", lua_tostring (L, -1)); } lua_settop (L, top); return ret; } static int json_utils_match_rules (lua_State *L) { g_autoptr (WpProperties) properties = NULL; g_autoptr (GError) error = NULL; WpSpaJson *json; gboolean res; json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON); luaL_checktype (L, 2, LUA_TTABLE); luaL_checktype (L, 3, LUA_TFUNCTION); properties = wplua_table_to_properties (L, 2); res = wp_json_utils_match_rules (json, properties, json_utils_match_rules_cb, L, &error); lua_pushboolean (L, res); if (error) lua_pushstring (L, error->message); else lua_pushnil (L); return 2; } static int json_utils_match_rules_update_properties (lua_State *L) { g_autoptr (WpProperties) properties = NULL; WpSpaJson *json; int count; json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON); luaL_checktype (L, 2, LUA_TTABLE); properties = wplua_table_to_properties (L, 2); count = wp_json_utils_match_rules_update_properties (json, properties); wplua_properties_to_table (L, properties); lua_pushinteger (L, count); return 2; } static const luaL_Reg json_utils_funcs[] = { { "match_rules", json_utils_match_rules }, { "match_rules_update_properties", json_utils_match_rules_update_properties }, { NULL, NULL } }; /* WpSettings */ static int settings_get (lua_State *L) { const char *setting = luaL_checkstring (L, 1); g_autoptr (WpSettings) s = wp_settings_find (get_wp_core (L), NULL); if (s) { WpSpaJson *j = wp_settings_get (s, setting); if (j) wplua_pushboxed (L, WP_TYPE_SPA_JSON, j); else lua_pushnil (L); } else lua_pushnil (L); return 1; } static int settings_get_boolean (lua_State *L) { const char *setting = luaL_checkstring (L, 1); g_autoptr (WpSettings) s = wp_settings_find (get_wp_core (L), NULL); gboolean val = FALSE; if (s) { g_autoptr (WpSpaJson) j = wp_settings_get (s, setting); if (j) wp_spa_json_parse_boolean (j, &val); } lua_pushboolean (L, val); return 1; } static int settings_get_int (lua_State *L) { const char *setting = luaL_checkstring (L, 1); g_autoptr (WpSettings) s = wp_settings_find (get_wp_core (L), NULL); gint val = 0; if (s) { g_autoptr (WpSpaJson) j = wp_settings_get (s, setting); if (j) wp_spa_json_parse_int (j, &val); } lua_pushinteger (L, val); return 1; } static int settings_get_float (lua_State *L) { const char *setting = luaL_checkstring (L, 1); g_autoptr (WpSettings) s = wp_settings_find (get_wp_core (L), NULL); float val = 0.0; if (s) { g_autoptr (WpSpaJson) j = wp_settings_get (s, setting); if (j) wp_spa_json_parse_float (j, &val); } lua_pushnumber (L, val); return 1; } static int settings_get_string (lua_State *L) { const char *setting = luaL_checkstring (L, 1); g_autoptr (WpSettings) s = wp_settings_find (get_wp_core (L), NULL); if (s) { g_autoptr (WpSpaJson) j = wp_settings_get (s, setting); if (j) { g_autofree gchar *val = wp_spa_json_parse_string (j); if (val) { lua_pushstring (L, val); return 1; } } } lua_pushstring (L, ""); return 1; } static int settings_get_array (lua_State *L) { const char *setting = luaL_checkstring (L, 1); g_autoptr (WpSettings) s = wp_settings_find (get_wp_core (L), NULL); g_autoptr (WpSpaJson) val = NULL; if (s) { g_autoptr (WpSpaJson) j = wp_settings_get (s, setting); if (j && wp_spa_json_is_array (j)) { push_luajson (L, j, INT_MAX); return 1; } } val = wp_spa_json_new_array (NULL, NULL); push_luajson (L, val, INT_MAX); return 1; } static int settings_get_object (lua_State *L) { const char *setting = luaL_checkstring (L, 1); g_autoptr (WpSettings) s = wp_settings_find (get_wp_core (L), NULL); g_autoptr (WpSpaJson) val = NULL; if (s) { g_autoptr (WpSpaJson) j = wp_settings_get (s, setting); if (j && wp_spa_json_is_object (j)) { push_luajson (L, j, INT_MAX); return 1; } } val = wp_spa_json_new_object (NULL, NULL, NULL); push_luajson (L, val, INT_MAX); return 1; } static int settings_get_saved (lua_State *L) { const char *setting = luaL_checkstring (L, 1); g_autoptr (WpSettings) s = wp_settings_find (get_wp_core (L), NULL); if (s) { WpSpaJson *j = wp_settings_get_saved (s, setting); if (j) wplua_pushboxed (L, WP_TYPE_SPA_JSON, j); else lua_pushnil (L); } else lua_pushnil (L); return 1; } static int settings_set (lua_State *L) { const char *key = luaL_checkstring (L, 1); WpSpaJson *val = wplua_checkboxed (L, 2, WP_TYPE_SPA_JSON); g_autoptr (WpSettings) s = wp_settings_find (get_wp_core (L), NULL); if (s) { lua_pushboolean (L, wp_settings_set (s, key, val)); } else { lua_pushboolean (L, FALSE); } return 1; } static int settings_reset (lua_State *L) { const char *key = luaL_checkstring (L, 1); g_autoptr (WpSettings) s = wp_settings_find (get_wp_core (L), NULL); if (s) { lua_pushboolean (L, wp_settings_reset (s, key)); } else { lua_pushboolean (L, FALSE); } return 1; } static int settings_save (lua_State *L) { const char *key = luaL_checkstring (L, 1); g_autoptr (WpSettings) s = wp_settings_find (get_wp_core (L), NULL); if (s) { lua_pushboolean (L, wp_settings_save (s, key)); } else { lua_pushboolean (L, FALSE); } return 1; } static int settings_delete (lua_State *L) { const char *key = luaL_checkstring (L, 1); g_autoptr (WpSettings) s = wp_settings_find (get_wp_core (L), NULL); if (s) { lua_pushboolean (L, wp_settings_delete (s, key)); } else { lua_pushboolean (L, FALSE); } return 1; } static int settings_reset_all (lua_State *L) { g_autoptr (WpSettings) s = wp_settings_find (get_wp_core (L), NULL); if (s) wp_settings_reset_all (s); return 0; } static int settings_save_all (lua_State *L) { g_autoptr (WpSettings) s = wp_settings_find (get_wp_core (L), NULL); if (s) wp_settings_save_all (s); return 0; } static int settings_delete_all (lua_State *L) { g_autoptr (WpSettings) s = wp_settings_find (get_wp_core (L), NULL); if (s) wp_settings_delete_all (s); return 0; } static int settings_iterate (lua_State *L) { g_autoptr (WpSettings) s = wp_settings_find (get_wp_core (L), NULL); WpIterator *it = wp_settings_new_iterator (s); return push_settings_wpiterator (L, it); } static int settings_subscribe (lua_State *L) { const gchar *pattern = luaL_checkstring (L, 1); g_autoptr (WpSettings) s = wp_settings_find (get_wp_core (L), NULL); guintptr sub_id = 0; GClosure * closure = wplua_function_to_closure (L, -1); if (s) sub_id = wp_settings_subscribe_closure (s, pattern, closure); lua_pushinteger (L, sub_id); return 1; } static int settings_unsubscribe (lua_State *L) { guintptr sub_id = luaL_checkinteger (L, 1); gboolean ret = FALSE; g_autoptr (WpSettings) s = wp_settings_find (get_wp_core (L), NULL); if (s) ret = wp_settings_unsubscribe (s, sub_id); lua_pushboolean (L, ret); return 1; } static const luaL_Reg settings_methods[] = { { "get", settings_get }, { "get_boolean", settings_get_boolean }, { "get_int", settings_get_int }, { "get_float", settings_get_float }, { "get_string", settings_get_string }, { "get_array", settings_get_array }, { "get_object", settings_get_object }, { "get_saved", settings_get_saved }, { "set", settings_set }, { "reset", settings_reset }, { "save", settings_save }, { "delete", settings_delete }, { "reset_all", settings_reset_all }, { "save_all", settings_save_all }, { "delete_all", settings_delete_all }, { "iterate", settings_iterate }, { "subscribe", settings_subscribe }, { "unsubscribe", settings_unsubscribe }, { NULL, NULL } }; /* WpEvent */ static int event_get_properties (lua_State *L) { WpEvent *event = wplua_checkboxed (L, 1, WP_TYPE_EVENT); g_autoptr (WpProperties) props = wp_event_get_properties (event); wplua_properties_to_table (L, props); return 1; } static int event_get_source (lua_State *L) { WpEvent *event = wplua_checkboxed (L, 1, WP_TYPE_EVENT); wplua_pushobject (L, wp_event_get_source (event)); return 1; } static int event_get_subject (lua_State *L) { WpEvent *event = wplua_checkboxed (L, 1, WP_TYPE_EVENT); wplua_pushobject (L, wp_event_get_subject (event)); return 1; } static int event_stop_processing (lua_State *L) { WpEvent *event = wplua_checkboxed (L, 1, WP_TYPE_EVENT); wp_event_stop_processing (event); return 0; } static int event_set_data (lua_State *L) { WpEvent *event = wplua_checkboxed (L, 1, WP_TYPE_EVENT); const gchar *key = luaL_checkstring (L, 2); GType type = G_TYPE_INVALID; g_auto (GValue) value = G_VALUE_INIT; const GValue *data = NULL; switch (lua_type (L, 3)) { case LUA_TNONE: case LUA_TNIL: break; case LUA_TUSERDATA: type = wplua_gvalue_userdata_type (L, 3); if (G_UNLIKELY (type == G_TYPE_INVALID)) wp_warning ("cannot set userdata on event data (not GValue userdata)"); break; case LUA_TBOOLEAN: type = G_TYPE_BOOLEAN; break; case LUA_TNUMBER: type = lua_isinteger (L, 3) ? G_TYPE_INT64 : G_TYPE_DOUBLE; break; case LUA_TSTRING: type = G_TYPE_STRING; break; case LUA_TTABLE: type = WP_TYPE_PROPERTIES; break; default: wp_warning ("cannot set value on event data (value type not supported)"); break; } if (type != G_TYPE_INVALID) { g_value_init (&value, type); wplua_lua_to_gvalue (L, 3, &value); data = &value; } wp_event_set_data (event, key, data); return 0; } static int event_get_data (lua_State *L) { WpEvent *event = wplua_checkboxed (L, 1, WP_TYPE_EVENT); const gchar *key = luaL_checkstring (L, 2); const GValue *data = wp_event_get_data (event, key); if (data) wplua_gvalue_to_lua (L, data); else lua_pushnil (L); return 1; } static const luaL_Reg event_methods[] = { { "get_properties", event_get_properties }, { "get_source", event_get_source }, { "get_subject", event_get_subject }, { "stop_processing", event_stop_processing }, { "set_data", event_set_data }, { "get_data", event_get_data }, { NULL, NULL } }; /* WpEventDispatcher */ static int event_dispatcher_push_event (lua_State *L) { const gchar *type = NULL; gint priority = 0; WpProperties *properties = NULL; GObject *source = NULL; GObject *subject = NULL; WpEvent *event = NULL; if (lua_type (L, 1) == LUA_TTABLE) { lua_pushliteral (L, "type"); if (lua_gettable (L, 1) != LUA_TSTRING) luaL_error (L, "EventDispatcher.push_event: expected 'type' as string"); type = lua_tostring (L, -1); // not popping to keep the string allocated lua_pushliteral (L, "priority"); if (lua_gettable (L, 1) != LUA_TNUMBER) luaL_error (L, "EventDispatcher.push_event: expected 'priority' as number"); priority = lua_tointeger (L, -1); lua_pop (L, 1); lua_pushliteral (L, "properties"); if (lua_gettable (L, 1) != LUA_TNIL) { luaL_checktype (L, -1, LUA_TTABLE); properties = wplua_table_to_properties (L, -1); } lua_pop (L, 1); lua_pushliteral (L, "source"); if (lua_gettable (L, 1) != LUA_TNIL) source = wplua_checkobject (L, -1, G_TYPE_OBJECT); lua_pop (L, 1); lua_pushliteral (L, "subject"); if (lua_gettable (L, 1) != LUA_TNIL) subject = wplua_checkobject (L, -1, G_TYPE_OBJECT); lua_pop (L, 1); event = wp_event_new (type, priority, properties, source, subject); } else { event = wp_event_ref (wplua_checkboxed (L, 1, WP_TYPE_EVENT)); } g_autoptr (WpEventDispatcher) dispatcher = wp_event_dispatcher_get_instance (get_wp_core (L)); wp_event_dispatcher_push_event (dispatcher, wp_event_ref (event)); wplua_pushboxed (L, WP_TYPE_EVENT, event); return 1; } static const luaL_Reg event_dispatcher_funcs[] = { { "push_event", event_dispatcher_push_event }, { NULL, NULL } }; /* WpEventHook */ static int event_hook_register (lua_State *L) { WpEventHook *hook = wplua_checkobject (L, 1, WP_TYPE_EVENT_HOOK); g_autoptr (WpEventDispatcher) dispatcher = wp_event_dispatcher_get_instance (get_wp_core (L)); wp_event_dispatcher_register_hook (dispatcher, hook); return 0; } static int event_hook_remove (lua_State *L) { WpEventHook *hook = wplua_checkobject (L, 1, WP_TYPE_EVENT_HOOK); g_autoptr (WpEventDispatcher) dispatcher = wp_event_dispatcher_get_instance (get_wp_core (L)); wp_event_dispatcher_unregister_hook (dispatcher, hook); return 0; } static const luaL_Reg event_hook_methods[] = { { "register", event_hook_register }, { "remove", event_hook_remove }, { NULL, NULL } }; /* WpSimpleEventHook */ static int simple_event_hook_new (lua_State *L) { WpEventHook *hook = NULL; int before_size = 0, after_size = 0, i = 0; const gchar **before, **after; const gchar *name; GClosure *closure = NULL; /* discard any possible arguments after the first one to avoid any surprises when working with absolute stack indices below */ lua_settop (L, 1); /* validate arguments */ luaL_checktype (L, 1, LUA_TTABLE); if (lua_getfield (L, 1, "name") != LUA_TSTRING) luaL_error(L, "SimpleEventHook: expected 'name' as string"); if (lua_getfield (L, 1, "execute") != LUA_TFUNCTION) luaL_error (L, "SimpleEventHook: expected 'execute' as function"); switch (lua_getfield (L, 1, "before")) { case LUA_TTABLE: lua_len (L, -1); before_size = lua_tointeger (L, -1); lua_pop (L, 1); break; case LUA_TSTRING: before_size = 1; break; case LUA_TNIL: before_size = 0; break; default: luaL_error(L, "SimpleEventHook: unexpected value type for 'before'; " "should be table or string"); } switch (lua_getfield (L, 1, "after")) { case LUA_TTABLE: lua_len (L, -1); after_size = lua_tointeger (L, -1); lua_pop (L, 1); break; case LUA_TSTRING: after_size = 1; break; case LUA_TNIL: after_size = 0; break; default: luaL_error(L, "SimpleEventHook: unexpected value type for 'after'; " "should be table or string"); } /* allocate C stack space for before & after arrays */ before = before_size > 0 ? (const gchar **) g_newa (gpointer, before_size + 1) : NULL; after = after_size > 0 ? (const gchar **) g_newa (gpointer, after_size + 1) : NULL; /* parse before */ if (lua_type (L, 4) == LUA_TTABLE && before_size > 0) { i = 0; lua_pushnil (L); while (lua_next (L, 4) && i < before_size) { before[i++] = luaL_checkstring (L, -1); /* bring the key on top without popping the string value */ lua_rotate (L, lua_gettop (L) - 1, 1); } before[i] = NULL; } else if (lua_type (L, 4) == LUA_TSTRING) { before[0] = lua_tostring (L, 4); before[1] = NULL; } /* parse after */ if (lua_type (L, 5) == LUA_TTABLE && after_size > 0) { i = 0; lua_pushnil (L); while (lua_next (L, 5) && i < after_size) { after[i++] = luaL_checkstring (L, -1); /* bring the key on top without popping the string value */ lua_rotate (L, lua_gettop (L) - 1, 1); } after[i] = NULL; } else if (lua_type (L, 5) == LUA_TSTRING) { after[0] = lua_tostring (L, 5); after[1] = NULL; } name = lua_tostring (L, 2); closure = wplua_function_to_closure (L, 3); hook = wp_simple_event_hook_new (name, before, after, closure); /* clear the lua stack now to make some space */ lua_settop (L, 1); wplua_pushobject (L, hook); if (lua_getfield (L, 1, "interests") == LUA_TTABLE) { lua_pushnil (L); while (lua_next (L, -2)) { WpObjectInterest *interest = wplua_checkboxed (L, -1, WP_TYPE_OBJECT_INTEREST); wp_interest_event_hook_add_interest_full (WP_INTEREST_EVENT_HOOK (hook), wp_object_interest_ref (interest)); lua_pop (L, 1); } } lua_pop (L, 1); return 1; } /* WpAsyncEventHook */ static int async_event_hook_get_next_step (lua_State *L) { WpTransition *transition = wplua_checkobject (L, 1, WP_TYPE_TRANSITION); guint step = luaL_checkinteger (L, 2); wp_trace_object (transition, "prev step: %u", step); if (step == WP_TRANSITION_STEP_NONE) { lua_pushinteger (L, WP_TRANSITION_STEP_CUSTOM_START); return 1; } /* step number is the value on the stack at this point */ if (G_UNLIKELY (lua_gettable (L, lua_upvalueindex (1)) != LUA_TSTRING)) { wp_critical_object (transition, "unknown step number"); lua_pushinteger (L, WP_TRANSITION_STEP_ERROR); return 1; } /* step string is now on the stack */ if (G_UNLIKELY (lua_gettable (L, lua_upvalueindex (1)) != LUA_TTABLE)) { wp_critical_object (transition, "unknown step string"); lua_pushinteger (L, WP_TRANSITION_STEP_ERROR); return 1; } lua_pushliteral (L, "next_idx"); if (G_UNLIKELY (lua_gettable (L, -2) != LUA_TNUMBER)) { wp_critical_object (transition, "next_idx not found"); lua_pushinteger (L, WP_TRANSITION_STEP_ERROR); return 1; } return 1; } static int async_event_hook_execute_step (lua_State *L) { WpTransition *transition = wplua_checkobject (L, 1, WP_TYPE_TRANSITION); WpEvent *event = wp_transition_get_data (transition); guint step = luaL_checkinteger (L, 2); const char *step_str = NULL; wp_trace_object (transition, "execute step: %u", step); if (G_LIKELY (step != WP_TRANSITION_STEP_ERROR)) { /* step_str = steps_table[step_number] */ /* step number is the value on the stack at this point */ if (G_UNLIKELY (lua_gettable (L, lua_upvalueindex (1)) != LUA_TSTRING)) { wp_critical_object (transition, "unknown step number %u", step); wp_transition_return_error (transition, g_error_new (WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVARIANT, "unknown step number %u", step)); return 0; } } else { /* try to execute a step called "error", if it exists */ lua_pushliteral (L, "error"); } step_str = lua_tostring (L, -1); /* step string is now on the stack */ if (G_UNLIKELY (lua_gettable (L, lua_upvalueindex (1)) != LUA_TTABLE)) { /* it's ok if the "error" step is missing */ if (step != WP_TRANSITION_STEP_ERROR) { wp_critical_object (transition, "unknown step string '%s'", step_str); wp_transition_return_error (transition, g_error_new (WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVARIANT, "unknown step string '%s", step_str)); } return 0; } lua_pushliteral (L, "execute"); if (G_UNLIKELY (lua_gettable (L, -2) != LUA_TFUNCTION)) { wp_critical_object (transition, "no execute function defined for '%s'", step_str); wp_transition_return_error (transition, g_error_new (WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVARIANT, "no execute function defined for '%s'", step_str)); return 0; } wplua_pushboxed (L, WP_TYPE_EVENT, wp_event_ref (event)); wplua_pushobject (L, g_object_ref (transition)); lua_call (L, 2, 0); return 0; } static void async_event_hook_prepare_steps_table (lua_State *L, int steps_tbl) { const char *step_str = NULL; int step_str_index = 0; int step = WP_TRANSITION_STEP_CUSTOM_START; steps_tbl = lua_absindex (L, steps_tbl); lua_pushliteral (L, "start"); step_str_index = lua_absindex (L, -1); step_str = lua_tostring (L, -1); while (step != WP_TRANSITION_STEP_NONE) { /* steps[step number] = step string */ lua_pushvalue (L, -1); lua_seti (L, steps_tbl, step); lua_pushvalue (L, -1); if (lua_gettable (L, steps_tbl) != LUA_TTABLE) luaL_error (L, "AsyncEventHook: expected '%s' in 'steps'", step_str); lua_pushinteger (L, step++); lua_setfield (L, -2, "idx"); lua_pushliteral (L, "next"); if (lua_gettable (L, -2) != LUA_TSTRING) luaL_error (L, "AsyncEventHook: expected 'next' in step '%s'", step_str); lua_replace (L, step_str_index); step_str = lua_tostring (L, step_str_index); if (!g_strcmp0 (step_str, "none")) step = WP_TRANSITION_STEP_NONE; lua_pushinteger (L, step); lua_setfield (L, -2, "next_idx"); lua_settop (L, step_str_index); } lua_pop (L, 1); } static int async_event_hook_new (lua_State *L) { WpEventHook *hook = NULL; int before_size = 0, after_size = 0, i = 0; const gchar **before, **after; const gchar *name; GClosure *get_next_step = NULL; GClosure *execute_step = NULL; /* discard any possible arguments after the first one to avoid any surprises when working with absolute stack indices below */ lua_settop (L, 1); /* validate arguments */ luaL_checktype (L, 1, LUA_TTABLE); if (lua_getfield (L, 1, "name") != LUA_TSTRING) luaL_error(L, "AsyncEventHook: expected 'name' as string"); if (lua_getfield (L, 1, "steps") != LUA_TTABLE) luaL_error (L, "AsyncEventHook: expected 'steps' as table"); switch (lua_getfield (L, 1, "before")) { case LUA_TTABLE: lua_len (L, -1); before_size = lua_tointeger (L, -1); lua_pop (L, 1); break; case LUA_TSTRING: before_size = 1; break; case LUA_TNIL: before_size = 0; break; default: luaL_error(L, "AsyncEventHook: unexpected value type for 'before'; " "should be table or string"); } switch (lua_getfield (L, 1, "after")) { case LUA_TTABLE: lua_len (L, -1); after_size = lua_tointeger (L, -1); lua_pop (L, 1); break; case LUA_TSTRING: after_size = 1; break; case LUA_TNIL: after_size = 0; break; default: luaL_error(L, "AsyncEventHook: unexpected value type for 'after'; " "should be table or string"); } /* allocate C stack space for before & after arrays */ before = before_size > 0 ? (const gchar **) g_newa (gpointer, before_size + 1) : NULL; after = after_size > 0 ? (const gchar **) g_newa (gpointer, after_size + 1) : NULL; /* parse before */ if (lua_type (L, 4) == LUA_TTABLE && before_size > 0) { i = 0; lua_pushnil (L); while (lua_next (L, 4) && i < before_size) { before[i++] = luaL_checkstring (L, -1); /* bring the key on top without popping the string value */ lua_rotate (L, lua_gettop (L) - 1, 1); } before[i] = NULL; } else if (lua_type (L, 4) == LUA_TSTRING) { before[0] = lua_tostring (L, 4); before[1] = NULL; } /* parse after */ if (lua_type (L, 5) == LUA_TTABLE && after_size > 0) { i = 0; lua_pushnil (L); while (lua_next (L, 5) && i < after_size) { after[i++] = luaL_checkstring (L, -1); /* bring the key on top without popping the string value */ lua_rotate (L, lua_gettop (L) - 1, 1); } after[i] = NULL; } else if (lua_type (L, 5) == LUA_TSTRING) { after[0] = lua_tostring (L, 5); after[1] = NULL; } name = lua_tostring (L, 2); async_event_hook_prepare_steps_table (L, 3); lua_pushvalue (L, 3); /* pass 'steps' table as upvalue */ lua_pushcclosure (L, async_event_hook_get_next_step, 1); get_next_step = wplua_function_to_closure (L, -1); lua_pop (L, 1); lua_pushvalue (L, 3); /* pass 'steps' table as upvalue */ lua_pushcclosure (L, async_event_hook_execute_step, 1); execute_step = wplua_function_to_closure (L, -1); lua_pop (L, 1); hook = wp_async_event_hook_new (name, before, after, get_next_step, execute_step); /* clear the lua stack now to make some space */ lua_settop (L, 1); wplua_pushobject (L, hook); if (lua_getfield (L, 1, "interests") == LUA_TTABLE) { lua_pushnil (L); while (lua_next (L, -2)) { WpObjectInterest *interest = wplua_checkboxed (L, -1, WP_TYPE_OBJECT_INTEREST); wp_interest_event_hook_add_interest_full (WP_INTEREST_EVENT_HOOK (hook), wp_object_interest_ref (interest)); lua_pop (L, 1); } } lua_pop (L, 1); return 1; } static int transition_advance (lua_State *L) { WpTransition *t = wplua_checkobject (L, 1, WP_TYPE_TRANSITION); wp_transition_advance (t); return 0; } static int transition_return_error (lua_State *L) { WpTransition *t = wplua_checkobject (L, 1, WP_TYPE_TRANSITION); const char *err = luaL_checkstring (L, 2); wp_transition_return_error (t, g_error_new (WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED, "%s", err)); return 0; } static const luaL_Reg transition_methods[] = { { "advance", transition_advance }, { "return_error", transition_return_error }, { NULL, NULL } }; void wp_lua_scripting_api_init (lua_State *L) { g_autoptr (GError) error = NULL; luaL_newlib (L, glib_methods); lua_setglobal (L, "GLib"); luaL_newlib (L, i18n_funcs); lua_setglobal (L, "I18n"); luaL_newlib (L, log_funcs); lua_setglobal (L, "WpLog"); luaL_newlib (L, core_funcs); lua_setglobal (L, "WpCore"); luaL_newlib (L, plugin_funcs); lua_setglobal (L, "WpPlugin"); luaL_newlib (L, conf_methods); lua_setglobal (L, "WpConf"); luaL_newlib (L, json_utils_funcs); lua_setglobal (L, "JsonUtils"); luaL_newlib (L, settings_methods); lua_setglobal (L, "WpSettings"); luaL_newlib (L, event_dispatcher_funcs); lua_setglobal (L, "WpEventDispatcher"); wp_lua_scripting_pod_init (L); wp_lua_scripting_json_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, object_interest_methods); 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_IMPL_METADATA, impl_metadata_new, NULL); 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_PORT, NULL, port_methods); wplua_register_type_methods (L, WP_TYPE_LINK, link_new, NULL); wplua_register_type_methods (L, WP_TYPE_CLIENT, NULL, client_methods); wplua_register_type_methods (L, WP_TYPE_SESSION_ITEM, session_item_new, session_item_methods); wplua_register_type_methods (L, WP_TYPE_SI_ADAPTER, NULL, si_adapter_methods); wplua_register_type_methods (L, WP_TYPE_PIPEWIRE_OBJECT, NULL, pipewire_object_methods); wplua_register_type_methods (L, WP_TYPE_STATE, state_new, state_methods); wplua_register_type_methods (L, WP_TYPE_IMPL_MODULE, impl_module_new, NULL); wplua_register_type_methods (L, WP_TYPE_EVENT, NULL, event_methods); wplua_register_type_methods (L, WP_TYPE_EVENT_HOOK, NULL, event_hook_methods); wplua_register_type_methods (L, WP_TYPE_SIMPLE_EVENT_HOOK, simple_event_hook_new, NULL); wplua_register_type_methods (L, WP_TYPE_ASYNC_EVENT_HOOK, async_event_hook_new, NULL); wplua_register_type_methods (L, WP_TYPE_TRANSITION, NULL, transition_methods); if (!wplua_load_uri (L, URI_API, &error) || !wplua_pcall (L, 0, 0, &error)) { wp_critical ("Failed to load api: %s", error->message); } }