1
Fork 0

feat: Implement basic context object

This commit is contained in:
Lucas Schwiderski 2022-04-06 09:21:55 +02:00
parent 1da668d7d6
commit c4058583f1
Signed by: lucas
GPG key ID: AA12679AAA6DF4D8
4 changed files with 270 additions and 18 deletions

View file

@ -0,0 +1,151 @@
#include "pulseaudio.h"
#include "context.h"
#include "pulse/context.h"
void
context_state_callback(pa_context* c, void* userdata)
{
context_state_callback_data* data = (context_state_callback_data*) userdata;
luaL_checktype(data->L, 1, LUA_TFUNCTION);
luaL_checkudata(data->L, 2, LUA_PA_CONTEXT);
// `lua_call` will pop the function and arguments from the stack, but this callback will likely be called
// multiple times.
// To preseve the values for future calls, we need to duplicate them.
lua_pushvalue(data->L, 1);
lua_pushvalue(data->L, 2);
pa_context_state_t state = pa_context_get_state(c);
lua_pushinteger(data->L, state);
lua_call(data->L, 2, 0);
}
int
context_new(lua_State* L, pa_mainloop_api* pa_api)
{
const char* name = luaL_checkstring(L, -1);
// 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);
if (ctx == NULL) {
return luaL_error(L, "failed to create pulseaudio context");
}
lua_pa_context* lgi_ctx = lua_newuserdata (L, sizeof(lua_pa_context));
if (lgi_ctx == NULL) {
return luaL_error(L, "failed to create context userdata");
}
lgi_ctx->context = ctx;
lgi_ctx->connected = FALSE;
lgi_ctx->state_callback_data = (context_state_callback_data*) calloc(1, sizeof(struct context_state_callback_data));
luaL_getmetatable(L, LUA_PA_CONTEXT);
lua_setmetatable(L, -2);
return 1;
}
int
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);
if (ctx->connected == TRUE) {
pa_context_disconnect(ctx->context);
ctx->connected = 0;
}
if (ctx->state_callback_data != NULL) {
lua_pushstring(L, LUA_PULSEAUDIO);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_pushstring(L, LUA_PA_REGISTRY);
lua_gettable(L, -2);
luaL_unref(L, -1, ctx->state_callback_data->thread_ref);
free(ctx->state_callback_data);
}
pa_context_unref(ctx->context);
return 0;
}
int
context_connect(lua_State* L)
{
int nargs = lua_gettop(L);
const char* server = NULL;
if (lua_type(L, 2) == LUA_TSTRING)
server = lua_tostring(L, 2);
else if (lua_type(L, 2) != LUA_TNIL) {
const char *typearg;
if (luaL_getmetafield(L, 2, "__name") == LUA_TSTRING)
typearg = lua_tostring(L, -1);
else if (lua_type(L, 2) == LUA_TLIGHTUSERDATA)
typearg = "light userdata";
else
typearg = luaL_typename(L, 2);
return luaL_argerror(L, 2, lua_pushfstring(L, "string or nil expected, got %s", typearg));
}
luaL_checktype(L, 3, LUA_TFUNCTION);
lua_pa_context* ctx = luaL_checkudata(L, 1, LUA_PA_CONTEXT);
pa_context_flags_t flags = PA_CONTEXT_NOAUTOSPAWN;
if (nargs > 3)
flags = luaL_checkinteger(L, 4);
// 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 and arguments to the thread's stack
lua_pushvalue(L, 3);
lua_pushvalue(L, 1);
lua_xmove(L, thread, 2);
context_state_callback_data* data = calloc(1, sizeof(struct context_state_callback_data));
data->L = thread;
data->thread_ref = thread_ref;
ctx->state_callback_data = data;
pa_context_set_state_callback(ctx->context, context_state_callback, data);
// TODO: Check if I need to create bindings for `pa_spawn_api`.
int ret = pa_context_connect(ctx->context, server, flags, NULL);
if (ret < 0) {
return luaL_error(L, "failed to connect: %s", pa_strerror(ret));
}
return 0;
}
int
context_get_state(lua_State* L)
{
lua_pa_context* ctx = luaL_checkudata(L, 1, LUA_PA_CONTEXT);
pa_context_state_t state = pa_context_get_state(ctx->context);
lua_pushinteger(L, state);
return 1;
}

View file

