Implement event subscription
This commit is contained in:
parent
d2f424cbe2
commit
aca076f51b
2 changed files with 51 additions and 10 deletions
|
@ -1,7 +1,7 @@
|
||||||
#include "context.h"
|
#include "context.h"
|
||||||
|
|
||||||
#include "pulseaudio.h"
|
|
||||||
#include "lua_util.h"
|
#include "lua_util.h"
|
||||||
|
#include "pulseaudio.h"
|
||||||
|
|
||||||
#include <pulse/context.h>
|
#include <pulse/context.h>
|
||||||
#include <pulse/error.h>
|
#include <pulse/error.h>
|
||||||
|
@ -33,18 +33,29 @@ void context_event_callback(pa_context* c, pa_subscription_event_type_t event_ty
|
||||||
simple_callback_data* data = (simple_callback_data*) userdata;
|
simple_callback_data* data = (simple_callback_data*) userdata;
|
||||||
lua_State* L = data->L;
|
lua_State* L = data->L;
|
||||||
|
|
||||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
luaL_checkudata(L, 2, LUA_PA_CONTEXT);
|
luaL_checkudata(L, 2, LUA_PA_CONTEXT);
|
||||||
|
|
||||||
// `lua_call` will pop the function and arguments from the stack, but this callback will likely be called
|
// Iterate over the list of subscription callbacks and call each one
|
||||||
// multiple times.
|
lua_pushnil(L);
|
||||||
// To preseve the values for future calls, we need to duplicate them.
|
while (lua_next(L, 1) != 0) {
|
||||||
lua_pushvalue(L, 1);
|
// TODO: Once we do have the "nothing here" value, we need to check for that here.
|
||||||
lua_pushvalue(L, 2);
|
|
||||||
lua_pushinteger(L, event_type);
|
|
||||||
lua_pushinteger(L, index);
|
|
||||||
|
|
||||||
lua_call(L, 3, 0);
|
// Copy the `self` parameter
|
||||||
|
lua_pushvalue(L, 2);
|
||||||
|
lua_pushinteger(L, event_type);
|
||||||
|
lua_pushinteger(L, index);
|
||||||
|
|
||||||
|
lua_call(L, 3, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void context_subscribe_success_callback(pa_context* _c, int success, void* userdata) {
|
||||||
|
simple_callback_data* data = (simple_callback_data*) userdata;
|
||||||
|
if (!success) {
|
||||||
|
luaL_error(data->L, "Failed to subscribe to events");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -62,6 +73,7 @@ int context_new(lua_State* L, pa_mainloop_api* pa_api) {
|
||||||
}
|
}
|
||||||
lgi_ctx->context = ctx;
|
lgi_ctx->context = ctx;
|
||||||
lgi_ctx->connected = FALSE;
|
lgi_ctx->connected = FALSE;
|
||||||
|
lgi_ctx->subscribed = FALSE;
|
||||||
lgi_ctx->state_callback_data = prepare_lua_callback(L, 0);
|
lgi_ctx->state_callback_data = prepare_lua_callback(L, 0);
|
||||||
lgi_ctx->event_callback_data = prepare_lua_callback(L, 0);
|
lgi_ctx->event_callback_data = prepare_lua_callback(L, 0);
|
||||||
|
|
||||||
|
@ -71,6 +83,10 @@ int context_new(lua_State* L, pa_mainloop_api* pa_api) {
|
||||||
luaL_getmetatable(L, LUA_PA_CONTEXT);
|
luaL_getmetatable(L, LUA_PA_CONTEXT);
|
||||||
lua_setmetatable(L, -2);
|
lua_setmetatable(L, -2);
|
||||||
|
|
||||||
|
// Copy the `context` value to the event callback
|
||||||
|
lua_pushvalue(L, -1);
|
||||||
|
lua_xmove(L, lgi_ctx->event_callback_data->L, 1);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,8 +213,19 @@ int context_subscribe(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);
|
||||||
luaL_checktype(L, 2, LUA_TFUNCTION);
|
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||||
|
|
||||||
|
// This call is only effective when the connection state is "ready".
|
||||||
|
// So we have to do it here, rather than during `context_connect`,
|
||||||
|
// but we also need to make sure it's only called once.
|
||||||
|
if (!ctx->subscribed) {
|
||||||
|
pa_context_subscribe(ctx->context,
|
||||||
|
PA_SUBSCRIPTION_MASK_ALL,
|
||||||
|
context_subscribe_success_callback,
|
||||||
|
ctx->event_callback_data);
|
||||||
|
}
|
||||||
|
|
||||||
size_t pos = lua_rawlen(ctx->event_callback_data->L, 1) + 1;
|
size_t pos = lua_rawlen(ctx->event_callback_data->L, 1) + 1;
|
||||||
// Duplicate the callback function, so we can move it over to the other thread
|
// Duplicate the callback function, so we can move it over to the other thread
|
||||||
|
// TODO: Do we actually need to duplicate?
|
||||||
lua_pushvalue(L, 2);
|
lua_pushvalue(L, 2);
|
||||||
lua_xmove(L, ctx->event_callback_data->L, 1);
|
lua_xmove(L, ctx->event_callback_data->L, 1);
|
||||||
lua_rawseti(ctx->event_callback_data->L, 1, pos);
|
lua_rawseti(ctx->event_callback_data->L, 1, pos);
|
||||||
|
@ -218,6 +245,14 @@ int context_unsubscribe(lua_State* L) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Handle calling this twice on the same index.
|
||||||
|
// Given that we use an array to track things, and Lua's way of counting in arrays
|
||||||
|
// doesn't like `nil`s, we probably need a special "nothing here" value that's not `nil`,
|
||||||
|
// and signifies an index that has already been unsubscribed.
|
||||||
|
|
||||||
|
// TODO: Simplify things. Supporting just the index is enough.
|
||||||
|
// Comparing by function is convenient, but also confusing to inexperienced devs.
|
||||||
|
|
||||||
switch (lua_type(L, 2)) {
|
switch (lua_type(L, 2)) {
|
||||||
case LUA_TNUMBER: {
|
case LUA_TNUMBER: {
|
||||||
pos = lua_tointeger(L, 2);
|
pos = lua_tointeger(L, 2);
|
||||||
|
@ -257,6 +292,8 @@ int context_unsubscribe(lua_State* L) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: As explained above, we need to handle calling unsubscribe twice better.
|
||||||
|
// Indices should not be re-used but replaced by a special "nothing here" value.
|
||||||
for (; pos < len; ++pos) {
|
for (; pos < len; ++pos) {
|
||||||
lua_rawgeti(thread_L, 1, pos + 1);
|
lua_rawgeti(thread_L, 1, pos + 1);
|
||||||
lua_rawseti(thread_L, 1, pos);
|
lua_rawseti(thread_L, 1, pos);
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
typedef struct lua_pa_context {
|
typedef struct lua_pa_context {
|
||||||
pa_context* context;
|
pa_context* context;
|
||||||
bool connected;
|
bool connected;
|
||||||
|
bool subscribed;
|
||||||
simple_callback_data* state_callback_data;
|
simple_callback_data* state_callback_data;
|
||||||
simple_callback_data* event_callback_data;
|
simple_callback_data* event_callback_data;
|
||||||
} lua_pa_context;
|
} lua_pa_context;
|
||||||
|
@ -95,6 +96,9 @@ int context_get_state(lua_State*);
|
||||||
* Any number of callbacks may be registered at the same time, and can be unscubscribed with
|
* Any number of callbacks may be registered at the same time, and can be unscubscribed with
|
||||||
* @{Context:unsubscribe}, using the returned subscription ID.
|
* @{Context:unsubscribe}, using the returned subscription ID.
|
||||||
*
|
*
|
||||||
|
* This must called after the connection state changed to "ready" (index `4`). Otherwise
|
||||||
|
* it will have no effect.
|
||||||
|
*
|
||||||
* @function Context:subscribe
|
* @function Context:subscribe
|
||||||
* @tparam function cb
|
* @tparam function cb
|
||||||
* @treturn number The subscription ID.
|
* @treturn number The subscription ID.
|
||||||
|
|
Loading…
Add table
Reference in a new issue