feat: Implement basic context object
This commit is contained in:
parent
1da668d7d6
commit
c4058583f1
4 changed files with 270 additions and 18 deletions
151
src/lua_libpulse_glib/context.c
Normal file
151
src/lua_libpulse_glib/context.c
Normal 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;
|
||||
}
|
40
src/lua_libpulse_glib/context.h
Normal file
40
src/lua_libpulse_glib/context.h
Normal 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}
|
||||
};
|
|
@ -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);
|
||||
|
||||
|
|
44
src/lua_libpulse_glib/pulseaudio.h
Normal file
44
src/lua_libpulse_glib/pulseaudio.h
Normal 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}
|
||||
};
|
Loading…
Add table
Reference in a new issue