@ -0,0 +1,40 @@
#pragma once
#include "stdbool.h"
#include "lua.h"
#include "lauxlib.h"
#include "pulse/context.h"
#include "pulse/mainloop-api.h"
#define LUA_PA_CONTEXT "pulseaudio.context"
typedef struct context_state_callback_data {
lua_State* L;
int thread_ref;
} context_state_callback_data;
typedef struct lua_pa_context {
pa_context* context;
bool connected;
context_state_callback_data* state_callback_data;
} lua_pa_context;
int
context_new(lua_State*, pa_mainloop_api*);
int
context__index(lua_State*);
int
context__gc(lua_State*);
int
context_connect(lua_State*);
static const struct luaL_Reg context_mt [] = {
{"__index", context__index},
{"__gc", context__gc},
{"connect", context_connect},
{NULL, NULL}
};

View file

@ -6,6 +6,8 @@
#include <lualib.h>
#include <lauxlib.h>
#include <pulse/glib-mainloop.h>
#include "pulseaudio.h"
#include "context.h"
#define LUA_MOD_EXPORT extern
#define LUA_PULSEAUDIO "pulseaudio"
@ -13,7 +15,7 @@
#if LUA_VERSION_NUM <= 501
// Shamelessly copied from Lua 5.3 source.
// TODO: Is there any 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) {
luaL_checkstack(L, nup, "too many upvalues");
for (; l->name != NULL; l++) { /* fill the table with given functions */
@ -35,11 +37,6 @@ void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
#endif
typedef struct pulseaudio {
pa_glib_mainloop* mainloop;
} pulseaudio;
/**
* Creates a new PulseAudio object.
*
@ -48,7 +45,7 @@ typedef struct pulseaudio {
*
* @return[type=PulseAudio]
*/
static int
int
pulseaudio_new(lua_State* L)
{
GMainContext* ctx = g_main_context_default();
@ -71,10 +68,23 @@ pulseaudio_new(lua_State* L)
}
/**
* Proxies table index operations to our metatable.
*/
int
pulseaudio__index(lua_State* L)
{
const char* index = luaL_checkstring(L, 2);
luaL_getmetatable(L, LUA_PULSEAUDIO);
lua_getfield(L, -1, index);
return 1;
}
/**
* Free the PulseAudio object
*/
static int
int
pulseaudio__gc(lua_State* L)
{
pulseaudio* pa = luaL_checkudata(L, 1, LUA_PULSEAUDIO);
@ -83,20 +93,27 @@ pulseaudio__gc(lua_State* L)
}
static const struct luaL_Reg pulseaudio_mt [] = {
{"__gc", pulseaudio__gc},
{NULL, NULL}
};
static const struct luaL_Reg pulseaudio_lib [] = {
{"new", pulseaudio_new},
{NULL, NULL}
};
int
pulseaudio_new_context(lua_State* L)
{
pulseaudio* pa = luaL_checkudata(L, 1, LUA_PULSEAUDIO);
return context_new(L, pa_glib_mainloop_get_api(pa->mainloop));
}
LUA_MOD_EXPORT int luaopen_lua_libpulse_glib(lua_State* L)
{
// Create a table to store callback refs in, stored in the Lua registry
lua_pushstring(L, LUA_PULSEAUDIO);
lua_newtable(L);
lua_pushstring(L, LUA_PA_REGISTRY);
lua_newtable(L);
lua_settable(L, -3);
lua_rawset(L, LUA_REGISTRYINDEX);
luaL_newmetatable(L, LUA_PA_CONTEXT);
luaL_setfuncs(L, context_mt, 0);
luaL_newmetatable(L, LUA_PULSEAUDIO);
luaL_setfuncs(L, pulseaudio_mt, 0);

View file

@ -0,0 +1,44 @@
#pragma once
#include "lua.h"
#include "lauxlib.h"
#include <pulse/glib-mainloop.h>
#ifdef _WIN32
#define LUA_MOD_EXPORT __declspec(dllexport)
#else
#define LUA_MOD_EXPORT extern
#endif
#define LUA_PULSEAUDIO "pulseaudio"
#define LUA_PA_REGISTRY "pulseaudio.registry"
typedef struct pulseaudio {
pa_glib_mainloop* mainloop;
} pulseaudio;
int
pulseaudio_new(lua_State*);
int
pulseaudio__gc(lua_State*);
int
pulseaudio__index(lua_State*);
int
pulseaudio_new_context(lua_State*);
static const struct luaL_Reg pulseaudio_mt [] = {
{"__index", pulseaudio__index},
{"__gc", pulseaudio__gc},
{"context", pulseaudio_new_context},
{NULL, NULL}
};
static const struct luaL_Reg pulseaudio_lib [] = {
{"new", pulseaudio_new},
{NULL, NULL}
};