feat: Implement introspection for sinks and sources
This provides a way to query the current state for the primary data structures, as well as common setters.
This commit is contained in:
parent
f8b2a45740
commit
366e08498e
17 changed files with 3607 additions and 327 deletions
|
@ -1,13 +1,27 @@
|
||||||
project = 'lgi-async-extra'
|
project = 'lua-libpulse-glib'
|
||||||
title = 'lgi-async-extra'
|
title = 'lua-libpulse-glib'
|
||||||
description = 'An asynchronous high(er)-level API wrapper for lua-lgi'
|
description = "Lua bindings for PulseAudio's libpulse, using the GLib Main Loop."
|
||||||
|
|
||||||
|
full_description = [[
|
||||||
|
While libpulse provides different event loop implementations, these bindings focus on the GLib comaptibility only.
|
||||||
|
Therefore, all asynchronous functions need to be executed inside a running GLib Main Loop.
|
||||||
|
For now, this loop has to run on GLib's default main context. Custom loop contexts are currently not supported.
|
||||||
|
|
||||||
|
The majority of the API is callback-based asynchronous. Callbacks always receive any potential errors as their
|
||||||
|
first parameter, usually just the error message as string, or `nil` when there was no error. Additional parameters
|
||||||
|
may either be just a boolean, for operations that don't return data, or the actual queried data.
|
||||||
|
|
||||||
|
All numeric indices (such as sink or source indices) are adjusted to be 1-based in typical Lua fashion.
|
||||||
|
This means that when comparing the output of calls like @{Context:get_sinks} to the output of tools like `pactl`,
|
||||||
|
indices will be off by one.
|
||||||
|
]]
|
||||||
|
|
||||||
template = true
|
template = true
|
||||||
|
|
||||||
format = 'discount'
|
format = 'discount'
|
||||||
|
|
||||||
pretty = 'lua'
|
pretty = 'lua'
|
||||||
prettify_files = 'show'
|
-- prettify_files = 'show'
|
||||||
backtick_references = false
|
backtick_references = false
|
||||||
wrap = true
|
wrap = true
|
||||||
no_space_before_args = true
|
no_space_before_args = true
|
||||||
|
|
51
src/lua_libpulse_glib/callback.c
Normal file
51
src/lua_libpulse_glib/callback.c
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#include "callback.h"
|
||||||
|
|
||||||
|
#include "pulseaudio.h"
|
||||||
|
|
||||||
|
|
||||||
|
simple_callback_data* prepare_lua_callback(lua_State* L) {
|
||||||
|
// Prepare a new thread to run the callback with
|
||||||
|
lua_pushstring(L, LUA_PULSEAUDIO);
|
||||||
|
lua_rawget(L, LUA_REGISTRYINDEX);
|
||||||
|
lua_pushstring(L, LUA_PA_REGISTRY);
|
||||||
|
lua_gettable(L, -2);
|
||||||
|
lua_State* thread = lua_newthread(L);
|
||||||
|
int thread_ref = luaL_ref(L, -2);
|
||||||
|
|
||||||
|
// Copy the callback function to the thread's stack
|
||||||
|
lua_pushvalue(L, 2);
|
||||||
|
lua_xmove(L, thread, 1);
|
||||||
|
|
||||||
|
simple_callback_data* data = malloc(sizeof(struct simple_callback_data));
|
||||||
|
data->L = thread;
|
||||||
|
data->thread_ref = thread_ref;
|
||||||
|
data->is_list = false;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void free_lua_callback(simple_callback_data* data) {
|
||||||
|
lua_State* L = data->L;
|
||||||
|
|
||||||
|
// Remove thread reference
|
||||||
|
lua_pushstring(L, LUA_PULSEAUDIO);
|
||||||
|
lua_rawget(L, LUA_REGISTRYINDEX);
|
||||||
|
lua_pushstring(L, LUA_PA_REGISTRY);
|
||||||
|
lua_gettable(L, -2);
|
||||||
|
luaL_unref(L, -1, data->thread_ref);
|
||||||
|
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void success_callback(pa_context* c, int success, void* userdata) {
|
||||||
|
simple_callback_data* data = (simple_callback_data*) userdata;
|
||||||
|
lua_State* L = data->L;
|
||||||
|
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushboolean(L, success);
|
||||||
|
lua_call(L, 2, 0);
|
||||||
|
|
||||||
|
free_lua_callback(data);
|
||||||
|
}
|
42
src/lua_libpulse_glib/callback.h
Normal file
42
src/lua_libpulse_glib/callback.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef callback_h_INCLUDED
|
||||||
|
#define callback_h_INCLUDED
|
||||||
|
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
#include <pulse/context.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef struct simple_callback_data {
|
||||||
|
lua_State* L;
|
||||||
|
int thread_ref;
|
||||||
|
// PulseAudio's introspection callbacks are used for operations that return single values, as well as operations
|
||||||
|
// that return a list. But the callback itself cannot know in which context it is called, so we have to provide
|
||||||
|
// that information explicitly.
|
||||||
|
bool is_list;
|
||||||
|
} simple_callback_data;
|
||||||
|
|
||||||
|
|
||||||
|
// Prepares a Lua thread that can call a Lua function as
|
||||||
|
// callback inside a PulseAudio callback.
|
||||||
|
//
|
||||||
|
// Assumes that the Lua function to call is at the top of the stack and copies it to the
|
||||||
|
// new thread.
|
||||||
|
//
|
||||||
|
// The thread will be kept in memory by a unique ref in the registry, so the callback has to
|
||||||
|
// `luaL_unref` that, to mark it for the garbage collector.
|
||||||
|
//
|
||||||
|
// The returned callback data needs to be `free()`d at the end of the callback. `free_lua_callback`
|
||||||
|
// handles both `free()` and `luaL_unref()`.
|
||||||
|
simple_callback_data* prepare_lua_callback(lua_State* L);
|
||||||
|
|
||||||
|
|
||||||
|
// Removes the thread reference, to allow the thread to be garbage collected, and `free`s
|
||||||
|
// the callback data.
|
||||||
|
void free_lua_callback(simple_callback_data* data);
|
||||||
|
|
||||||
|
|
||||||
|
// Simple implementation of `pa_context_success_cb_t` that calls a provided Lua function.
|
||||||
|
void success_callback(pa_context* c, int success, void* userdata);
|
||||||
|
|
||||||
|
#endif // callback_h_INCLUDED
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
|
|
||||||
#include "pulseaudio.h"
|
|
||||||
#include "context.h"
|
#include "context.h"
|
||||||
#include "pulse/context.h"
|
|
||||||
|
#include "callback.h"
|
||||||
|
#include "pulseaudio.h"
|
||||||
|
|
||||||
|
#include <pulse/context.h>
|
||||||
|
#include <pulse/error.h>
|
||||||
|
|
||||||
|
|
||||||
void
|
/* Calls the user-provided callback with the updates state info.
|
||||||
context_state_callback(pa_context* c, void* userdata)
|
*/
|
||||||
{
|
void context_state_callback(pa_context* c, void* userdata) {
|
||||||
context_state_callback_data* data = (context_state_callback_data*) userdata;
|
context_state_callback_data* data = (context_state_callback_data*) userdata;
|
||||||
luaL_checktype(data->L, 1, LUA_TFUNCTION);
|
luaL_checktype(data->L, 1, LUA_TFUNCTION);
|
||||||
luaL_checkudata(data->L, 2, LUA_PA_CONTEXT);
|
luaL_checkudata(data->L, 2, LUA_PA_CONTEXT);
|
||||||
|
@ -23,9 +26,7 @@ context_state_callback(pa_context* c, void* userdata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int context_new(lua_State* L, pa_mainloop_api* pa_api) {
|
||||||
context_new(lua_State* L, pa_mainloop_api* pa_api)
|
|
||||||
{
|
|
||||||
const char* name = luaL_checkstring(L, -1);
|
const char* name = luaL_checkstring(L, -1);
|
||||||
// TODO: libpulse recommends using `new_with_proplist` instead. But I need to figure out that `proplist` first.
|
// TODO: libpulse recommends using `new_with_proplist` instead. But I need to figure out that `proplist` first.
|
||||||
pa_context* ctx = pa_context_new(pa_api, name);
|
pa_context* ctx = pa_context_new(pa_api, name);
|
||||||
|
@ -33,7 +34,7 @@ context_new(lua_State* L, pa_mainloop_api* pa_api)
|
||||||
return luaL_error(L, "failed to create pulseaudio context");
|
return luaL_error(L, "failed to create pulseaudio context");
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_pa_context* lgi_ctx = lua_newuserdata (L, sizeof(lua_pa_context));
|
lua_pa_context* lgi_ctx = lua_newuserdata(L, sizeof(lua_pa_context));
|
||||||
if (lgi_ctx == NULL) {
|
if (lgi_ctx == NULL) {
|
||||||
return luaL_error(L, "failed to create context userdata");
|
return luaL_error(L, "failed to create context userdata");
|
||||||
}
|
}
|
||||||
|
@ -48,19 +49,7 @@ context_new(lua_State* L, pa_mainloop_api* pa_api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int context__gc(lua_State* L) {
|
||||||
context__index(lua_State* L)
|
|
||||||
{
|
|
||||||
const char* index = luaL_checkstring(L, 2);
|
|
||||||
luaL_getmetatable(L, LUA_PA_CONTEXT);
|
|
||||||
lua_getfield(L, -1, index);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
context__gc(lua_State* L)
|
|
||||||
{
|
|
||||||
lua_pa_context* ctx = luaL_checkudata(L, 1, LUA_PA_CONTEXT);
|
lua_pa_context* ctx = luaL_checkudata(L, 1, LUA_PA_CONTEXT);
|
||||||
|
|
||||||
if (ctx->connected == TRUE) {
|
if (ctx->connected == TRUE) {
|
||||||
|
@ -83,16 +72,14 @@ context__gc(lua_State* L)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int context_connect(lua_State* L) {
|
||||||
context_connect(lua_State* L)
|
|
||||||
{
|
|
||||||
int nargs = lua_gettop(L);
|
int nargs = lua_gettop(L);
|
||||||
const char* server = NULL;
|
const char* server = NULL;
|
||||||
|
|
||||||
if (lua_type(L, 2) == LUA_TSTRING)
|
if (lua_type(L, 2) == LUA_TSTRING)
|
||||||
server = lua_tostring(L, 2);
|
server = lua_tostring(L, 2);
|
||||||
else if (lua_type(L, 2) != LUA_TNIL) {
|
else if (lua_type(L, 2) != LUA_TNIL) {
|
||||||
const char *typearg;
|
const char* typearg;
|
||||||
if (luaL_getmetafield(L, 2, "__name") == LUA_TSTRING)
|
if (luaL_getmetafield(L, 2, "__name") == LUA_TSTRING)
|
||||||
typearg = lua_tostring(L, -1);
|
typearg = lua_tostring(L, -1);
|
||||||
else if (lua_type(L, 2) == LUA_TLIGHTUSERDATA)
|
else if (lua_type(L, 2) == LUA_TLIGHTUSERDATA)
|
||||||
|
@ -141,11 +128,54 @@ context_connect(lua_State* L)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int context_disconnect(lua_State* L) {
|
||||||
context_get_state(lua_State* L)
|
lua_pa_context* ctx = luaL_checkudata(L, 1, LUA_PA_CONTEXT);
|
||||||
{
|
pa_context_disconnect(ctx->context);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int context_get_state(lua_State* L) {
|
||||||
lua_pa_context* ctx = luaL_checkudata(L, 1, LUA_PA_CONTEXT);
|
lua_pa_context* ctx = luaL_checkudata(L, 1, LUA_PA_CONTEXT);
|
||||||
pa_context_state_t state = pa_context_get_state(ctx->context);
|
pa_context_state_t state = pa_context_get_state(ctx->context);
|
||||||
lua_pushinteger(L, state);
|
lua_pushinteger(L, state);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int context_set_default_sink(lua_State* L) {
|
||||||
|
lua_pa_context* ctx = luaL_checkudata(L, 1, LUA_PA_CONTEXT);
|
||||||
|
const char* name = luaL_checkstring(L, 2);
|
||||||
|
|
||||||
|
simple_callback_data* data = prepare_lua_callback(L);
|
||||||
|
|
||||||
|
pa_operation* op = pa_context_set_default_sink(ctx->context, name, success_callback, data);
|
||||||
|
if (op == NULL) {
|
||||||
|
int error = pa_context_errno(ctx->context);
|
||||||
|
lua_pushvalue(L, 2);
|
||||||
|
lua_pushfstring(L, "failed to set default sink: %s", pa_strerror(error));
|
||||||
|
lua_call(L, 1, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int context_set_default_source(lua_State* L) {
|
||||||
|
lua_pa_context* ctx = luaL_checkudata(L, 1, LUA_PA_CONTEXT);
|
||||||
|
const char* name = luaL_checkstring(L, 2);
|
||||||
|
|
||||||
|
simple_callback_data* data = prepare_lua_callback(L);
|
||||||
|
|
||||||
|
pa_operation* op = pa_context_set_default_source(ctx->context, name, success_callback, data);
|
||||||
|
if (op == NULL) {
|
||||||
|
int error = pa_context_errno(ctx->context);
|
||||||
|
lua_pushvalue(L, 2);
|
||||||
|
lua_pushfstring(L, "failed to set default source: %s", pa_strerror(error));
|
||||||
|
lua_call(L, 1, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,20 @@
|
||||||
|
/** Bindings for libpulse's connection context.
|
||||||
|
*
|
||||||
|
* The connection @{Context} provides introspection calls to query state from the server and various commands to
|
||||||
|
* change this state.
|
||||||
|
*
|
||||||
|
* In many cases, sinks and sources may be addressed by either their name or their numeric index.
|
||||||
|
* Both can be queried using the `get_(sink|source)_info` or `get_(sink|source)s` calls.
|
||||||
|
*
|
||||||
|
* @module pulseaudio.context
|
||||||
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "stdbool.h"
|
#include <lauxlib.h>
|
||||||
#include "lua.h"
|
#include <lua.h>
|
||||||
#include "lauxlib.h"
|
#include <pulse/context.h>
|
||||||
#include "pulse/context.h"
|
#include <pulse/mainloop-api.h>
|
||||||
#include "pulse/mainloop-api.h"
|
#include <stdbool.h>
|
||||||
#include "introspection.h"
|
|
||||||
|
|
||||||
#define LUA_PA_CONTEXT "pulseaudio.context"
|
#define LUA_PA_CONTEXT "pulseaudio.context"
|
||||||
|
|
||||||
|
@ -23,22 +32,428 @@ typedef struct lua_pa_context {
|
||||||
} lua_pa_context;
|
} lua_pa_context;
|
||||||
|
|
||||||
|
|
||||||
int
|
int context_new(lua_State*, pa_mainloop_api*);
|
||||||
context_new(lua_State*, pa_mainloop_api*);
|
int context__gc(lua_State*);
|
||||||
int
|
|
||||||
context__index(lua_State*);
|
|
||||||
int
|
|
||||||
context__gc(lua_State*);
|
|
||||||
int
|
|
||||||
context_connect(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
static const struct luaL_Reg context_mt [] = {
|
/// Context
|
||||||
{"__index", context__index},
|
/// @type Context
|
||||||
|
|
||||||
|
|
||||||
|
/** Connects the context to the given server address.
|
||||||
|
*
|
||||||
|
* If the server address is `nil`, libpulse will attempt to connect to what it considers the default server.
|
||||||
|
* In most cases, this is the local machine.
|
||||||
|
*
|
||||||
|
* The provided callback function will be registered as state callback function, and will be called whenever the
|
||||||
|
* context's connection state changes.
|
||||||
|
*
|
||||||
|
* @function Context:connect
|
||||||
|
* @async
|
||||||
|
* @tparam[opt=nil] string server_address The server address.
|
||||||
|
* @tparam function cb The connection state callback.
|
||||||
|
* @treturn[opt] string The error message
|
||||||
|
* @treturn string The state
|
||||||
|
*/
|
||||||
|
int context_connect(lua_State*);
|
||||||
|
|
||||||
|
/** Disconnects from the server.
|
||||||
|
*
|
||||||
|
* @function Context:disconnect
|
||||||
|
*/
|
||||||
|
int context_disconnect(lua_State* L);
|
||||||
|
|
||||||
|
|
||||||
|
/** Gets information about the server the context is connected to.
|
||||||
|
*
|
||||||
|
* See [pa_server_info](https://freedesktop.org/software/pulseaudio/doxygen/structpa__server__info.html)
|
||||||
|
* for documentation on the return type.
|
||||||
|
*
|
||||||
|
* This will fail when the connection state is anything other than `READY`.
|
||||||
|
*
|
||||||
|
* @function Context:get_server_info
|
||||||
|
* @async
|
||||||
|
* @tparam function cb
|
||||||
|
* @return[opt] string The error
|
||||||
|
* @return table The server info.
|
||||||
|
*/
|
||||||
|
int context_get_server_info(lua_State*);
|
||||||
|
|
||||||
|
|
||||||
|
// Sinks
|
||||||
|
|
||||||
|
|
||||||
|
/** Gets information about the given sink.
|
||||||
|
*
|
||||||
|
* The sink may be indicated by either its name or its index.
|
||||||
|
*
|
||||||
|
* See [pa_sink_info](https://freedesktop.org/software/pulseaudio/doxygen/structpa__sink__info.html)
|
||||||
|
* for documentation on the return type.
|
||||||
|
*
|
||||||
|
* @function Context:get_sink_info
|
||||||
|
* @async
|
||||||
|
* @tparam number|string sink The index or name of the sink to query.
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn table
|
||||||
|
*/
|
||||||
|
int context_get_sink_info(lua_State*);
|
||||||
|
|
||||||
|
/** Gets information about all sinks.
|
||||||
|
*
|
||||||
|
* This returns the same information as @{Context:get_sink_info} would have returned for every sink
|
||||||
|
* that's currently registered at the server.
|
||||||
|
*
|
||||||
|
* See [pa_sink_info](https://freedesktop.org/software/pulseaudio/doxygen/structpa__sink__info.html)
|
||||||
|
* for documentation on the return type.
|
||||||
|
*
|
||||||
|
* @function Context:get_sinks
|
||||||
|
* @async
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn table
|
||||||
|
*/
|
||||||
|
int context_get_sink_info_list(lua_State*);
|
||||||
|
int context_get_sink_info_by_name(lua_State*);
|
||||||
|
int context_get_sink_info_by_index(lua_State*);
|
||||||
|
|
||||||
|
/** Sets the sink's volume to the given value.
|
||||||
|
*
|
||||||
|
* The sink may be indicated by either its name or its index.
|
||||||
|
*
|
||||||
|
* @function Context:set_sink_volume
|
||||||
|
* @async
|
||||||
|
* @tparam number|string sink The sink to update.
|
||||||
|
* @tparam Volume volume
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn boolean
|
||||||
|
*/
|
||||||
|
int context_set_sink_volume(lua_State*);
|
||||||
|
int context_set_sink_volume_by_name(lua_State*);
|
||||||
|
int context_set_sink_volume_by_index(lua_State*);
|
||||||
|
|
||||||
|
/** Sets the sink's mute state.
|
||||||
|
*
|
||||||
|
* The sink may be indicated by either its name or its index.
|
||||||
|
*
|
||||||
|
* @function Context:set_sink_mute
|
||||||
|
* @async
|
||||||
|
* @tparam number|string sink The sink to update.
|
||||||
|
* @tparam boolean mute
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn boolean
|
||||||
|
*/
|
||||||
|
int context_set_sink_mute(lua_State*);
|
||||||
|
int context_set_sink_mute_by_name(lua_State*);
|
||||||
|
int context_set_sink_mute_by_index(lua_State*);
|
||||||
|
|
||||||
|
/** Sets the sink's suspended state.
|
||||||
|
*
|
||||||
|
* The sink may be indicated by either its name or its index.
|
||||||
|
*
|
||||||
|
* @function Context:set_sink_suspended
|
||||||
|
* @async
|
||||||
|
* @tparam number|string sink The sink to update.
|
||||||
|
* @tparam boolean suspended
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn boolean
|
||||||
|
*/
|
||||||
|
int context_set_sink_suspended(lua_State*);
|
||||||
|
int context_set_sink_suspended_by_name(lua_State*);
|
||||||
|
int context_set_sink_suspended_by_index(lua_State*);
|
||||||
|
|
||||||
|
|
||||||
|
// Sink Inputs
|
||||||
|
|
||||||
|
/** Gets information about the given sink input.
|
||||||
|
*
|
||||||
|
* The sink input may be indicated by either its name or its index.
|
||||||
|
*
|
||||||
|
* See [pa_sink_input_info](https://freedesktop.org/software/pulseaudio/doxygen/structpa__sink_input__info.html)
|
||||||
|
* for documentation on the return type.
|
||||||
|
*
|
||||||
|
* @function Context:get_sink_input_info
|
||||||
|
* @async
|
||||||
|
* @tparam number|string sink input The index or name of the sink input to query.
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn table
|
||||||
|
*/
|
||||||
|
int context_get_sink_input_info(lua_State*);
|
||||||
|
|
||||||
|
/** Gets information about all sink inputs.
|
||||||
|
*
|
||||||
|
* This returns the same information as @{Context:get_sink_input_info} would have returned for every sink input
|
||||||
|
* that's currently registered at the server.
|
||||||
|
*
|
||||||
|
* See [pa_sink_input_info](https://freedesktop.org/software/pulseaudio/doxygen/structpa__sink_input__info.html)
|
||||||
|
* for documentation on the return type.
|
||||||
|
*
|
||||||
|
* @function Context:get_sink_inputs
|
||||||
|
* @async
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn table
|
||||||
|
*/
|
||||||
|
int context_get_sink_input_info_list(lua_State*);
|
||||||
|
|
||||||
|
|
||||||
|
/** Moves the sink input to a different name.
|
||||||
|
*
|
||||||
|
* The target sink may be indicated by either its name or its index.
|
||||||
|
*
|
||||||
|
* @function Context:set_sink_suspended
|
||||||
|
* @async
|
||||||
|
* @tparam number The sink input to move.
|
||||||
|
* @tparam number|string sink The sink to update.
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn boolean
|
||||||
|
*/
|
||||||
|
int context_move_sink_input(lua_State*);
|
||||||
|
int context_move_sink_input_by_name(lua_State*);
|
||||||
|
int context_move_sink_input_by_index(lua_State*);
|
||||||
|
|
||||||
|
/** Sets the sink input's volume to the given value.
|
||||||
|
*
|
||||||
|
* @function Context:set_sink_input_volume
|
||||||
|
* @async
|
||||||
|
* @tparam number sink_input The sink input to update.
|
||||||
|
* @tparam Volume volume
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn boolean
|
||||||
|
*/
|
||||||
|
int context_set_sink_input_volume(lua_State*);
|
||||||
|
|
||||||
|
/** Sets the sink input's mute state.
|
||||||
|
*
|
||||||
|
* @function Context:set_sink_input_mute
|
||||||
|
* @async
|
||||||
|
* @tparam number sink_input The sink input to update.
|
||||||
|
* @tparam boolean mute
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn boolean
|
||||||
|
*/
|
||||||
|
int context_set_sink_input_mute(lua_State*);
|
||||||
|
|
||||||
|
/** Kills the sink input.
|
||||||
|
*
|
||||||
|
* @function Context:kill_sink_input
|
||||||
|
* @async
|
||||||
|
* @tparam number sink_input The sink input to kill.
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn boolean
|
||||||
|
*/
|
||||||
|
int context_kill_sink_input(lua_State*);
|
||||||
|
|
||||||
|
|
||||||
|
// Sources
|
||||||
|
|
||||||
|
/** Gets information about the given source.
|
||||||
|
*
|
||||||
|
* The source may be indicated by either its name or its index.
|
||||||
|
*
|
||||||
|
* See [pa_source_info](https://freedesktop.org/software/pulseaudio/doxygen/structpa__source__info.html)
|
||||||
|
* for documentation on the return type.
|
||||||
|
*
|
||||||
|
* @function Context:get_source_info
|
||||||
|
* @async
|
||||||
|
* @tparam number|string source The index or name of the source to query.
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn table
|
||||||
|
*/
|
||||||
|
int context_get_source_info(lua_State*);
|
||||||
|
|
||||||
|
/** Gets information about all sources.
|
||||||
|
*
|
||||||
|
* This returns the same information as @{Context:get_source_info} would have returned for every source
|
||||||
|
* that's currently registered at the server.
|
||||||
|
*
|
||||||
|
* See [pa_source_info](https://freedesktop.org/software/pulseaudio/doxygen/structpa__source__info.html)
|
||||||
|
* for documentation on the return type.
|
||||||
|
*
|
||||||
|
* @function Context:get_sources
|
||||||
|
* @async
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn table
|
||||||
|
*/
|
||||||
|
int context_get_source_info_list(lua_State*);
|
||||||
|
int context_get_source_info_by_name(lua_State*);
|
||||||
|
int context_get_source_info_by_index(lua_State*);
|
||||||
|
|
||||||
|
/** Sets the source's volume to the given value.
|
||||||
|
*
|
||||||
|
* The source may be indicated by either its name or its index.
|
||||||
|
*
|
||||||
|
* @function Context:set_source_volume
|
||||||
|
* @async
|
||||||
|
* @tparam number|string source The source to update.
|
||||||
|
* @tparam Volume volume
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn boolean
|
||||||
|
*/
|
||||||
|
int context_set_source_volume(lua_State*);
|
||||||
|
int context_set_source_volume_by_name(lua_State*);
|
||||||
|
int context_set_source_volume_by_index(lua_State*);
|
||||||
|
|
||||||
|
/** Sets the source's mute state.
|
||||||
|
*
|
||||||
|
* The source may be indicated by either its name or its index.
|
||||||
|
*
|
||||||
|
* @function Context:set_source_mute
|
||||||
|
* @async
|
||||||
|
* @tparam number|string source The source to update.
|
||||||
|
* @tparam boolean mute
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn boolean
|
||||||
|
*/
|
||||||
|
int context_set_source_mute(lua_State*);
|
||||||
|
int context_set_source_mute_by_name(lua_State*);
|
||||||
|
int context_set_source_mute_by_index(lua_State*);
|
||||||
|
|
||||||
|
/** Sets the source's suspended state.
|
||||||
|
*
|
||||||
|
* The source may be indicated by either its name or its index.
|
||||||
|
*
|
||||||
|
* @function Context:set_source_suspended
|
||||||
|
* @async
|
||||||
|
* @tparam number|string source The source to update.
|
||||||
|
* @tparam boolean suspended
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn boolean
|
||||||
|
*/
|
||||||
|
int context_set_source_suspended(lua_State*);
|
||||||
|
int context_set_source_suspended_by_name(lua_State*);
|
||||||
|
int context_set_source_suspended_by_index(lua_State*);
|
||||||
|
|
||||||
|
|
||||||
|
// Source Outputs
|
||||||
|
|
||||||
|
/** Gets information about the given source output.
|
||||||
|
*
|
||||||
|
* See [pa_source_output_info](https://freedesktop.org/software/pulseaudio/doxygen/structpa__source_output__info.html)
|
||||||
|
* for documentation on the return type.
|
||||||
|
*
|
||||||
|
* @function Context:get_source_output_info
|
||||||
|
* @async
|
||||||
|
* @tparam number source_output The index of the source output to query.
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn table
|
||||||
|
*/
|
||||||
|
int context_get_source_output_info(lua_State*);
|
||||||
|
|
||||||
|
/** Gets information about all source outputs.
|
||||||
|
*
|
||||||
|
* This returns the same information as @{Context:get_source_output_info} would have returned for every source output
|
||||||
|
* that's currently registered at the server.
|
||||||
|
*
|
||||||
|
* See [pa_source_output_info](https://freedesktop.org/software/pulseaudio/doxygen/structpa__source_output__info.html)
|
||||||
|
* for documentation on the return type.
|
||||||
|
*
|
||||||
|
* @function Context:get_source_outputs
|
||||||
|
* @async
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn table
|
||||||
|
*/
|
||||||
|
int context_get_source_output_info_list(lua_State*);
|
||||||
|
|
||||||
|
/** Moves the source output to a different name.
|
||||||
|
*
|
||||||
|
* The target source may be indicated by either its name or its index.
|
||||||
|
*
|
||||||
|
* @function Context:set_source_suspended
|
||||||
|
* @async
|
||||||
|
* @tparam number The source output to move.
|
||||||
|
* @tparam number|string source The source to update.
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn boolean
|
||||||
|
*/
|
||||||
|
int context_move_source_output(lua_State*);
|
||||||
|
int context_move_source_output_by_name(lua_State*);
|
||||||
|
int context_move_source_output_by_index(lua_State*);
|
||||||
|
|
||||||
|
|
||||||
|
/** Sets the source output's volume to the given value.
|
||||||
|
*
|
||||||
|
* @function Context:set_source_output_volume
|
||||||
|
* @async
|
||||||
|
* @tparam number source_output The source output to update.
|
||||||
|
* @tparam Volume volume
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn boolean
|
||||||
|
*/
|
||||||
|
int context_set_source_output_volume(lua_State*);
|
||||||
|
|
||||||
|
/** Sets the source output's mute state.
|
||||||
|
*
|
||||||
|
* @function Context:set_source_output_mute
|
||||||
|
* @async
|
||||||
|
* @tparam number source_output The source output to update.
|
||||||
|
* @tparam boolean mute
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn boolean
|
||||||
|
*/
|
||||||
|
int context_set_source_output_mute(lua_State*);
|
||||||
|
|
||||||
|
/** Kills the source output.
|
||||||
|
*
|
||||||
|
* @function Context:kill_source_output
|
||||||
|
* @async
|
||||||
|
* @tparam number source_output The source output to kill.
|
||||||
|
* @tparam function cb
|
||||||
|
* @treturn[opt] string
|
||||||
|
* @treturn boolean
|
||||||
|
*/
|
||||||
|
int context_kill_source_output(lua_State*);
|
||||||
|
|
||||||
|
|
||||||
|
static const struct luaL_Reg context_mt[] = {
|
||||||
{"__gc", context__gc},
|
{"__gc", context__gc},
|
||||||
{"connect", context_connect},
|
{ NULL, NULL }
|
||||||
{"get_server_info", context_get_server_info},
|
};
|
||||||
{"get_sinks", context_get_sink_info_list},
|
|
||||||
{"get_sources", context_get_source_info_list},
|
|
||||||
{NULL, NULL}
|
static const struct luaL_Reg context_f[] = {
|
||||||
|
{"connect", context_connect },
|
||||||
|
{ "disconnect", context_disconnect },
|
||||||
|
{ "get_server_info", context_get_server_info },
|
||||||
|
{ "get_sinks", context_get_sink_info_list },
|
||||||
|
{ "get_sink_info", context_get_sink_info },
|
||||||
|
{ "set_sink_volume", context_set_sink_volume },
|
||||||
|
{ "set_sink_mute", context_set_sink_mute },
|
||||||
|
{ "set_sink_suspended", context_set_sink_suspended },
|
||||||
|
{ "get_sink_inputs", context_get_sink_input_info_list },
|
||||||
|
{ "get_sink_input_info", context_get_sink_input_info },
|
||||||
|
{ "set_sink_input_volume", context_set_sink_input_volume },
|
||||||
|
{ "set_sink_input_mute", context_set_sink_input_mute },
|
||||||
|
{ "move_sink_input", context_move_sink_input },
|
||||||
|
{ "kill_sink_input", context_kill_sink_input },
|
||||||
|
{ "set_source_volume", context_set_source_volume },
|
||||||
|
{ "set_source_mute", context_set_source_mute },
|
||||||
|
{ "set_source_suspended", context_set_source_suspended },
|
||||||
|
{ "get_sources", context_get_source_info_list },
|
||||||
|
{ "get_source_info", context_get_source_info },
|
||||||
|
{ "get_source_outputs", context_get_source_output_info_list},
|
||||||
|
{ "get_source_output_info", context_get_source_output_info },
|
||||||
|
{ "set_source_output_volume", context_set_source_output_volume },
|
||||||
|
{ "set_source_output_mute", context_set_source_output_mute },
|
||||||
|
{ "move_source_output", context_move_source_output },
|
||||||
|
{ "kill_source_output", context_kill_source_output },
|
||||||
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
532
src/lua_libpulse_glib/convert.c
Normal file
532
src/lua_libpulse_glib/convert.c
Normal file
|
@ -0,0 +1,532 @@
|
||||||
|
#include "convert.h"
|
||||||
|
|
||||||
|
#include "proplist.h"
|
||||||
|
#include "volume.h"
|
||||||
|
|
||||||
|
#include <pulse/xmalloc.h>
|
||||||
|
|
||||||
|
|
||||||
|
void channel_map_to_lua(lua_State* L, const pa_channel_map* spec) {
|
||||||
|
lua_createtable(L, spec->channels, 0);
|
||||||
|
int table_index = lua_gettop(L);
|
||||||
|
|
||||||
|
for (int i = 0; i < spec->channels; ++i) {
|
||||||
|
lua_pushinteger(L, i + 1);
|
||||||
|
lua_pushinteger(L, spec->map[i]);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void sample_spec_to_lua(lua_State* L, const pa_sample_spec* spec) {
|
||||||
|
lua_createtable(L, 0, 3);
|
||||||
|
int table_index = lua_gettop(L);
|
||||||
|
|
||||||
|
lua_pushstring(L, "rate");
|
||||||
|
lua_pushinteger(L, spec->rate);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "channels");
|
||||||
|
lua_pushinteger(L, spec->channels);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "format");
|
||||||
|
lua_pushinteger(L, spec->format);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void sink_port_info_to_lua(lua_State* L, const pa_sink_port_info* info) {
|
||||||
|
lua_createtable(L, 0, 6);
|
||||||
|
int table_index = lua_gettop(L);
|
||||||
|
|
||||||
|
lua_pushstring(L, "name");
|
||||||
|
lua_pushstring(L, info->name);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "description");
|
||||||
|
lua_pushstring(L, info->description);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "priority");
|
||||||
|
lua_pushinteger(L, info->priority);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "available");
|
||||||
|
lua_pushinteger(L, info->available);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "availability_group");
|
||||||
|
lua_pushstring(L, info->availability_group);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "type");
|
||||||
|
lua_pushinteger(L, info->type);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void source_port_info_to_lua(lua_State* L, const pa_source_port_info* info) {
|
||||||
|
lua_createtable(L, 0, 6);
|
||||||
|
int table_index = lua_gettop(L);
|
||||||
|
|
||||||
|
lua_pushstring(L, "name");
|
||||||
|
lua_pushstring(L, info->name);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "description");
|
||||||
|
lua_pushstring(L, info->description);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "priority");
|
||||||
|
lua_pushinteger(L, info->priority);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "available");
|
||||||
|
lua_pushinteger(L, info->available);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "availability_group");
|
||||||
|
lua_pushstring(L, info->availability_group);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "type");
|
||||||
|
lua_pushinteger(L, info->type);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void format_info_to_lua(lua_State* L, const pa_format_info* info) {
|
||||||
|
lua_createtable(L, 0, 2);
|
||||||
|
int table_index = lua_gettop(L);
|
||||||
|
|
||||||
|
lua_pushstring(L, "encoding");
|
||||||
|
lua_pushinteger(L, info->encoding);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "plist");
|
||||||
|
proplist_to_lua(L, pa_proplist_copy(info->plist));
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void sink_ports_to_lua(lua_State* L, pa_sink_port_info** list, int n_ports, pa_sink_port_info* active) {
|
||||||
|
lua_createtable(L, n_ports, 1);
|
||||||
|
int table_index = lua_gettop(L);
|
||||||
|
|
||||||
|
for (int i = 0; i < n_ports; ++i) {
|
||||||
|
lua_pushinteger(L, i + 1);
|
||||||
|
sink_port_info_to_lua(L, list[i]);
|
||||||
|
|
||||||
|
if (list[i] == active) {
|
||||||
|
lua_pushstring(L, "active");
|
||||||
|
lua_pushvalue(L, -2);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void source_ports_to_lua(lua_State* L, pa_source_port_info** list, int n_ports, pa_source_port_info* active) {
|
||||||
|
lua_createtable(L, n_ports, 1);
|
||||||
|
int table_index = lua_gettop(L);
|
||||||
|
|
||||||
|
for (int i = 0; i < n_ports; ++i) {
|
||||||
|
lua_pushinteger(L, i + 1);
|
||||||
|
source_port_info_to_lua(L, list[i]);
|
||||||
|
|
||||||
|
if (list[i] == active) {
|
||||||
|
lua_pushstring(L, "active");
|
||||||
|
lua_pushvalue(L, -2);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void formats_to_lua(lua_State* L, pa_format_info** list, int n_formats) {
|
||||||
|
lua_createtable(L, n_formats, 0);
|
||||||
|
int table_index = lua_gettop(L);
|
||||||
|
|
||||||
|
for (int i = 0; i < n_formats; ++i) {
|
||||||
|
lua_pushinteger(L, i + 1);
|
||||||
|
format_info_to_lua(L, list[i]);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void sink_info_to_lua(lua_State* L, const pa_sink_info* info) {
|
||||||
|
lua_createtable(L, 0, 24);
|
||||||
|
int table_index = lua_gettop(L);
|
||||||
|
|
||||||
|
lua_pushstring(L, "name");
|
||||||
|
lua_pushstring(L, info->name);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "index");
|
||||||
|
// Convert C's 0-based index to Lua's 1-base
|
||||||
|
lua_pushinteger(L, info->index + 1);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "description");
|
||||||
|
lua_pushstring(L, info->description);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "sample_spec");
|
||||||
|
sample_spec_to_lua(L, &info->sample_spec);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "channel_map");
|
||||||
|
channel_map_to_lua(L, &info->channel_map);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "owner_module");
|
||||||
|
lua_pushinteger(L, info->owner_module);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "volume");
|
||||||
|
volume_to_lua(L, &info->volume);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "mute");
|
||||||
|
lua_pushinteger(L, info->mute);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "monitor_source");
|
||||||
|
lua_pushinteger(L, info->monitor_source);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "monitor_source_name");
|
||||||
|
lua_pushstring(L, info->monitor_source_name);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "latency");
|
||||||
|
lua_pushinteger(L, info->latency);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "driver");
|
||||||
|
lua_pushstring(L, info->driver);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "flags");
|
||||||
|
lua_pushinteger(L, info->flags);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "proplist");
|
||||||
|
proplist_to_lua(L, pa_proplist_copy(info->proplist));
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "base_volume");
|
||||||
|
lua_pushinteger(L, info->base_volume);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "state");
|
||||||
|
lua_pushinteger(L, info->state);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "n_volume_steps");
|
||||||
|
lua_pushinteger(L, info->n_volume_steps);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "card");
|
||||||
|
lua_pushinteger(L, info->card);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "ports");
|
||||||
|
sink_ports_to_lua(L, info->ports, info->n_ports, info->active_port);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "formats");
|
||||||
|
formats_to_lua(L, info->formats, info->n_formats);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void source_info_to_lua(lua_State* L, const pa_source_info* info) {
|
||||||
|
lua_createtable(L, 0, 24);
|
||||||
|
int table_index = lua_gettop(L);
|
||||||
|
|
||||||
|
lua_pushstring(L, "name");
|
||||||
|
lua_pushstring(L, info->name);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "index");
|
||||||
|
// Convert C's 0-based index to Lua's 1-base
|
||||||
|
lua_pushinteger(L, info->index + 1);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "description");
|
||||||
|
lua_pushstring(L, info->description);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "sample_spec");
|
||||||
|
sample_spec_to_lua(L, &info->sample_spec);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "channel_map");
|
||||||
|
channel_map_to_lua(L, &info->channel_map);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "owner_module");
|
||||||
|
lua_pushinteger(L, info->owner_module);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "volume");
|
||||||
|
volume_to_lua(L, &info->volume);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "mute");
|
||||||
|
lua_pushinteger(L, info->mute);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "monitor_of_sink");
|
||||||
|
lua_pushinteger(L, info->monitor_of_sink);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "monitor_of_sink_name");
|
||||||
|
lua_pushstring(L, info->monitor_of_sink_name);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "latency");
|
||||||
|
lua_pushinteger(L, info->latency);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "driver");
|
||||||
|
lua_pushstring(L, info->driver);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "flags");
|
||||||
|
lua_pushinteger(L, info->flags);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "proplist");
|
||||||
|
proplist_to_lua(L, pa_proplist_copy(info->proplist));
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "configured_latency");
|
||||||
|
lua_pushinteger(L, info->configured_latency);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "base_volume");
|
||||||
|
lua_pushinteger(L, info->base_volume);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "state");
|
||||||
|
lua_pushinteger(L, info->state);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "n_volume_steps");
|
||||||
|
lua_pushinteger(L, info->n_volume_steps);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "card");
|
||||||
|
lua_pushinteger(L, info->card);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "ports");
|
||||||
|
source_ports_to_lua(L, info->ports, info->n_ports, info->active_port);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "formats");
|
||||||
|
formats_to_lua(L, info->formats, info->n_formats);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void server_info_to_lua(lua_State* L, const pa_server_info* info) {
|
||||||
|
lua_createtable(L, 0, 9);
|
||||||
|
int table_index = lua_gettop(L);
|
||||||
|
|
||||||
|
lua_pushstring(L, "user_name");
|
||||||
|
lua_pushstring(L, info->user_name);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "host_name");
|
||||||
|
lua_pushstring(L, info->host_name);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "server_version");
|
||||||
|
lua_pushstring(L, info->server_version);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "server_name");
|
||||||
|
lua_pushstring(L, info->server_name);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "default_sink_name");
|
||||||
|
lua_pushstring(L, info->default_sink_name);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "default_source_name");
|
||||||
|
lua_pushstring(L, info->default_source_name);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "cookie");
|
||||||
|
lua_pushinteger(L, info->cookie);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "sample_spec");
|
||||||
|
sample_spec_to_lua(L, &info->sample_spec);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "channel_map");
|
||||||
|
channel_map_to_lua(L, &info->channel_map);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void sink_input_info_to_lua(lua_State* L, const pa_sink_input_info* info) {
|
||||||
|
lua_createtable(L, 0, 15);
|
||||||
|
int table_index = lua_gettop(L);
|
||||||
|
|
||||||
|
lua_pushstring(L, "index");
|
||||||
|
lua_pushinteger(L, info->index + 1);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "name");
|
||||||
|
lua_pushstring(L, info->name);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "owner_module");
|
||||||
|
lua_pushinteger(L, info->owner_module);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "client");
|
||||||
|
lua_pushinteger(L, info->client);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "sink");
|
||||||
|
lua_pushinteger(L, info->sink);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "sample_spec");
|
||||||
|
sample_spec_to_lua(L, &info->sample_spec);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "channel_map");
|
||||||
|
channel_map_to_lua(L, &info->channel_map);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "has_volume");
|
||||||
|
lua_pushboolean(L, info->has_volume);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "volume_writable");
|
||||||
|
lua_pushboolean(L, info->volume_writable);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
if (info->has_volume) {
|
||||||
|
lua_pushstring(L, "volume");
|
||||||
|
volume_to_lua(L, &info->volume);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pushstring(L, "buffer_usec");
|
||||||
|
lua_pushinteger(L, info->buffer_usec);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "sink_usec");
|
||||||
|
lua_pushinteger(L, info->sink_usec);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "resample_method");
|
||||||
|
lua_pushstring(L, info->resample_method);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "driver");
|
||||||
|
lua_pushstring(L, info->driver);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "mute");
|
||||||
|
lua_pushboolean(L, info->mute);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "format");
|
||||||
|
format_info_to_lua(L, info->format);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "proplist");
|
||||||
|
proplist_to_lua(L, pa_proplist_copy(info->proplist));
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void source_output_info_to_lua(lua_State* L, const pa_source_output_info* info) {
|
||||||
|
lua_createtable(L, 0, 15);
|
||||||
|
int table_index = lua_gettop(L);
|
||||||
|
|
||||||
|
lua_pushstring(L, "index");
|
||||||
|
lua_pushinteger(L, info->index + 1);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "name");
|
||||||
|
lua_pushstring(L, info->name);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "owner_module");
|
||||||
|
lua_pushinteger(L, info->owner_module);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "client");
|
||||||
|
lua_pushinteger(L, info->client);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "source");
|
||||||
|
lua_pushinteger(L, info->source);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "sample_spec");
|
||||||
|
sample_spec_to_lua(L, &info->sample_spec);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "channel_map");
|
||||||
|
channel_map_to_lua(L, &info->channel_map);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "has_volume");
|
||||||
|
lua_pushboolean(L, info->has_volume);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "volume_writable");
|
||||||
|
lua_pushboolean(L, info->volume_writable);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
if (info->has_volume) {
|
||||||
|
lua_pushstring(L, "volume");
|
||||||
|
volume_to_lua(L, &info->volume);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pushstring(L, "buffer_usec");
|
||||||
|
lua_pushinteger(L, info->buffer_usec);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "source_usec");
|
||||||
|
lua_pushinteger(L, info->source_usec);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "resample_method");
|
||||||
|
lua_pushstring(L, info->resample_method);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "driver");
|
||||||
|
lua_pushstring(L, info->driver);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "mute");
|
||||||
|
lua_pushboolean(L, info->mute);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "format");
|
||||||
|
format_info_to_lua(L, info->format);
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
|
||||||
|
lua_pushstring(L, "proplist");
|
||||||
|
proplist_to_lua(L, pa_proplist_copy(info->proplist));
|
||||||
|
lua_settable(L, table_index);
|
||||||
|
}
|
20
src/lua_libpulse_glib/convert.h
Normal file
20
src/lua_libpulse_glib/convert.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
#include <pulse/error.h>
|
||||||
|
#include <pulse/introspect.h>
|
||||||
|
|
||||||
|
|
||||||
|
void channel_map_to_lua(lua_State*, const pa_channel_map*);
|
||||||
|
void sample_spec_to_lua(lua_State*, const pa_sample_spec*);
|
||||||
|
void sink_port_info_to_lua(lua_State*, const pa_sink_port_info*);
|
||||||
|
void source_port_info_to_lua(lua_State*, const pa_source_port_info*);
|
||||||
|
void format_info_to_lua(lua_State*, const pa_format_info*);
|
||||||
|
void sink_ports_to_lua(lua_State*, pa_sink_port_info**, int, pa_sink_port_info*);
|
||||||
|
void source_ports_to_lua(lua_State*, pa_source_port_info**, int, pa_source_port_info*);
|
||||||
|
void formats_to_lua(lua_State*, pa_format_info**, int);
|
||||||
|
void sink_info_to_lua(lua_State*, const pa_sink_info*);
|
||||||
|
void source_info_to_lua(lua_State*, const pa_source_info*);
|
||||||
|
void server_info_to_lua(lua_State*, const pa_server_info*);
|
||||||
|
void sink_input_info_to_lua(lua_State*, const pa_sink_input_info*);
|
||||||
|
void source_output_info_to_lua(lua_State*, const pa_source_output_info*);
|
File diff suppressed because it is too large
Load diff
|
@ -1,10 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "lua.h"
|
|
||||||
|
|
||||||
int
|
|
||||||
context_get_server_info(lua_State*);
|
|
||||||
int
|
|
||||||
context_get_sink_info_list(lua_State*);
|
|
||||||
int
|
|
||||||
context_get_source_info_list(lua_State*);
|
|
15
src/lua_libpulse_glib/lua_util.h
Normal file
15
src/lua_libpulse_glib/lua_util.h
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef lua_util_h_INCLUDED
|
||||||
|
#define lua_util_h_INCLUDED
|
||||||
|
|
||||||
|
#include <lauxlib.h>
|
||||||
|
#include <lua.h>
|
||||||
|
|
||||||
|
#define LUA_MOD_EXPORT extern
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct luaU_enumfield {
|
||||||
|
const char* name;
|
||||||
|
const char* value;
|
||||||
|
} luaU_enumfield;
|
||||||
|
|
||||||
|
#endif // lua_util_h_INCLUDED
|
214
src/lua_libpulse_glib/proplist.c
Normal file
214
src/lua_libpulse_glib/proplist.c
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
#include "proplist.h"
|
||||||
|
|
||||||
|
#include <pulse/xmalloc.h>
|
||||||
|
|
||||||
|
// Creates a Lua userdatum from/for a PulseAudio proplist.
|
||||||
|
//
|
||||||
|
// The provided proplist will be `pa_proplist_copy()`ed to pass memory management over
|
||||||
|
// to the garbage collector.
|
||||||
|
//
|
||||||
|
// @return[type=PropList]
|
||||||
|
int proplist_to_lua(lua_State* L, pa_proplist* pa_plist) {
|
||||||
|
proplist* plist = lua_newuserdata(L, sizeof(proplist));
|
||||||
|
if (pa_plist == NULL) {
|
||||||
|
lua_pushfstring(L, "Failed to allocate proplist userdata");
|
||||||
|
return lua_error(L);
|
||||||
|
}
|
||||||
|
luaL_getmetatable(L, LUA_PA_PROPLIST);
|
||||||
|
lua_setmetatable(L, -2);
|
||||||
|
|
||||||
|
plist->plist = pa_proplist_copy(pa_plist);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int proplist_new(lua_State* L) {
|
||||||
|
pa_proplist* pa_plist = pa_proplist_new();
|
||||||
|
if (pa_plist == NULL) {
|
||||||
|
lua_pushfstring(L, "Failed to allocate PulseAudio proplist");
|
||||||
|
return lua_error(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
return proplist_to_lua(L, pa_plist);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int proplist_from_string(lua_State* L) {
|
||||||
|
const char* str = luaL_checkstring(L, 1);
|
||||||
|
pa_proplist* pa_plist = pa_proplist_from_string(str);
|
||||||
|
|
||||||
|
return proplist_to_lua(L, pa_plist);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int proplist_key_valid(lua_State* L) {
|
||||||
|
const char* key = luaL_checkstring(L, 1);
|
||||||
|
int valid = pa_proplist_key_valid(key);
|
||||||
|
lua_pushboolean(L, valid);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int proplist_isempty(lua_State* L) {
|
||||||
|
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
||||||
|
int empty = pa_proplist_isempty(plist->plist);
|
||||||
|
lua_pushboolean(L, empty);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int proplist_tostring_sep(lua_State* L) {
|
||||||
|
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
||||||
|
const char* sep = luaL_checkstring(L, 2);
|
||||||
|
const char* str = pa_proplist_to_string_sep(plist->plist, sep);
|
||||||
|
lua_pushstring(L, str);
|
||||||
|
pa_xfree((void*) str);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int proplist_clear(lua_State* L) {
|
||||||
|
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
||||||
|
pa_proplist_clear(plist->plist);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int proplist_contains(lua_State* L) {
|
||||||
|
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
||||||
|
const char* key = luaL_checkstring(L, 2);
|
||||||
|
if (!pa_proplist_key_valid(key)) {
|
||||||
|
return luaL_error(L, "invalid key for proplist");
|
||||||
|
}
|
||||||
|
|
||||||
|
int contains = pa_proplist_contains(plist->plist, key);
|
||||||
|
if (contains < 0) {
|
||||||
|
return luaL_error(L, "failed to check if proplist contains the key");
|
||||||
|
} else {
|
||||||
|
lua_pushboolean(L, contains);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int proplist_copy(lua_State* L) {
|
||||||
|
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
||||||
|
pa_proplist* other = pa_proplist_copy(plist->plist);
|
||||||
|
|
||||||
|
return proplist_to_lua(L, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Metatable functions
|
||||||
|
|
||||||
|
|
||||||
|
// Accesses the value at the given string key.
|
||||||
|
//
|
||||||
|
// This functions very similar to a regular hash table.
|
||||||
|
//
|
||||||
|
// Numeric indices/keys are not supported.
|
||||||
|
//
|
||||||
|
// Will return `nil` if there is no value or if it is not valid UTF-8.
|
||||||
|
// Arbitrary data from `pa_proplist_get()` is currently not supported.
|
||||||
|
//
|
||||||
|
// @param[type=string] key The index to access.
|
||||||
|
// @return[type=string|nil] The data at the given index.
|
||||||
|
int proplist__index(lua_State* L) {
|
||||||
|
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
||||||
|
const char* key = luaL_checkstring(L, 2);
|
||||||
|
if (!pa_proplist_key_valid(key)) {
|
||||||
|
return luaL_error(L, "invalid key for proplist");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pa_proplist_contains(plist->plist, key)) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* value = pa_proplist_gets(plist->plist, key);
|
||||||
|
if (value == NULL) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
} else {
|
||||||
|
lua_pushstring(L, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Sets or overwrites the value at the given key.
|
||||||
|
//
|
||||||
|
// Passing `nil` as value will unset the key.
|
||||||
|
//
|
||||||
|
// For both keys and values only strings are supported.
|
||||||
|
// Numeric indices or binary data values (as in `pa_proplist_set()`) are not available.
|
||||||
|
//
|
||||||
|
// @param[type=string] key The index to write to.
|
||||||
|
// @param[type=string|nil] value The value to write.
|
||||||
|
int proplist__newindex(lua_State* L) {
|
||||||
|
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
||||||
|
const char* key = luaL_checkstring(L, 2);
|
||||||
|
|
||||||
|
if (lua_isnil(L, 3)) {
|
||||||
|
if (pa_proplist_unset(plist->plist, key) < 0) {
|
||||||
|
// TODO: Get last error. Need to get access to the current context for
|
||||||
|
// this.
|
||||||
|
return luaL_error(L, "failed to unset key %s", key);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const char* value = luaL_checkstring(L, 3);
|
||||||
|
if (pa_proplist_sets(plist->plist, key, value) < 0) {
|
||||||
|
return luaL_error(L, "failed to set value for key %s", key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Frees the internal proplist.
|
||||||
|
int proplist__gc(lua_State* L) {
|
||||||
|
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
||||||
|
pa_proplist_free(plist->plist);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Gets the size of the proplist.
|
||||||
|
int proplist__len(lua_State* L) {
|
||||||
|
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
||||||
|
unsigned len = pa_proplist_size(plist->plist);
|
||||||
|
lua_pushinteger(L, len);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Compares two proplists for equality.
|
||||||
|
//
|
||||||
|
// Proplists are equal if they contain the same keys with the same values.
|
||||||
|
//
|
||||||
|
// @param[type=PropList] other The proplist to compare against.
|
||||||
|
// @return[type=boolean]
|
||||||
|
int proplist__eq(lua_State* L) {
|
||||||
|
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
||||||
|
proplist* other = luaL_checkudata(L, 2, LUA_PA_PROPLIST);
|
||||||
|
int equal = pa_proplist_equal(plist->plist, other->plist);
|
||||||
|
lua_pushboolean(L, equal);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Creates a string representation.
|
||||||
|
//
|
||||||
|
// This adds a newline character as separator and as final character.
|
||||||
|
//
|
||||||
|
// @return[type=string]
|
||||||
|
int proplist__tostring(lua_State* L) {
|
||||||
|
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
||||||
|
char* str = pa_proplist_to_string(plist->plist);
|
||||||
|
lua_pushstring(L, str);
|
||||||
|
pa_xfree((void*) str);
|
||||||
|
return 1;
|
||||||
|
}
|
316
src/lua_libpulse_glib/proplist.h
Normal file
316
src/lua_libpulse_glib/proplist.h
Normal file
|
@ -0,0 +1,316 @@
|
||||||
|
/** Bindings for PulseAudio's property lists.
|
||||||
|
*
|
||||||
|
* Basic operations are mapped to Lua table operations:
|
||||||
|
*
|
||||||
|
* - get a value: `plist[key]`
|
||||||
|
* - set a value: `plist[key] = value`
|
||||||
|
* - get the size: `#plist`
|
||||||
|
* - get default string presentation: `tostring(plist)`
|
||||||
|
* - equality: `plist == other`
|
||||||
|
*
|
||||||
|
* Additional operations are exposed as methods, as documented below.
|
||||||
|
*
|
||||||
|
* @module pulseaudio.proplist
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "lua_util.h"
|
||||||
|
|
||||||
|
#include <lauxlib.h>
|
||||||
|
#include <lua.h>
|
||||||
|
#include <pulse/proplist.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define LUA_PA_PROPLIST "pulseaudio.proplist"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct proplist {
|
||||||
|
pa_proplist* plist;
|
||||||
|
} proplist;
|
||||||
|
|
||||||
|
|
||||||
|
/** A list of well-known keys. These will likely be used by other consumers
|
||||||
|
* of PulseAudio, so should be preferred for interoperatibility.
|
||||||
|
*
|
||||||
|
* See [proplist.h](https://freedesktop.org/software/pulseaudio/doxygen/proplist_8h.html) for details on
|
||||||
|
* these values.
|
||||||
|
*
|
||||||
|
* local proplist = require("pulseaudio.proplist")
|
||||||
|
* proplist.is_key_valid(proplist.MEDIA_NAME) -- true
|
||||||
|
* print(proplist.MEDIA_NAME) -- media.name
|
||||||
|
*
|
||||||
|
* @table pulseaudio.proplist
|
||||||
|
* @field MEDIA_NAME media.name
|
||||||
|
* @field MEDIA_TITLE media.title
|
||||||
|
* @field MEDIA_ARTIST media.artist
|
||||||
|
* @field MEDIA_COPYRIGHT media.copyright
|
||||||
|
* @field MEDIA_SOFTWARE media.software
|
||||||
|
* @field MEDIA_LANGUAGE media.language
|
||||||
|
* @field MEDIA_FILENAME media.filename
|
||||||
|
* @field MEDIA_ICON media.icon
|
||||||
|
* @field MEDIA_ICON_NAME media.icon_name
|
||||||
|
* @field MEDIA_ROLE media.role
|
||||||
|
* @field FILTER_WANT filter.want
|
||||||
|
* @field FILTER_APPLY filter.apply
|
||||||
|
* @field FILTER_SUPPRESS filter.suppress
|
||||||
|
* @field EVENT_ID event.id
|
||||||
|
* @field EVENT_DESCRIPTION event.description
|
||||||
|
* @field EVENT_MOUSE_X event.mouse.x
|
||||||
|
* @field EVENT_MOUSE_Y event.mouse.y
|
||||||
|
* @field EVENT_MOUSE_HPOS event.mouse.hpos
|
||||||
|
* @field EVENT_MOUSE_VPOS event.mouse.vpos
|
||||||
|
* @field EVENT_MOUSE_BUTTON event.mouse.button
|
||||||
|
* @field WINDOW_NAME window.name
|
||||||
|
* @field WINDOW_ID window.id
|
||||||
|
* @field WINDOW_ICON window.icon
|
||||||
|
* @field WINDOW_ICON_NAME window.icon_name
|
||||||
|
* @field WINDOW_X window.x
|
||||||
|
* @field WINDOW_Y window.y
|
||||||
|
* @field WINDOW_WIDTH window.width
|
||||||
|
* @field WINDOW_HEIGHT window.height
|
||||||
|
* @field WINDOW_HPOS window.hpos
|
||||||
|
* @field WINDOW_VPOS window.vpos
|
||||||
|
* @field WINDOW_DESKTOP window.desktop
|
||||||
|
* @field WINDOW_X11_DISPLAY window.x11.display
|
||||||
|
* @field WINDOW_X11_SCREEN window.x11.screen
|
||||||
|
* @field WINDOW_X11_MONITOR window.x11.monitor
|
||||||
|
* @field WINDOW_X11_XID window.x11.xid
|
||||||
|
* @field APPLICATION_NAME application.name
|
||||||
|
* @field APPLICATION_ID application.id
|
||||||
|
* @field APPLICATION_VERSION application.version
|
||||||
|
* @field APPLICATION_ICON application.icon
|
||||||
|
* @field APPLICATION_ICON_NAME application.icon_name
|
||||||
|
* @field APPLICATION_LANGUAGE application.language
|
||||||
|
* @field APPLICATION_PROCESS_ID application.process.id
|
||||||
|
* @field APPLICATION_PROCESS_BINARY application.process.binary
|
||||||
|
* @field APPLICATION_PROCESS_USER application.process.user
|
||||||
|
* @field APPLICATION_PROCESS_HOST application.process.host
|
||||||
|
* @field APPLICATION_PROCESS_MACHINE_ID application.process.machine_id
|
||||||
|
* @field APPLICATION_PROCESS_SESSION_ID application.process.session_id
|
||||||
|
* @field DEVICE_STRING device.string
|
||||||
|
* @field DEVICE_API device.api
|
||||||
|
* @field DEVICE_DESCRIPTION device.description
|
||||||
|
* @field DEVICE_BUS_PATH device.bus_path
|
||||||
|
* @field DEVICE_SERIAL device.serial
|
||||||
|
* @field DEVICE_VENDOR_ID device.vendor.id
|
||||||
|
* @field DEVICE_VENDOR_NAME device.vendor.name
|
||||||
|
* @field DEVICE_PRODUCT_ID device.product.id
|
||||||
|
* @field DEVICE_PRODUCT_NAME device.product.name
|
||||||
|
* @field DEVICE_CLASS device.class
|
||||||
|
* @field DEVICE_FORM_FACTOR device.form_factor
|
||||||
|
* @field DEVICE_BUS device.bus
|
||||||
|
* @field DEVICE_ICON device.icon
|
||||||
|
* @field DEVICE_ICON_NAME device.icon_name
|
||||||
|
* @field DEVICE_ACCESS_MODE device.access_mode
|
||||||
|
* @field DEVICE_MASTER_DEVICE device.master_device
|
||||||
|
* @field DEVICE_BUFFERING_BUFFER_SIZE device.buffering.buffer_size
|
||||||
|
* @field DEVICE_BUFFERING_FRAGMENT_SIZE device.buffering.fragment_size
|
||||||
|
* @field DEVICE_PROFILE_NAME device.profile.name
|
||||||
|
* @field DEVICE_INTENDED_ROLES device.intended_roles
|
||||||
|
* @field DEVICE_PROFILE_DESCRIPTION device.profile.description
|
||||||
|
* @field MODULE_AUTHOR module.author
|
||||||
|
* @field MODULE_DESCRIPTION module.description
|
||||||
|
* @field MODULE_USAGE module.usage
|
||||||
|
* @field MODULE_VERSION module.version
|
||||||
|
* @field FORMAT_SAMPLE_FORMAT format.sample_format
|
||||||
|
* @field FORMAT_RATE format.rate
|
||||||
|
* @field FORMAT_CHANNELS format.channels
|
||||||
|
* @field FORMAT_CHANNEL_MAP format.channel_map
|
||||||
|
* @field CONTEXT_FORCE_DISABLE_SHM context.force.disable.shm
|
||||||
|
* @field BLUETOOTH_CODEC bluetooth.codec
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/// Constructor functions.
|
||||||
|
/// @section constructors
|
||||||
|
|
||||||
|
|
||||||
|
/** Creates a new, empty property list.
|
||||||
|
*
|
||||||
|
* @function new
|
||||||
|
* @return[type=PropList]
|
||||||
|
*/
|
||||||
|
int proplist_new(lua_State*);
|
||||||
|
|
||||||
|
/** Parses a string into a @{PropList}.
|
||||||
|
*
|
||||||
|
* @function from_string
|
||||||
|
* @param[type=string] str The string to parse.
|
||||||
|
* @return[type=PropList]
|
||||||
|
*/
|
||||||
|
int proplist_from_string(lua_State*);
|
||||||
|
|
||||||
|
|
||||||
|
/// Static functions
|
||||||
|
/// @section static
|
||||||
|
|
||||||
|
|
||||||
|
/** Checks if the given string is a valid key.
|
||||||
|
*
|
||||||
|
* @function is_key_valid
|
||||||
|
* @param[type=string] key The string to check.
|
||||||
|
* @return[type=boolean]
|
||||||
|
*/
|
||||||
|
int proplist_key_valid(lua_State*);
|
||||||
|
|
||||||
|
|
||||||
|
// Internal functions
|
||||||
|
|
||||||
|
int proplist_to_lua(lua_State*, pa_proplist*);
|
||||||
|
int proplist__index(lua_State*);
|
||||||
|
int proplist__newindex(lua_State*);
|
||||||
|
int proplist__gc(lua_State*);
|
||||||
|
int proplist__len(lua_State*);
|
||||||
|
int proplist__eq(lua_State*);
|
||||||
|
int proplist__tostring(lua_State*);
|
||||||
|
|
||||||
|
|
||||||
|
/// Methods
|
||||||
|
/// @type PropList
|
||||||
|
|
||||||
|
|
||||||
|
/** Checks if the proplist is empty.
|
||||||
|
*
|
||||||
|
* @function is_empty
|
||||||
|
* @return[type=boolean]
|
||||||
|
*/
|
||||||
|
int proplist_isempty(lua_State*);
|
||||||
|
|
||||||
|
/** Creates a string representation with a custom separator.
|
||||||
|
*
|
||||||
|
* @function tostring_sep
|
||||||
|
* @return[type=string]
|
||||||
|
*/
|
||||||
|
int proplist_tostring_sep(lua_State*);
|
||||||
|
|
||||||
|
/** Removes all keys from the proplist
|
||||||
|
*
|
||||||
|
* @function clear
|
||||||
|
*/
|
||||||
|
int proplist_clear(lua_State*);
|
||||||
|
|
||||||
|
/** Checks if the proplist contains the given key.
|
||||||
|
*
|
||||||
|
* @function contains
|
||||||
|
* @param[type=string] key The key to check for.
|
||||||
|
* @return[type=boolean]
|
||||||
|
*/
|
||||||
|
int proplist_contains(lua_State*);
|
||||||
|
|
||||||
|
/** Duplicates the proplist.
|
||||||
|
*
|
||||||
|
* @function copy
|
||||||
|
* @return[type=proplist]
|
||||||
|
*/
|
||||||
|
int proplist_copy(lua_State*);
|
||||||
|
|
||||||
|
|
||||||
|
static const struct luaL_Reg proplist_f[] = {
|
||||||
|
{"clear", proplist_clear },
|
||||||
|
{ "contains", proplist_contains },
|
||||||
|
{ "copy", proplist_copy },
|
||||||
|
{ "is_empty", proplist_isempty },
|
||||||
|
{ "tostring_sep", proplist_tostring_sep},
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const struct luaL_Reg proplist_mt[] = {
|
||||||
|
{"__gc", proplist__gc },
|
||||||
|
{ "__len", proplist__len },
|
||||||
|
{ "__tostring", proplist__tostring},
|
||||||
|
{ "__eq", proplist__eq },
|
||||||
|
{ "__index", proplist__index },
|
||||||
|
{ "__newindex", proplist__newindex},
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const struct luaL_Reg proplist_lib[] = {
|
||||||
|
{"new", proplist_new },
|
||||||
|
{ "is_key_valid", proplist_key_valid },
|
||||||
|
{ "from_string", proplist_from_string},
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const struct luaU_enumfield proplist_enum[] = {
|
||||||
|
{"MEDIA_TITLE", PA_PROP_MEDIA_TITLE },
|
||||||
|
{ "MEDIA_ARTIST", PA_PROP_MEDIA_ARTIST },
|
||||||
|
{ "MEDIA_COPYRIGHT", PA_PROP_MEDIA_COPYRIGHT },
|
||||||
|
{ "MEDIA_SOFTWARE", PA_PROP_MEDIA_SOFTWARE },
|
||||||
|
{ "MEDIA_LANGUAGE", PA_PROP_MEDIA_LANGUAGE },
|
||||||
|
{ "MEDIA_FILENAME", PA_PROP_MEDIA_FILENAME },
|
||||||
|
{ "MEDIA_ICON", PA_PROP_MEDIA_ICON },
|
||||||
|
{ "MEDIA_ICON_NAME", PA_PROP_MEDIA_ICON_NAME },
|
||||||
|
{ "MEDIA_ROLE", PA_PROP_MEDIA_ROLE },
|
||||||
|
{ "FILTER_WANT", PA_PROP_FILTER_WANT },
|
||||||
|
{ "FILTER_APPLY", PA_PROP_FILTER_APPLY },
|
||||||
|
{ "FILTER_SUPPRESS", PA_PROP_FILTER_SUPPRESS },
|
||||||
|
{ "EVENT_ID", PA_PROP_EVENT_ID },
|
||||||
|
{ "EVENT_DESCRIPTION", PA_PROP_EVENT_DESCRIPTION },
|
||||||
|
{ "EVENT_MOUSE_X", PA_PROP_EVENT_MOUSE_X },
|
||||||
|
{ "EVENT_MOUSE_Y", PA_PROP_EVENT_MOUSE_Y },
|
||||||
|
{ "EVENT_MOUSE_HPOS", PA_PROP_EVENT_MOUSE_HPOS },
|
||||||
|
{ "EVENT_MOUSE_VPOS", PA_PROP_EVENT_MOUSE_VPOS },
|
||||||
|
{ "EVENT_MOUSE_BUTTON", PA_PROP_EVENT_MOUSE_BUTTON },
|
||||||
|
{ "WINDOW_NAME", PA_PROP_WINDOW_NAME },
|
||||||
|
{ "WINDOW_ID", PA_PROP_WINDOW_ID },
|
||||||
|
{ "WINDOW_ICON", PA_PROP_WINDOW_ICON },
|
||||||
|
{ "WINDOW_ICON_NAME", PA_PROP_WINDOW_ICON_NAME },
|
||||||
|
{ "WINDOW_X", PA_PROP_WINDOW_X },
|
||||||
|
{ "WINDOW_Y", PA_PROP_WINDOW_Y },
|
||||||
|
{ "WINDOW_WIDTH", PA_PROP_WINDOW_WIDTH },
|
||||||
|
{ "WINDOW_HEIGHT", PA_PROP_WINDOW_HEIGHT },
|
||||||
|
{ "WINDOW_HPOS", PA_PROP_WINDOW_HPOS },
|
||||||
|
{ "WINDOW_VPOS", PA_PROP_WINDOW_VPOS },
|
||||||
|
{ "WINDOW_DESKTOP", PA_PROP_WINDOW_DESKTOP },
|
||||||
|
{ "WINDOW_X11_DISPLAY", PA_PROP_WINDOW_X11_DISPLAY },
|
||||||
|
{ "WINDOW_X11_SCREEN", PA_PROP_WINDOW_X11_SCREEN },
|
||||||
|
{ "WINDOW_X11_MONITOR", PA_PROP_WINDOW_X11_MONITOR },
|
||||||
|
{ "WINDOW_X11_XID", PA_PROP_WINDOW_X11_XID },
|
||||||
|
{ "APPLICATION_NAME", PA_PROP_APPLICATION_NAME },
|
||||||
|
{ "APPLICATION_ID", PA_PROP_APPLICATION_ID },
|
||||||
|
{ "APPLICATION_VERSION", PA_PROP_APPLICATION_VERSION },
|
||||||
|
{ "APPLICATION_ICON", PA_PROP_APPLICATION_ICON },
|
||||||
|
{ "APPLICATION_ICON_NAME", PA_PROP_APPLICATION_ICON_NAME },
|
||||||
|
{ "APPLICATION_LANGUAGE", PA_PROP_APPLICATION_LANGUAGE },
|
||||||
|
{ "APPLICATION_PROCESS_ID", PA_PROP_APPLICATION_PROCESS_ID },
|
||||||
|
{ "APPLICATION_PROCESS_BINARY", PA_PROP_APPLICATION_PROCESS_BINARY },
|
||||||
|
{ "APPLICATION_PROCESS_USER", PA_PROP_APPLICATION_PROCESS_USER },
|
||||||
|
{ "APPLICATION_PROCESS_HOST", PA_PROP_APPLICATION_PROCESS_HOST },
|
||||||
|
{ "APPLICATION_PROCESS_MACHINE_ID", PA_PROP_APPLICATION_PROCESS_MACHINE_ID},
|
||||||
|
{ "APPLICATION_PROCESS_SESSION_ID", PA_PROP_APPLICATION_PROCESS_SESSION_ID},
|
||||||
|
{ "DEVICE_STRING", PA_PROP_DEVICE_STRING },
|
||||||
|
{ "DEVICE_API", PA_PROP_DEVICE_API },
|
||||||
|
{ "DEVICE_DESCRIPTION", PA_PROP_DEVICE_DESCRIPTION },
|
||||||
|
{ "DEVICE_BUS_PATH", PA_PROP_DEVICE_BUS_PATH },
|
||||||
|
{ "DEVICE_SERIAL", PA_PROP_DEVICE_SERIAL },
|
||||||
|
{ "DEVICE_VENDOR_ID", PA_PROP_DEVICE_VENDOR_ID },
|
||||||
|
{ "DEVICE_VENDOR_NAME", PA_PROP_DEVICE_VENDOR_NAME },
|
||||||
|
{ "DEVICE_PRODUCT_ID", PA_PROP_DEVICE_PRODUCT_ID },
|
||||||
|
{ "DEVICE_PRODUCT_NAME", PA_PROP_DEVICE_PRODUCT_NAME },
|
||||||
|
{ "DEVICE_CLASS", PA_PROP_DEVICE_CLASS },
|
||||||
|
{ "DEVICE_FORM_FACTOR", PA_PROP_DEVICE_FORM_FACTOR },
|
||||||
|
{ "DEVICE_BUS", PA_PROP_DEVICE_BUS },
|
||||||
|
{ "DEVICE_ICON", PA_PROP_DEVICE_ICON },
|
||||||
|
{ "DEVICE_ICON_NAME", PA_PROP_DEVICE_ICON_NAME },
|
||||||
|
{ "DEVICE_ACCESS_MODE", PA_PROP_DEVICE_ACCESS_MODE },
|
||||||
|
{ "DEVICE_MASTER_DEVICE", PA_PROP_DEVICE_MASTER_DEVICE },
|
||||||
|
{ "DEVICE_BUFFERING_BUFFER_SIZE", PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE },
|
||||||
|
{ "DEVICE_BUFFERING_FRAGMENT_SIZE", PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE},
|
||||||
|
{ "DEVICE_PROFILE_NAME", PA_PROP_DEVICE_PROFILE_NAME },
|
||||||
|
{ "DEVICE_INTENDED_ROLES", PA_PROP_DEVICE_INTENDED_ROLES },
|
||||||
|
{ "DEVICE_PROFILE_DESCRIPTION", PA_PROP_DEVICE_PROFILE_DESCRIPTION },
|
||||||
|
{ "MODULE_AUTHOR", PA_PROP_MODULE_AUTHOR },
|
||||||
|
{ "MODULE_DESCRIPTION", PA_PROP_MODULE_DESCRIPTION },
|
||||||
|
{ "MODULE_USAGE", PA_PROP_MODULE_USAGE },
|
||||||
|
{ "MODULE_VERSION", PA_PROP_MODULE_VERSION },
|
||||||
|
{ "FORMAT_SAMPLE_FORMAT", PA_PROP_FORMAT_SAMPLE_FORMAT },
|
||||||
|
{ "FORMAT_RATE", PA_PROP_FORMAT_RATE },
|
||||||
|
{ "FORMAT_CHANNELS", PA_PROP_FORMAT_CHANNELS },
|
||||||
|
{ "FORMAT_CHANNEL_MAP", PA_PROP_FORMAT_CHANNEL_MAP },
|
||||||
|
{ "CONTEXT_FORCE_DISABLE_SHM", PA_PROP_CONTEXT_FORCE_DISABLE_SHM },
|
||||||
|
{ "BLUETOOTH_CODEC", PA_PROP_BLUETOOTH_CODEC },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
|
@ -1,53 +1,42 @@
|
||||||
/// libpulse bindings.
|
#include "pulseaudio.h"
|
||||||
//
|
|
||||||
// @module pulseaudio
|
|
||||||
|
|
||||||
|
#include "context.h"
|
||||||
|
#include "lua_util.h"
|
||||||
|
#include "proplist.h"
|
||||||
|
#include "volume.h"
|
||||||
|
|
||||||
|
#include <lauxlib.h>
|
||||||
#include <lua.h>
|
#include <lua.h>
|
||||||
#include <lualib.h>
|
#include <lualib.h>
|
||||||
#include <lauxlib.h>
|
|
||||||
#include <pulse/glib-mainloop.h>
|
#include <pulse/glib-mainloop.h>
|
||||||
#include "pulseaudio.h"
|
|
||||||
#include "context.h"
|
|
||||||
|
|
||||||
#define LUA_MOD_EXPORT extern
|
|
||||||
#define LUA_PULSEAUDIO "pulseaudio"
|
#define LUA_PULSEAUDIO "pulseaudio"
|
||||||
|
|
||||||
|
|
||||||
#if LUA_VERSION_NUM <= 501
|
#if LUA_VERSION_NUM <= 501
|
||||||
// Shamelessly copied from Lua 5.3 source.
|
// Shamelessly copied from Lua 5.3 source.
|
||||||
// TODO: What's the official way to do this in 5.1?
|
// TODO: What's the official way to do this in 5.1?
|
||||||
void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
|
void luaL_setfuncs(lua_State* L, const luaL_Reg* l, int nup) {
|
||||||
luaL_checkstack(L, nup, "too many upvalues");
|
luaL_checkstack(L, nup, "too many upvalues");
|
||||||
for (; l->name != NULL; l++) { /* fill the table with given functions */
|
for (; l->name != NULL; l++) { /* fill the table with given functions */
|
||||||
if (l->func == NULL) /* place holder? */
|
if (l->func == NULL) /* place holder? */
|
||||||
lua_pushboolean(L, 0);
|
lua_pushboolean(L, 0);
|
||||||
else {
|
else {
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < nup; i++) /* copy upvalues to the top */
|
for (i = 0; i < nup; i++) /* copy upvalues to the top */
|
||||||
lua_pushvalue(L, -nup);
|
lua_pushvalue(L, -nup);
|
||||||
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
|
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
|
||||||
}
|
}
|
||||||
lua_setfield(L, -(nup + 2), l->name);
|
lua_setfield(L, -(nup + 2), l->name);
|
||||||
}
|
}
|
||||||
lua_pop(L, nup); /* remove upvalues */
|
lua_pop(L, nup); /* remove upvalues */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define luaL_newlib(L,l) (luaL_register(L,LUA_PULSEAUDIO,l))
|
#define luaL_newlib(L, l) (luaL_register(L, LUA_PULSEAUDIO, l))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/**
|
int pulseaudio_new(lua_State* L) {
|
||||||
* Creates a new PulseAudio object.
|
|
||||||
*
|
|
||||||
* The API requires a GLib Main Context internally. Currently, only the default context
|
|
||||||
* is supported.
|
|
||||||
*
|
|
||||||
* @return[type=PulseAudio]
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
pulseaudio_new(lua_State* L)
|
|
||||||
{
|
|
||||||
GMainContext* ctx = g_main_context_default();
|
GMainContext* ctx = g_main_context_default();
|
||||||
if (ctx == NULL) {
|
if (ctx == NULL) {
|
||||||
lua_pushfstring(L, "Failed to accquire default GLib Main Context. Are we running in a Main Loop?");
|
lua_pushfstring(L, "Failed to accquire default GLib Main Context. Are we running in a Main Loop?");
|
||||||
|
@ -55,7 +44,7 @@ pulseaudio_new(lua_State* L)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pulseaudio* pa = lua_newuserdata (L, sizeof(pulseaudio));
|
pulseaudio* pa = lua_newuserdata(L, sizeof(pulseaudio));
|
||||||
if (!pa) {
|
if (!pa) {
|
||||||
return luaL_error(L, "failed to create pulseaudio userdata");
|
return luaL_error(L, "failed to create pulseaudio userdata");
|
||||||
}
|
}
|
||||||
|
@ -68,12 +57,10 @@ pulseaudio_new(lua_State* L)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Proxies table index operations to our metatable.
|
* Proxies table index operations to our metatable.
|
||||||
*/
|
*/
|
||||||
int
|
int pulseaudio__index(lua_State* L) {
|
||||||
pulseaudio__index(lua_State* L)
|
|
||||||
{
|
|
||||||
const char* index = luaL_checkstring(L, 2);
|
const char* index = luaL_checkstring(L, 2);
|
||||||
luaL_getmetatable(L, LUA_PULSEAUDIO);
|
luaL_getmetatable(L, LUA_PULSEAUDIO);
|
||||||
lua_getfield(L, -1, index);
|
lua_getfield(L, -1, index);
|
||||||
|
@ -81,28 +68,98 @@ pulseaudio__index(lua_State* L)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Free the PulseAudio object
|
* Free the PulseAudio object
|
||||||
*/
|
*/
|
||||||
int
|
int pulseaudio__gc(lua_State* L) {
|
||||||
pulseaudio__gc(lua_State* L)
|
|
||||||
{
|
|
||||||
pulseaudio* pa = luaL_checkudata(L, 1, LUA_PULSEAUDIO);
|
pulseaudio* pa = luaL_checkudata(L, 1, LUA_PULSEAUDIO);
|
||||||
pa_glib_mainloop_free(pa->mainloop);
|
pa_glib_mainloop_free(pa->mainloop);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int pulseaudio_new_context(lua_State* L) {
|
||||||
pulseaudio_new_context(lua_State* L)
|
|
||||||
{
|
|
||||||
pulseaudio* pa = luaL_checkudata(L, 1, LUA_PULSEAUDIO);
|
pulseaudio* pa = luaL_checkudata(L, 1, LUA_PULSEAUDIO);
|
||||||
return context_new(L, pa_glib_mainloop_get_api(pa->mainloop));
|
return context_new(L, pa_glib_mainloop_get_api(pa->mainloop));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LUA_MOD_EXPORT int luaopen_lua_libpulse_glib(lua_State* L)
|
void createlib_volume(lua_State* L) {
|
||||||
{
|
luaL_newmetatable(L, LUA_PA_VOLUME);
|
||||||
|
|
||||||
|
lua_createtable(L, 0, sizeof volume_f / sizeof volume_f[0]);
|
||||||
|
luaL_setfuncs(L, volume_f, 0);
|
||||||
|
lua_setfield(L, -2, "__index");
|
||||||
|
|
||||||
|
luaL_setfuncs(L, volume_mt, 0);
|
||||||
|
|
||||||
|
#if LUA_VERSION_NUM <= 501
|
||||||
|
luaL_register(L, LUA_PA_VOLUME, volume_lib);
|
||||||
|
#else
|
||||||
|
luaL_newlib(L, volume_lib);
|
||||||
|
#endif
|
||||||
|
lua_setmetatable(L, -2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void createlib_proplist(lua_State* L) {
|
||||||
|
luaL_newmetatable(L, LUA_PA_PROPLIST);
|
||||||
|
|
||||||
|
lua_createtable(L, 0, sizeof proplist_f / sizeof proplist_f[0]);
|
||||||
|
luaL_setfuncs(L, proplist_f, 0);
|
||||||
|
lua_setfield(L, -2, "__index");
|
||||||
|
|
||||||
|
luaL_setfuncs(L, proplist_mt, 0);
|
||||||
|
|
||||||
|
#if LUA_VERSION_NUM <= 501
|
||||||
|
luaL_register(L, LUA_PA_PROPLIST, proplist_lib);
|
||||||
|
#else
|
||||||
|
luaL_newlib(L, proplist_lib);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Create a metatable with an `__index` table for read-only enum fields.
|
||||||
|
lua_createtable(L, 0, 1);
|
||||||
|
|
||||||
|
lua_createtable(L, 0, sizeof proplist_enum / sizeof proplist_enum[0]);
|
||||||
|
for (int i = 0; proplist_enum[i].name != NULL; ++i) {
|
||||||
|
lua_pushstring(L, proplist_enum[i].value);
|
||||||
|
lua_setfield(L, -2, proplist_enum[i].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_setfield(L, -2, "__index");
|
||||||
|
lua_setmetatable(L, -2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void createlib_context(lua_State* L) {
|
||||||
|
luaL_newmetatable(L, LUA_PA_CONTEXT);
|
||||||
|
|
||||||
|
lua_createtable(L, 0, sizeof context_f / sizeof context_f[0]);
|
||||||
|
luaL_setfuncs(L, context_f, 0);
|
||||||
|
lua_setfield(L, -2, "__index");
|
||||||
|
|
||||||
|
luaL_setfuncs(L, context_mt, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void createlib_pulseaudio(lua_State* L) {
|
||||||
|
luaL_newmetatable(L, LUA_PULSEAUDIO);
|
||||||
|
|
||||||
|
lua_createtable(L, 0, sizeof pulseaudio_f / sizeof pulseaudio_f[0]);
|
||||||
|
luaL_setfuncs(L, pulseaudio_f, 0);
|
||||||
|
lua_setfield(L, -2, "__index");
|
||||||
|
|
||||||
|
luaL_setfuncs(L, pulseaudio_mt, 0);
|
||||||
|
|
||||||
|
#if LUA_VERSION_NUM <= 501
|
||||||
|
luaL_register(L, LUA_PULSEAUDIO, pulseaudio_lib);
|
||||||
|
#else
|
||||||
|
luaL_newlib(L, pulseaudio_lib);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LUA_MOD_EXPORT int luaopen_lua_libpulse_glib(lua_State* L) {
|
||||||
// Create a table to store callback refs in, stored in the Lua registry
|
// Create a table to store callback refs in, stored in the Lua registry
|
||||||
lua_pushstring(L, LUA_PULSEAUDIO);
|
lua_pushstring(L, LUA_PULSEAUDIO);
|
||||||
lua_newtable(L);
|
lua_newtable(L);
|
||||||
|
@ -111,12 +168,9 @@ LUA_MOD_EXPORT int luaopen_lua_libpulse_glib(lua_State* L)
|
||||||
lua_settable(L, -3);
|
lua_settable(L, -3);
|
||||||
lua_rawset(L, LUA_REGISTRYINDEX);
|
lua_rawset(L, LUA_REGISTRYINDEX);
|
||||||
|
|
||||||
luaL_newmetatable(L, LUA_PA_CONTEXT);
|
createlib_volume(L);
|
||||||
luaL_setfuncs(L, context_mt, 0);
|
createlib_context(L);
|
||||||
|
createlib_proplist(L);
|
||||||
luaL_newmetatable(L, LUA_PULSEAUDIO);
|
createlib_pulseaudio(L);
|
||||||
luaL_setfuncs(L, pulseaudio_mt, 0);
|
|
||||||
|
|
||||||
luaL_newlib(L, pulseaudio_lib);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
|
/** Bindings for PulseAudio's libpulse, using the GLib Main Loop.
|
||||||
|
*
|
||||||
|
* @module pulseaudio
|
||||||
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "lua.h"
|
|
||||||
#include "lauxlib.h"
|
#include "lauxlib.h"
|
||||||
|
#include "lua.h"
|
||||||
|
|
||||||
#include <pulse/glib-mainloop.h>
|
#include <pulse/glib-mainloop.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#define LUA_PULSEAUDIO "pulseaudio"
|
||||||
#define LUA_MOD_EXPORT __declspec(dllexport)
|
|
||||||
#else
|
|
||||||
#define LUA_MOD_EXPORT extern
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#define LUA_PULSEAUDIO "pulseaudio"
|
|
||||||
#define LUA_PA_REGISTRY "pulseaudio.registry"
|
#define LUA_PA_REGISTRY "pulseaudio.registry"
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,25 +18,43 @@ typedef struct pulseaudio {
|
||||||
} pulseaudio;
|
} pulseaudio;
|
||||||
|
|
||||||
|
|
||||||
int
|
/** Creates a new PulseAudio object.
|
||||||
pulseaudio_new(lua_State*);
|
*
|
||||||
int
|
* @function new
|
||||||
pulseaudio__gc(lua_State*);
|
* @return[type=PulseAudio]
|
||||||
int
|
*/
|
||||||
pulseaudio__index(lua_State*);
|
int pulseaudio_new(lua_State*);
|
||||||
int
|
|
||||||
pulseaudio_new_context(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
static const struct luaL_Reg pulseaudio_mt [] = {
|
int pulseaudio__gc(lua_State*);
|
||||||
{"__index", pulseaudio__index},
|
|
||||||
|
|
||||||
|
/// PulseAudio API
|
||||||
|
/// @type PulseAudio
|
||||||
|
|
||||||
|
|
||||||
|
/** Creates a new PulseAudio context
|
||||||
|
*
|
||||||
|
* @function context
|
||||||
|
* @tparam string name The application name.
|
||||||
|
* @return[type=Context]
|
||||||
|
*/
|
||||||
|
int pulseaudio_new_context(lua_State*);
|
||||||
|
|
||||||
|
|
||||||
|
static const struct luaL_Reg pulseaudio_mt[] = {
|
||||||
{"__gc", pulseaudio__gc},
|
{"__gc", pulseaudio__gc},
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const struct luaL_Reg pulseaudio_f[] = {
|
||||||
{"context", pulseaudio_new_context},
|
{"context", pulseaudio_new_context},
|
||||||
{NULL, NULL}
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static const struct luaL_Reg pulseaudio_lib [] = {
|
static const struct luaL_Reg pulseaudio_lib[] = {
|
||||||
{"new", pulseaudio_new},
|
{"new", pulseaudio_new},
|
||||||
{NULL, NULL}
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
307
src/lua_libpulse_glib/volume.c
Normal file
307
src/lua_libpulse_glib/volume.c
Normal file
|
@ -0,0 +1,307 @@
|
||||||
|
#include "volume.h"
|
||||||
|
|
||||||
|
#include <pulse/xmalloc.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
int volume_to_lua(lua_State* L, const pa_cvolume* pa_volume) {
|
||||||
|
volume_t* volume = lua_newuserdata(L, sizeof(volume_t));
|
||||||
|
if (pa_volume == NULL) {
|
||||||
|
lua_pushfstring(L, "Failed to allocate volume userdata");
|
||||||
|
return lua_error(L);
|
||||||
|
}
|
||||||
|
luaL_getmetatable(L, LUA_PA_VOLUME);
|
||||||
|
lua_setmetatable(L, -2);
|
||||||
|
|
||||||
|
volume->inner = *pa_volume;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// When the value is a plain Lua table, a new `pa_cvolume` is allocated and the caller is
|
||||||
|
// responsible of freeing it with `pa_xfree`.
|
||||||
|
// If a userdata is passed instead, memory is owned by Lua and not be freed.
|
||||||
|
// TODO: Maybe construct userdata from the table, so that memory management is always handled
|
||||||
|
// by GC. Makes this easier to use. But only do so after the current version has been committed
|
||||||
|
// once, just so it stays available.
|
||||||
|
pa_cvolume* volume_from_lua(lua_State* L, int index) {
|
||||||
|
switch (lua_type(L, 2)) {
|
||||||
|
case LUA_TTABLE: {
|
||||||
|
uint8_t channels = (uint8_t) lua_objlen(L, index);
|
||||||
|
if (channels > PA_CHANNELS_MAX) {
|
||||||
|
channels = PA_CHANNELS_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_cvolume* volume = pa_xnew(pa_cvolume, 1);
|
||||||
|
volume->channels = channels;
|
||||||
|
|
||||||
|
for (int i = 0; i < channels; ++i) {
|
||||||
|
lua_pushinteger(L, i + 1);
|
||||||
|
lua_gettable(L, index);
|
||||||
|
pa_volume_t vol = (pa_volume_t) luaL_checkinteger(L, -1);
|
||||||
|
volume->values[i] = PA_CLAMP_VOLUME(vol);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return volume;
|
||||||
|
}
|
||||||
|
case LUA_TUSERDATA: {
|
||||||
|
volume_t* volume = luaL_checkudata(L, index, LUA_PA_VOLUME);
|
||||||
|
return &volume->inner;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
luaL_argerror(L, index, "expected table or userdata");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume__len(lua_State* L) {
|
||||||
|
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
lua_pushinteger(L, volume->inner.channels);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume__eq(lua_State* L) {
|
||||||
|
volume_t* left = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
volume_t* right = luaL_checkudata(L, 2, LUA_PA_VOLUME);
|
||||||
|
lua_pushboolean(L, pa_cvolume_equal(&left->inner, &right->inner));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume__index(lua_State* L) {
|
||||||
|
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
int index = luaL_checkint(L, 2);
|
||||||
|
luaL_argcheck(L, index >= 1 && index <= (PA_CHANNELS_MAX + 1), 2, "channel index out of bounds");
|
||||||
|
lua_pushinteger(L, volume->inner.values[index - 1]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume__newindex(lua_State* L) {
|
||||||
|
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
lua_Integer index = luaL_checkinteger(L, 2);
|
||||||
|
lua_Integer value = luaL_checkinteger(L, 3);
|
||||||
|
luaL_argcheck(L, index >= 1 && index <= (PA_CHANNELS_MAX + 1), 2, "channel index out of bounds");
|
||||||
|
luaL_argcheck(L, PA_VOLUME_IS_VALID(value), 3, "volume value is invalid");
|
||||||
|
volume->inner.values[index - 1] = value;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume__tostring(lua_State* L) {
|
||||||
|
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
char s[PA_CVOLUME_SNPRINT_MAX];
|
||||||
|
pa_cvolume_snprint(s, PA_CVOLUME_SNPRINT_MAX, &volume->inner);
|
||||||
|
lua_pushstring(L, s);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int volume_is_valid(lua_State* L) {
|
||||||
|
// Lua's `checkudata` throws an error, and catching that is expensive.
|
||||||
|
// So we have to manually implement the check.
|
||||||
|
volume_t* volume = lua_touserdata(L, 1);
|
||||||
|
lua_getfield(L, LUA_REGISTRYINDEX, LUA_PA_VOLUME);
|
||||||
|
bool is_userdata = volume != NULL && lua_getmetatable(L, 1) && lua_rawequal(L, -1, -2);
|
||||||
|
// Remove the two metatables
|
||||||
|
lua_pop(L, 2);
|
||||||
|
lua_pushboolean(L, is_userdata && pa_cvolume_valid(&volume->inner));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume_channels_equal_to(lua_State* L) {
|
||||||
|
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
lua_Integer value = luaL_checkinteger(L, 2);
|
||||||
|
|
||||||
|
luaL_argcheck(L, PA_VOLUME_IS_VALID(value), 2, "volume out of bounds");
|
||||||
|
|
||||||
|
lua_pushboolean(L, pa_cvolume_channels_equal_to(&volume->inner, (pa_volume_t) value));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume_is_muted(lua_State* L) {
|
||||||
|
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
lua_pushboolean(L, pa_cvolume_is_muted(&volume->inner));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume_is_norm(lua_State* L) {
|
||||||
|
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
lua_pushboolean(L, pa_cvolume_is_norm(&volume->inner));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume_set_channels(lua_State* L) {
|
||||||
|
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
lua_Integer channels = luaL_checkinteger(L, 2);
|
||||||
|
lua_Integer value = luaL_checkinteger(L, 3);
|
||||||
|
|
||||||
|
luaL_argcheck(L, channels >= 0 && channels <= PA_CHANNELS_MAX, 2, "channel count out of bounds");
|
||||||
|
luaL_argcheck(L, PA_VOLUME_IS_VALID(value), 3, "volume out of bounds");
|
||||||
|
|
||||||
|
pa_cvolume_set(&volume->inner, (unsigned int) channels, (pa_volume_t) value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume_set(lua_State* L) {
|
||||||
|
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
lua_Integer index = luaL_checkinteger(L, 2) - 1;
|
||||||
|
lua_Integer value = luaL_checkinteger(L, 3);
|
||||||
|
|
||||||
|
luaL_argcheck(L, index >= 0 && index <= PA_CHANNELS_MAX, 2, "channel index out of bounds");
|
||||||
|
luaL_argcheck(L, PA_VOLUME_IS_VALID(value), 3, "volume out of bounds");
|
||||||
|
|
||||||
|
volume->inner.values[index] = (pa_volume_t) value;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume_get(lua_State* L) {
|
||||||
|
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
lua_Integer index = luaL_checkinteger(L, 2) - 1;
|
||||||
|
|
||||||
|
luaL_argcheck(L, index > 0 && index < volume->inner.channels, 2, "channel index out of bounds");
|
||||||
|
|
||||||
|
lua_pushinteger(L, volume->inner.values[index]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume_reset(lua_State* L) {
|
||||||
|
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
lua_Integer channels = luaL_checkinteger(L, 2);
|
||||||
|
luaL_argcheck(L, channels >= 0 && channels <= PA_CHANNELS_MAX, 2, "channel count out of bounds");
|
||||||
|
pa_cvolume_reset(&volume->inner, channels);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume_mute(lua_State* L) {
|
||||||
|
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
lua_Integer channels = luaL_checkinteger(L, 2);
|
||||||
|
luaL_argcheck(L, channels >= 0 && channels <= PA_CHANNELS_MAX, 2, "channel count out of bounds");
|
||||||
|
pa_cvolume_mute(&volume->inner, channels);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume_avg(lua_State* L) {
|
||||||
|
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
lua_pushinteger(L, pa_cvolume_avg(&volume->inner));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume_min(lua_State* L) {
|
||||||
|
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
lua_pushinteger(L, pa_cvolume_min(&volume->inner));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume_max(lua_State* L) {
|
||||||
|
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
lua_pushinteger(L, pa_cvolume_max(&volume->inner));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume_inc(lua_State* L) {
|
||||||
|
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
lua_Integer value = luaL_checkinteger(L, 2);
|
||||||
|
luaL_argcheck(L, PA_VOLUME_IS_VALID(value), 2, "volume out of bounds");
|
||||||
|
if (!pa_cvolume_inc(&volume->inner, (pa_volume_t) value)) {
|
||||||
|
return luaL_error(L, "failed to increase volume");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume_dec(lua_State* L) {
|
||||||
|
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
lua_Integer value = luaL_checkinteger(L, 2);
|
||||||
|
luaL_argcheck(L, PA_VOLUME_IS_VALID(value), 2, "volume out of bounds");
|
||||||
|
if (!pa_cvolume_dec(&volume->inner, (pa_volume_t) value)) {
|
||||||
|
return luaL_error(L, "failed to decrease volume");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume_scale(lua_State* L) {
|
||||||
|
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
lua_Integer value = luaL_checkinteger(L, 2);
|
||||||
|
luaL_argcheck(L, PA_VOLUME_IS_VALID(value), 2, "volume out of bounds");
|
||||||
|
if (!pa_cvolume_scale(&volume->inner, (pa_volume_t) value)) {
|
||||||
|
return luaL_error(L, "failed to scale volume");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int volume_multiply(lua_State* L) {
|
||||||
|
volume_t* left = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
|
||||||
|
switch (lua_type(L, 2)) {
|
||||||
|
case LUA_TNUMBER: {
|
||||||
|
lua_Integer value = luaL_checkinteger(L, 2);
|
||||||
|
luaL_argcheck(L, PA_VOLUME_IS_VALID(value), 2, "volume out of bounds");
|
||||||
|
if (!pa_sw_cvolume_multiply_scalar(&left->inner, &left->inner, (pa_volume_t) value)) {
|
||||||
|
return luaL_error(L, "failed to multiply volume");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case LUA_TUSERDATA: {
|
||||||
|
volume_t* right = luaL_checkudata(L, 2, LUA_PA_VOLUME);
|
||||||
|
if (!pa_sw_cvolume_multiply(&left->inner, &left->inner, &right->inner)) {
|
||||||
|
return luaL_error(L, "failed to multiply volume");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return luaL_argerror(L, 2, "expected number or userdata");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int volume_divide(lua_State* L) {
|
||||||
|
volume_t* left = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
||||||
|
|
||||||
|
switch (lua_type(L, 2)) {
|
||||||
|
case LUA_TNUMBER: {
|
||||||
|
lua_Integer value = luaL_checkinteger(L, 2);
|
||||||
|
luaL_argcheck(L, PA_VOLUME_IS_VALID(value), 2, "volume out of bounds");
|
||||||
|
if (!pa_sw_cvolume_divide_scalar(&left->inner, &left->inner, (pa_volume_t) value)) {
|
||||||
|
return luaL_error(L, "failed to divide volume");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case LUA_TUSERDATA: {
|
||||||
|
volume_t* right = luaL_checkudata(L, 2, LUA_PA_VOLUME);
|
||||||
|
if (!pa_sw_cvolume_divide(&left->inner, &left->inner, &right->inner)) {
|
||||||
|
return luaL_error(L, "failed to divide volume");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return luaL_argerror(L, 2, "expected number or userdata");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
237
src/lua_libpulse_glib/volume.h
Normal file
237
src/lua_libpulse_glib/volume.h
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
/** Bindings for libpulse's `pa_cvolume`.
|
||||||
|
*
|
||||||
|
* Contrary to libpulse, methods that change the a @{Volume} object generally don't return anything, but
|
||||||
|
* instead change the instance itself.
|
||||||
|
*
|
||||||
|
* @module pulseaudio.volume
|
||||||
|
*/
|
||||||
|
#ifndef volume_h_INCLUDED
|
||||||
|
#define volume_h_INCLUDED
|
||||||
|
|
||||||
|
#include <lauxlib.h>
|
||||||
|
#include <lua.h>
|
||||||
|
#include <pulse/volume.h>
|
||||||
|
|
||||||
|
#define LUA_PA_VOLUME "pulseaudio.volume"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct volume_t {
|
||||||
|
pa_cvolume inner;
|
||||||
|
} volume_t;
|
||||||
|
|
||||||
|
|
||||||
|
/* Creates a Lua userdatum from/for a PulseAudio volume.
|
||||||
|
*
|
||||||
|
* Control over the `pa_plist`'s memory has to be taken over by this function,
|
||||||
|
* to enable integration into Lua's garbage collection.
|
||||||
|
*
|
||||||
|
* @function volume_to_lua
|
||||||
|
* @treturn Volume
|
||||||
|
*/
|
||||||
|
int volume_to_lua(lua_State*, const pa_cvolume*);
|
||||||
|
|
||||||
|
/* Creates a pa_cvolume from Lua userdata
|
||||||
|
*/
|
||||||
|
pa_cvolume* volume_from_lua(lua_State*, int);
|
||||||
|
|
||||||
|
/* Implements the `#` length operator.
|
||||||
|
* This simply proxies to the `.channels` value.
|
||||||
|
*/
|
||||||
|
int volume__len(lua_State*);
|
||||||
|
int volume__eq(lua_State*);
|
||||||
|
int volume__tostring(lua_State*);
|
||||||
|
int volume__index(lua_State*);
|
||||||
|
int volume__newindex(lua_State*);
|
||||||
|
|
||||||
|
|
||||||
|
/// Static Functions
|
||||||
|
/// @section static
|
||||||
|
|
||||||
|
|
||||||
|
/** Checks whether a value is a valid @{Volume}.
|
||||||
|
*
|
||||||
|
* @function is_valid
|
||||||
|
* @tparam any value The value to check
|
||||||
|
* @treturn boolean
|
||||||
|
*/
|
||||||
|
int volume_is_valid(lua_State*);
|
||||||
|
|
||||||
|
/// Volume
|
||||||
|
/// @type Volume
|
||||||
|
|
||||||
|
/** Returns the average volume over all channels.
|
||||||
|
*
|
||||||
|
* @function Volume:avg
|
||||||
|
* @treturn number
|
||||||
|
*/
|
||||||
|
int volume_avg(lua_State*);
|
||||||
|
|
||||||
|
/** Returns `true` if the volume of all channels is equal to the specified value.
|
||||||
|
*
|
||||||
|
* @function Volume:channels_equal_to
|
||||||
|
* @tparam number value Volume to compare to
|
||||||
|
* @treturn boolean
|
||||||
|
*/
|
||||||
|
int volume_channels_equal_to(lua_State*);
|
||||||
|
|
||||||
|
/** Decreases the volume by the given amount.
|
||||||
|
*
|
||||||
|
* The proportions between the channels are kept.
|
||||||
|
*
|
||||||
|
* @function Volume:dec
|
||||||
|
* @tparam number value The value to decrease by.
|
||||||
|
*/
|
||||||
|
int volume_dec(lua_State*);
|
||||||
|
|
||||||
|
/** Divides the volume by the given value.
|
||||||
|
*
|
||||||
|
* The value to divide by may either be a scalar, that's applied to all channels,
|
||||||
|
* or another instance of @{Volume}, which would be applied channel by channel.
|
||||||
|
*
|
||||||
|
* It is possible to divide a @{Volume} by itself.
|
||||||
|
*
|
||||||
|
* This is only valid for software volumes.
|
||||||
|
*
|
||||||
|
* @function Volume:divide
|
||||||
|
* @tparam number|Volume value The volume to divide by.
|
||||||
|
*/
|
||||||
|
int volume_divide(lua_State*);
|
||||||
|
|
||||||
|
/** Returns the volume of a single channel.
|
||||||
|
*
|
||||||
|
* @function Volume:get
|
||||||
|
* @tparam number index The channel index
|
||||||
|
* @treturn number The channel's volume
|
||||||
|
*/
|
||||||
|
int volume_get(lua_State*);
|
||||||
|
|
||||||
|
/** Increases the volume by the given amount.
|
||||||
|
*
|
||||||
|
* The proportions between the channels are kept.
|
||||||
|
*
|
||||||
|
* @function Volume:inc
|
||||||
|
* @tparam number value the value to increase by.
|
||||||
|
*/
|
||||||
|
int volume_inc(lua_State*);
|
||||||
|
|
||||||
|
/** Returns `true` when all channels are muted.
|
||||||
|
*
|
||||||
|
* @function Volume:is_muted
|
||||||
|
* @treturn boolean
|
||||||
|
*/
|
||||||
|
int volume_is_muted(lua_State*);
|
||||||
|
|
||||||
|
/** Returns `true` when all channels are on normal level.
|
||||||
|
*
|
||||||
|
* @function Volume:is_norm
|
||||||
|
* @treturn boolean
|
||||||
|
*/
|
||||||
|
int volume_is_norm(lua_State*);
|
||||||
|
|
||||||
|
/** Returns the maximum volume out of all channels.
|
||||||
|
*
|
||||||
|
* @function Volume:max
|
||||||
|
* @treturn number
|
||||||
|
*/
|
||||||
|
int volume_max(lua_State*);
|
||||||
|
|
||||||
|
/** Returns the minimum volume out of all channels.
|
||||||
|
*
|
||||||
|
* @function Volume:min
|
||||||
|
* @treturn number
|
||||||
|
*/
|
||||||
|
int volume_min(lua_State*);
|
||||||
|
|
||||||
|
/** Multiplies the volume by the given amount.
|
||||||
|
*
|
||||||
|
* The value to multiply with may either be a scalar, that's applied to all channels,
|
||||||
|
* or another instance of @{Volume}, which would be applied channel by channel.
|
||||||
|
*
|
||||||
|
* It is possible to multiply a @{Volume} by itself.
|
||||||
|
*
|
||||||
|
* This is only valid for software volumes.
|
||||||
|
*
|
||||||
|
* @function Volume:multiply
|
||||||
|
* @tparam number|Volume value
|
||||||
|
*/
|
||||||
|
int volume_multiply(lua_State*);
|
||||||
|
|
||||||
|
/** Mutes all channels.
|
||||||
|
*
|
||||||
|
* @function Volume:mute
|
||||||
|
*/
|
||||||
|
int volume_mute(lua_State*);
|
||||||
|
|
||||||
|
/** Resets all channels to normal volume.
|
||||||
|
*
|
||||||
|
* @function Volume:reset
|
||||||
|
*/
|
||||||
|
int volume_reset(lua_State*);
|
||||||
|
|
||||||
|
/** Scales all channels to the passed amount.
|
||||||
|
*
|
||||||
|
* This adjust all channel volumes so that the maximum between them equals the given value, while
|
||||||
|
* keeping proportions between channels the same.
|
||||||
|
*
|
||||||
|
* @function Volume:scale
|
||||||
|
* @tparam number value The value to scale to.
|
||||||
|
*/
|
||||||
|
int volume_scale(lua_State*);
|
||||||
|
|
||||||
|
/** Sets a channel to the given value.
|
||||||
|
*
|
||||||
|
* @function Volume:set
|
||||||
|
* @tparam number index The channel index.
|
||||||
|
* @tparam number value The volume to set to.
|
||||||
|
*/
|
||||||
|
int volume_set(lua_State*);
|
||||||
|
|
||||||
|
/** Sets a number of channels to the given volume value.
|
||||||
|
*
|
||||||
|
* @function Volume:set_channels
|
||||||
|
* @tparam number channels The number of channels to set
|
||||||
|
* @tparam number value The volume to set to.
|
||||||
|
*/
|
||||||
|
int volume_set_channels(lua_State*);
|
||||||
|
|
||||||
|
|
||||||
|
static const struct luaL_Reg volume_f[] = {
|
||||||
|
{"avg", volume_avg },
|
||||||
|
{ "channels_equal_to", volume_channels_equal_to},
|
||||||
|
{ "dec", volume_dec },
|
||||||
|
{ "divide", volume_divide },
|
||||||
|
{ "get", volume_get },
|
||||||
|
{ "inc", volume_inc },
|
||||||
|
{ "is_muted", volume_is_muted },
|
||||||
|
{ "is_norm", volume_is_norm },
|
||||||
|
{ "is_valid", volume_is_valid },
|
||||||
|
{ "max", volume_max },
|
||||||
|
{ "min", volume_min },
|
||||||
|
{ "multiply", volume_multiply },
|
||||||
|
{ "mute", volume_mute },
|
||||||
|
{ "reset", volume_reset },
|
||||||
|
{ "scale", volume_scale },
|
||||||
|
{ "set", volume_set },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// We don't add `__index` here, as it needs to be handled specially,
|
||||||
|
// to allow indexing both by channel index as well as accessing the methods above.
|
||||||
|
static const struct luaL_Reg volume_mt[] = {
|
||||||
|
{"__len", volume__len },
|
||||||
|
{ "__tostring", volume__tostring},
|
||||||
|
{ "__eq", volume__eq },
|
||||||
|
{ "__newindex", volume__newindex},
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const struct luaL_Reg volume_lib[] = {
|
||||||
|
{"is_valid", volume_is_valid},
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // volume_h_INCLUDED
|
||||||
|
|
|
@ -14,7 +14,7 @@ run() {
|
||||||
"$@"
|
"$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
find src -iname '*.lua' -or -iname '*.c' -not -path '*/internal/*' | while read -r f; do
|
find src -iname '*.lua' -or -iname '*.c' -or -iname '*.h' -not -path '*/internal/*' | while read -r f; do
|
||||||
mkdir -p "$(dirname "$OUT/$f")"
|
mkdir -p "$(dirname "$OUT/$f")"
|
||||||
run "$LUA" ./tools/preprocessor.lua "$f" "$OUT/$f"
|
run "$LUA" ./tools/preprocessor.lua "$f" "$OUT/$f"
|
||||||
done
|
done
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue