docs: make progress on lua api documentation
This commit is contained in:
@@ -2,3 +2,121 @@
|
||||
|
||||
Core
|
||||
====
|
||||
|
||||
The :ref:`WpCore <core_api>` API is mostly transparent to lua, as the core
|
||||
object is not exposed to the scripts.
|
||||
|
||||
For some functionality, though, the following static functions are exposed.
|
||||
|
||||
.. function:: Core.get_info()
|
||||
|
||||
Returns a table with information about the core. The table contains
|
||||
the following fields:
|
||||
|
||||
=========== ===========
|
||||
Field Contains
|
||||
=========== ===========
|
||||
cookie The value of :c:func:`wp_core_get_remote_cookie`
|
||||
name The value of :c:func:`wp_core_get_remote_name`
|
||||
user_name The value of :c:func:`wp_core_get_remote_user_name`
|
||||
host_name The value of :c:func:`wp_core_get_remote_host_name`
|
||||
version The value of :c:func:`wp_core_get_remote_version`
|
||||
properties The value of :c:func:`wp_core_get_remote_properties`
|
||||
=========== ===========
|
||||
|
||||
:returns: information about the core
|
||||
:rtype: table
|
||||
|
||||
.. function:: Core.idle_add(callback)
|
||||
|
||||
Binds :c:func:`wp_core_idle_add_closure`
|
||||
|
||||
Schedules to call *callback* the next time that the event loop will be idle
|
||||
|
||||
:param function callback: the function to call; the function takes no
|
||||
arguments and must return true/false, as it is a GSourceFunc:
|
||||
return true to have the event loop call it again the next time it is idle,
|
||||
false to stop calling it and remove the associated GSource
|
||||
:returns: the GSource associated with this idle callback
|
||||
:rtype: GSource, see :func:`GSource.destroy`
|
||||
|
||||
.. function:: Core.timeout_add(timeout_ms, callback)
|
||||
|
||||
Binds :c:func:`wp_core_timeout_add_closure`
|
||||
|
||||
Schedules to call *callback* after *timeout_ms* milliseconds
|
||||
|
||||
:param function callback: the function to call; the function takes no
|
||||
arguments and must return true/false, as it is a GSourceFunc:
|
||||
return true to have the event loop call it again periodically every
|
||||
*timeout_ms* milliseconds, false to stop calling it and remove the
|
||||
associated GSource
|
||||
:returns: the GSource associated with this idle callback
|
||||
:rtype: GSource, see :func:`GSource.destroy`
|
||||
|
||||
.. function:: GSource.destroy(self)
|
||||
|
||||
For the purpose of working with :func:`Core.idle_add` and
|
||||
:func:`Core.timeout_add`, the GSource object that is returned by those
|
||||
functions contains this method.
|
||||
|
||||
Call this method to destroy the source, so that the associated callback
|
||||
is not called again. This can be used to stop a repeating timer callback
|
||||
or just to abort some idle operation
|
||||
|
||||
This method binds *g_source_destroy*
|
||||
|
||||
.. function:: Core.sync(callback)
|
||||
|
||||
Binds :c:func:`wp_core_sync`
|
||||
|
||||
Calls *callback* after synchronizing the transaction state with PipeWire
|
||||
|
||||
:param function callback: a function to be called after syncing with PipeWire;
|
||||
the function takes one argument that will be an error string, if something
|
||||
went wrong and nil otherwise and returns nothing
|
||||
|
||||
.. function:: Core.quit()
|
||||
|
||||
Quits the current *wpexec* process
|
||||
|
||||
.. note::
|
||||
|
||||
This can only be called when the script is running in *wpexec*;
|
||||
if it is running in the main WirePlumber daemon, it will print
|
||||
a warning and do nothing
|
||||
|
||||
.. function:: Core.require_api(..., callback)
|
||||
|
||||
Ensures that the specified API plugins are loaded.
|
||||
|
||||
API plugins are plugins that provide some API extensions for use in scripts.
|
||||
These plugins must always have their name end in "-api" and the names
|
||||
specified here must not have the "-api" extension.
|
||||
|
||||
For instance, the "mixer-api" module provides an API to change volume/mute
|
||||
controls from scripts, via action signals. It can be used like this:
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
Core.require_api("mixer", function(mixer)
|
||||
-- get the volume of node 35
|
||||
local volume = mixer:call("get-volume", 35)
|
||||
|
||||
-- the return value of "get-volume" is a GVariant(a{sv}),
|
||||
-- which gets translated to a Lua table
|
||||
Debug.dump_table(volume)
|
||||
end)
|
||||
|
||||
See also the example in :func:`GObject.call`
|
||||
|
||||
.. note::
|
||||
|
||||
This can only be called when the script is running in *wpexec*;
|
||||
if it is running in the main WirePlumber daemon, it will print
|
||||
a warning and do nothing
|
||||
|
||||
:param strings ...: a list of string arguments, which specify the names of
|
||||
the api plugins to load, if they are not already loaded
|
||||
:param callback: the function to call after the plugins have been loaded;
|
||||
this function takes references to the plugins as parameters
|
||||
|
@@ -10,7 +10,7 @@ to have a basic understanding of GObject's basic concepts, such as signals and
|
||||
properties.
|
||||
|
||||
Properties
|
||||
..........
|
||||
----------
|
||||
|
||||
All GObjects have the ability to have `properties`_.
|
||||
In C we normally use `g_object_get`_ to retrieve them and `g_object_set`_
|
||||
@@ -37,28 +37,259 @@ Writable properties can also be set in a similar fashion:
|
||||
mixer["scale"] = "cubic"
|
||||
|
||||
Signals
|
||||
.......
|
||||
-------
|
||||
|
||||
GObjects also have a generic mechanism to deliver events to external callbacks.
|
||||
These events are called `signals`_
|
||||
These events are called `signals`_.
|
||||
To connect to a signal and handle it, you may use the *connect* method:
|
||||
|
||||
All lua objects that wrap a GObject contain the following methods:
|
||||
|
||||
.. function:: connect(detailed_signal, callback)
|
||||
.. function:: GObject.connect(self, detailed_signal, callback)
|
||||
|
||||
Connects the signal to a callback. When the signal is emitted by the
|
||||
underlying object, the callback will be executed.
|
||||
|
||||
The signature of the callback is expected to match the signature of the
|
||||
signal, with the first parameter being the object itself.
|
||||
|
||||
**Example:**
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
-- connects the "bound" signal from WpProxy to a callback
|
||||
local proxy = function_that_returns_a_wp_proxy()
|
||||
proxy:connect("bound", function(p, id)
|
||||
print("Proxy " .. tostring(p) .. " bound to " .. tostring(id))
|
||||
end)
|
||||
|
||||
In this example, the ``p`` variable in the callback is the ``proxy`` object,
|
||||
while ``id`` is the first parameter of the *"bound"* signal, as documented
|
||||
in :c:struct:`WpProxy`
|
||||
|
||||
:param detailed_signal: the signal name to listen to
|
||||
(of the form "signal-name::detail")
|
||||
:param callback: a lua function that will be called when the signal is emitted
|
||||
|
||||
.. function:: call(action_signal, ...)
|
||||
Signals may also be used as a way to have dynamic methods on objects. These
|
||||
signals are meant to be called by external code and not handled. These signals
|
||||
are called **action signals**.
|
||||
You may call an action signal using the *call* method:
|
||||
|
||||
.. function:: GObject.call(self, action_signal, ...)
|
||||
|
||||
Calls an action signal on this object.
|
||||
|
||||
**Example:**
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
Core.require_api("default-nodes", "mixer", function(...)
|
||||
local default_nodes, mixer = ...
|
||||
|
||||
-- "get-default-node" and "get-volume" are action signals of the
|
||||
-- "default-nodes-api" and "mixer-api" plugins respectively
|
||||
local id = default_nodes:call("get-default-node", "Audio/Sink")
|
||||
local volume = mixer:call("get-volume", id)
|
||||
|
||||
-- the return value of "get-volume" is a GVariant(a{sv}),
|
||||
-- which gets translated to a Lua table
|
||||
Debug.dump_table(volume)
|
||||
end)
|
||||
|
||||
:param action_signal: the signal name to call
|
||||
:param ...: a list of arguments that will be passed to the signal
|
||||
:returns: the return value of the action signal, if any
|
||||
|
||||
Type conversions
|
||||
----------------
|
||||
|
||||
When working with GObject properties and signals, variables need to be
|
||||
converted from C types to Lua types and vice versa. The following tables
|
||||
list the type conversions that happen automatically:
|
||||
|
||||
C to Lua
|
||||
^^^^^^^^
|
||||
|
||||
Conversion from C to lua is based on the C type.
|
||||
|
||||
================================ ===============================================
|
||||
C Lua
|
||||
================================ ===============================================
|
||||
gchar, guchar, gint, guint integer
|
||||
glong, gulong, gint64, guint64 integer
|
||||
gfloat, gdouble number
|
||||
gboolean boolean
|
||||
gchar * string
|
||||
gpointer lightuserdata
|
||||
WpProperties * table (keys: string, values: string)
|
||||
enum string containing the nickname (short name) of
|
||||
the enum, or integer if the enum is not
|
||||
registered with GType
|
||||
flags integer (as in C)
|
||||
GVariant * a native type, see below
|
||||
other GObject, GInterface userdata holding reference to the object
|
||||
other GBoxed userdata holding reference to the object
|
||||
================================ ===============================================
|
||||
|
||||
Lua to C
|
||||
^^^^^^^^
|
||||
|
||||
Conversion from Lua to C is based on the expected type in C.
|
||||
|
||||
============================== ==================================================
|
||||
Expecting Lua
|
||||
============================== ==================================================
|
||||
gchar, guchar, gint, guint, convertible to integer
|
||||
glong, gulong, gint64, guint64 convertible to integer
|
||||
gfloat, gdouble convertible to number
|
||||
gboolean convertible to boolean
|
||||
gchar * convertible to string
|
||||
gpointer must be lightuserdata
|
||||
WpProperties * must be table (keys: string, values: convertible
|
||||
to string)
|
||||
enum must be string holding the nickname of the enum,
|
||||
or convertible to integer
|
||||
flags convertible to integer
|
||||
GVariant * see below
|
||||
other GObject, GInterface must be userdata holding a compatible GObject type
|
||||
other GBoxed must be userdata holding the same GBoxed type
|
||||
============================== ==================================================
|
||||
|
||||
GVariant to Lua
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
============================= =============================================
|
||||
GVariant Lua
|
||||
============================= =============================================
|
||||
NULL or G_VARIANT_TYPE_UNIT nil
|
||||
G_VARIANT_TYPE_INT16 integer
|
||||
G_VARIANT_TYPE_INT32 integer
|
||||
G_VARIANT_TYPE_INT64 integer
|
||||
G_VARIANT_TYPE_UINT16 integer
|
||||
G_VARIANT_TYPE_UINT32 integer
|
||||
G_VARIANT_TYPE_UINT64 integer
|
||||
G_VARIANT_TYPE_DOUBLE number
|
||||
G_VARIANT_TYPE_BOOLEAN boolean
|
||||
G_VARIANT_TYPE_STRING string
|
||||
G_VARIANT_TYPE_VARIANT converted recursively
|
||||
G_VARIANT_TYPE_DICTIONARY table (keys & values converted recursively)
|
||||
G_VARIANT_TYPE_ARRAY table (children converted recursively)
|
||||
============================= =============================================
|
||||
|
||||
Lua to GVariant
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Conversion from Lua to GVariant is based on the lua type and is quite limited.
|
||||
|
||||
There is no way to recover an array, for instance, because there is no way
|
||||
in Lua to tell if a table contains an array or a dictionary. All Lua tables
|
||||
are converted to dictionaries and integer keys are converted to strings.
|
||||
|
||||
========= ================================
|
||||
Lua GVariant
|
||||
========= ================================
|
||||
nil G_VARIANT_TYPE_UNIT
|
||||
boolean G_VARIANT_TYPE_BOOLEAN
|
||||
integer G_VARIANT_TYPE_INT64
|
||||
number G_VARIANT_TYPE_DOUBLE
|
||||
string G_VARIANT_TYPE_STRING
|
||||
table G_VARIANT_TYPE_VARDICT (a{sv})
|
||||
========= ================================
|
||||
|
||||
Closures
|
||||
--------
|
||||
|
||||
When a C function is expecting a GClosure, in Lua it is possible to pass
|
||||
a Lua function directly. The function is then wrapped into a custom GClosure.
|
||||
|
||||
When this GClosure is invalidated, the reference to the Lua function is dropped.
|
||||
Similarly, when the lua engine is stopped, all the GClosures that were
|
||||
created by this engine are invalidated.
|
||||
|
||||
Reference counting
|
||||
------------------
|
||||
|
||||
GObject references in Lua always hold a reference to the underlying GObject.
|
||||
When moving this reference around to other variables in Lua, the underlying
|
||||
GObject reference is shared, but Lua reference counts the wrapper "userdata"
|
||||
object.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
-- creating a new FooObject instance; obj holds the GObject reference
|
||||
local obj = FooObject()
|
||||
|
||||
-- GObject reference is dropped and FooObject is finalized
|
||||
obj = nil
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
-- creating a new FooObject instance; obj holds the GObject reference
|
||||
local obj = FooObject()
|
||||
|
||||
function store_global(o)
|
||||
-- o is now stored in the global 'obj_global' variable
|
||||
-- the GObject ref count is still 1
|
||||
obj_global = o
|
||||
end
|
||||
|
||||
-- obj userdata reference is passed to o, the GObject ref count is still 1
|
||||
store_global(obj)
|
||||
|
||||
-- userdata reference dropped from obj, the GObject is still alive
|
||||
obj = nil
|
||||
|
||||
-- userdata reference dropped from obj_global,
|
||||
-- the GObject ref is dropped and FooObject is finalized
|
||||
obj_global = nil
|
||||
|
||||
.. note::
|
||||
|
||||
When assigning a variable to nil, Lua may not immediately drop
|
||||
the reference of the underlying object. This is because Lua uses a garbage
|
||||
collector and goes through all the unreferenced objects to cleanup when
|
||||
the garbage collector runs.
|
||||
|
||||
When a GObject that is already referenced in Lua re-appears somewhere else
|
||||
through calling some API or because of a callback from C, a new reference is
|
||||
added on the GObject.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
-- ObjectManager is created in Lua, om holds 1 ref
|
||||
local om = ObjectManager(...)
|
||||
om:connect("objects-changed", function (om)
|
||||
-- om in this scope is a local function argument that was created
|
||||
-- by the signal's closure marshaller and holds a second reference
|
||||
-- to the ObjectManager
|
||||
|
||||
do_some_stuff()
|
||||
|
||||
-- this second reference is dropped when the function goes out of scope
|
||||
end)
|
||||
|
||||
.. danger::
|
||||
|
||||
Because Lua variables hold strong references to GObjects, it is dangerous
|
||||
to create closures that reference such variables, because these closures
|
||||
may create reference loops and **leak** objects
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
local om = ObjectManager(...)
|
||||
|
||||
om:connect("objects-changed", function (obj_mgr)
|
||||
-- using 'om' here instead of the local 'obj_mgr'
|
||||
-- creates a dangerous reference from the closure to 'om'
|
||||
for obj in om:iterate() do
|
||||
do_stuff(obj)
|
||||
end
|
||||
end)
|
||||
|
||||
-- local userdata reference dropped, but the GClosure that was generated
|
||||
-- from the above function is still holding a reference and keeps
|
||||
-- the ObjectManager alive; the GClosure is referenced by the ObjectManager
|
||||
-- because of the signal connection, so the ObjectManager is leaked
|
||||
om = nil
|
||||
|
||||
.. _GObject: https://developer.gnome.org/gobject/stable/
|
||||
.. _properties: https://developer.gnome.org/gobject/stable/gobject-properties.html
|
||||
|
68
docs/rst/lua_api/lua_introduction.rst
Normal file
68
docs/rst/lua_api/lua_introduction.rst
Normal file
@@ -0,0 +1,68 @@
|
||||
.. _lua_introduction:
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
`Lua <https://www.lua.org/>`_ is a powerful, efficient, lightweight,
|
||||
embeddable scripting language.
|
||||
|
||||
WirePlumber uses `Lua version 5.3 <https://www.lua.org/versions.html>`_ to
|
||||
implement its engine. Another, more recent, version may be considered
|
||||
in the future, but do note that different Lua versions are not API-compatible
|
||||
and that will likely also affect WirePlumber's Lua API.
|
||||
|
||||
There are currently two uses for Lua in WirePlumber:
|
||||
|
||||
- To implement the scripting engine
|
||||
- To implement lua-based :ref:`config files <daemon-configuration>`
|
||||
|
||||
This section is only documenting the API of the **scripting engine**
|
||||
|
||||
Lua Reference
|
||||
-------------
|
||||
|
||||
If you are not familiar with the Lua language and its API, please refer to
|
||||
the `Lua 5.3 Reference Manual <https://www.lua.org/manual/5.3/manual.html>`_
|
||||
|
||||
Sandbox
|
||||
-------
|
||||
|
||||
WirePlumber's scripting engine sandboxes the lua scripts to a safe environment.
|
||||
In this environment, the following rules apply:
|
||||
|
||||
- Scripts are isolated from one another; global variables in one script
|
||||
are not visible from another, even though they are actually executed in
|
||||
the same ``lua_State``
|
||||
|
||||
- Tables that hold API methods are not writable. While this may sound strange,
|
||||
standard Lua allows you to change standard API, for instance
|
||||
``string.format = rogue_format`` is valid outside the sandbox.
|
||||
WirePlumber does not allow that.
|
||||
|
||||
- The standard Lua API is limited only to safe functions. Functions that
|
||||
interact with the file system, the lua modules system, the lua state,
|
||||
the process's state, etc are **not** allowed.
|
||||
|
||||
Here is a full list of Lua functions (and API tables) that are exposed:
|
||||
|
||||
.. literalinclude:: ../../../lib/wplua/sandbox.lua
|
||||
:language: lua
|
||||
:lines: 40-55
|
||||
|
||||
- Object methods are not exposed in public tables. To call an object method
|
||||
you must use the method call syntax of Lua, i.e. ``object:method(params)``
|
||||
|
||||
The following, for instance, is **not** valid:
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
-- this will cause an exception
|
||||
local node = ...
|
||||
Node.send_command(node, "Suspend")
|
||||
|
||||
The correct form is this:
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
local node = ...
|
||||
node:send_command("Suspend")
|
51
docs/rst/lua_api/lua_log_api.rst
Normal file
51
docs/rst/lua_api/lua_log_api.rst
Normal file
@@ -0,0 +1,51 @@
|
||||
.. _lua_log_api:
|
||||
|
||||
Debug Logging
|
||||
=============
|
||||
|
||||
.. function:: Log.warning(object, message)
|
||||
|
||||
Logs a warning message, like :c:macro:`wp_warning_object`
|
||||
|
||||
:param GObject object: optional object to associate the message with; you
|
||||
may skip this and just start with the *message* as the first parameter
|
||||
:param string message: the warning message to log
|
||||
|
||||
.. function:: Log.message(object, message)
|
||||
|
||||
Logs a normal message, like :c:macro:`wp_message_object`
|
||||
|
||||
:param GObject object: optional object to associate the message with; you
|
||||
may skip this and just start with the *message* as the first parameter
|
||||
:param string message: the normal message to log
|
||||
|
||||
.. function:: Log.info(object, message)
|
||||
|
||||
Logs a info message, like :c:macro:`wp_info_object`
|
||||
|
||||
:param GObject object: optional object to associate the message with; you
|
||||
may skip this and just start with the *message* as the first parameter
|
||||
:param string message: the info message to log
|
||||
|
||||
.. function:: Log.debug(object, message)
|
||||
|
||||
Logs a debug message, like :c:macro:`wp_debug_object`
|
||||
|
||||
:param GObject object: optional object to associate the message with; you
|
||||
may skip this and just start with the *message* as the first parameter
|
||||
:param string message: the debug message to log
|
||||
|
||||
.. function:: Log.trace(object, message)
|
||||
|
||||
Logs a trace message, like :c:macro:`wp_trace_object`
|
||||
|
||||
:param GObject object: optional object to associate the message with; you
|
||||
may skip this and just start with the *message* as the first parameter
|
||||
:param string message: the trace message to log
|
||||
|
||||
.. function:: Debug.dump_table(t)
|
||||
|
||||
Prints a table with all its contents, recursively, to stdout
|
||||
for debugging purposes
|
||||
|
||||
:param table t: any table
|
@@ -2,6 +2,8 @@
|
||||
sphinx_files += files(
|
||||
'lua_core_api.rst',
|
||||
'lua_gobject.rst',
|
||||
'lua_introduction.rst',
|
||||
'lua_log_api.rst',
|
||||
'lua_object_api.rst',
|
||||
'lua_object_interest_api.rst',
|
||||
'lua_object_manager_api.rst',
|
||||
|
Reference in New Issue
Block a user