From 823431e44732a0824658c82de29aaa92f8f39f79 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 12 Feb 2009 03:18:05 +0100 Subject: allow sending meta/policy events to clients --- src/pulse/context.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/pulse/context.h | 11 ++++++++++ src/pulse/def.h | 10 +++++++++ src/pulse/internal.h | 7 ++++++ src/pulse/stream.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ src/pulse/stream.h | 14 +++++++++++- 6 files changed, 161 insertions(+), 2 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/context.c b/src/pulse/context.c index d41e62e2..81050914 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -95,7 +95,10 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_RECORD_STREAM_SUSPENDED] = pa_command_stream_suspended, [PA_COMMAND_STARTED] = pa_command_stream_started, [PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event, - [PA_COMMAND_EXTENSION] = pa_command_extension + [PA_COMMAND_EXTENSION] = pa_command_extension, + [PA_COMMAND_PLAYBACK_STREAM_EVENT] = pa_command_stream_event, + [PA_COMMAND_RECORD_STREAM_EVENT] = pa_command_stream_event, + [PA_COMMAND_CLIENT_EVENT] = pa_command_client_event }; static void context_free(pa_context *c); @@ -112,6 +115,9 @@ static void reset_callbacks(pa_context *c) { c->subscribe_callback = NULL; c->subscribe_userdata = NULL; + c->event_callback = NULL; + c->event_userdata = NULL; + c->ext_stream_restore.callback = NULL; c->ext_stream_restore.userdata = NULL; } @@ -917,6 +923,17 @@ void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, voi c->state_userdata = userdata; } +void pa_context_set_event_callback(pa_context *c, pa_context_event_cb_t cb, void *userdata) { + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED) + return; + + c->event_callback = cb; + c->event_userdata = userdata; +} + int pa_context_is_pending(pa_context *c) { pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); @@ -1245,6 +1262,11 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t pa_context_ref(c); + if (c->version < 15) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + if (pa_tagstruct_getu32(t, &idx) < 0 || pa_tagstruct_gets(t, &name) < 0) { pa_context_fail(c, PA_ERR_PROTOCOL); @@ -1259,3 +1281,41 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t finish: pa_context_unref(c); } + + +void pa_command_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_context *c = userdata; + pa_proplist *pl = NULL; + const char *event; + + pa_assert(pd); + pa_assert(command == PA_COMMAND_CLIENT_EVENT); + pa_assert(t); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + pa_context_ref(c); + + if (c->version < 15) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + pl = pa_proplist_new(); + + if (pa_tagstruct_gets(t, &event) < 0 || + pa_tagstruct_get_proplist(t, pl) < 0 || + !pa_tagstruct_eof(t) || !event) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + if (c->event_callback) + c->event_callback(c, event, pl, c->event_userdata); + +finish: + pa_context_unref(c); + + if (pl) + pa_proplist_free(pl); +} diff --git a/src/pulse/context.h b/src/pulse/context.h index dfb7e4a1..2ae4c013 100644 --- a/src/pulse/context.h +++ b/src/pulse/context.h @@ -164,6 +164,13 @@ typedef void (*pa_context_notify_cb_t)(pa_context *c, void *userdata); /** A generic callback for operation completion */ typedef void (*pa_context_success_cb_t) (pa_context *c, int success, void *userdata); +/** A callback for asynchronous meta/policy event messages. The set + * of defined events can be extended at any time. Also, server modules + * may introduce additional message types so make sure that your + * callback function ignores messages it doesn't know. \since + * 0.9.15 */ +typedef void (*pa_context_event_cb_t)(pa_context *c, const char *name, pa_proplist *p, void *userdata); + /** Instantiate a new connection context with an abstract mainloop API * and an application name. It is recommended to use pa_context_new_with_proplist() * instead and specify some initial properties.*/ @@ -183,6 +190,10 @@ pa_context* pa_context_ref(pa_context *c); /** Set a callback function that is called whenever the context status changes */ void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata); +/** Set a callback function that is called whenver a meta/policy + * control event is received. \since 0.9.15 */ +void pa_context_set_event_callback(pa_context *p, pa_context_event_cb_t cb, void *userdata); + /** Return the error number of the last failed operation */ int pa_context_errno(pa_context *c); diff --git a/src/pulse/def.h b/src/pulse/def.h index d4fa821a..7f3a0c5d 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -840,6 +840,16 @@ static inline int PA_SOURCE_IS_OPENED(pa_source_state_t x) { /** A generic free() like callback prototype */ typedef void (*pa_free_cb_t)(void *p); +/** A stream policy/meta event requesting that an application should + * cork a specific stream. See pa_stream_event_cb_t for more + * information, \since 0.9.15 */ +#define PA_STREAM_EVENT_REQUEST_CORK "request-cork" + +/** A stream policy/meta event requesting that an application should + * cork a specific stream. See pa_stream_event_cb_t for more + * information, \since 0.9.15 */ +#define PA_STREAM_EVENT_REQUEST_UNCORK "request-uncork" + PA_C_DECL_END #endif diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 9a2d6457..e533625d 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -71,6 +71,8 @@ struct pa_context { void *state_userdata; pa_context_subscribe_cb_t subscribe_callback; void *subscribe_userdata; + pa_context_event_cb_t event_callback; + void *event_userdata; pa_mempool *mempool; @@ -181,6 +183,8 @@ struct pa_stream { void *suspended_userdata; pa_stream_notify_cb_t started_callback; void *started_userdata; + pa_stream_event_cb_t event_callback; + void *event_userdata; }; typedef void (*pa_operation_cb_t)(void); @@ -207,6 +211,9 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32 void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +void pa_command_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); + pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata); void pa_operation_done(pa_operation *o); diff --git a/src/pulse/stream.c b/src/pulse/stream.c index fe2514d9..b36bf9b1 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -69,6 +69,8 @@ static void reset_callbacks(pa_stream *s) { s->suspended_userdata = NULL; s->started_callback = NULL; s->started_userdata = NULL; + s->event_callback = NULL; + s->event_userdata = NULL; } pa_stream *pa_stream_new_with_proplist( @@ -565,6 +567,52 @@ finish: pa_context_unref(c); } +void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_context *c = userdata; + pa_stream *s; + uint32_t channel; + pa_proplist *pl = NULL; + const char *event; + + pa_assert(pd); + pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_EVENT || command == PA_COMMAND_RECORD_STREAM_EVENT); + pa_assert(t); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + pa_context_ref(c); + + if (c->version < 15) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + pl = pa_proplist_new(); + + if (pa_tagstruct_getu32(t, &channel) < 0 || + pa_tagstruct_gets(t, &event) < 0 || + pa_tagstruct_get_proplist(t, pl) < 0 || + !pa_tagstruct_eof(t) || !event) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_EVENT ? c->playback_streams : c->record_streams, channel))) + goto finish; + + if (s->state != PA_STREAM_READY) + goto finish; + + if (s->event_callback) + s->event_callback(s, event, pl, s->event_userdata); + +finish: + pa_context_unref(c); + + if (pl) + pa_proplist_free(pl); +} + void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { pa_stream *s; pa_context *c = userdata; @@ -1696,6 +1744,17 @@ void pa_stream_set_started_callback(pa_stream *s, pa_stream_notify_cb_t cb, void s->started_userdata = userdata; } +void pa_stream_set_event_callback(pa_stream *s, pa_stream_event_cb_t cb, void *userdata) { + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + + s->event_callback = cb; + s->event_userdata = userdata; +} + void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { pa_operation *o = userdata; int success = 1; diff --git a/src/pulse/stream.h b/src/pulse/stream.h index 1bec1ebf..3965e9a2 100644 --- a/src/pulse/stream.h +++ b/src/pulse/stream.h @@ -324,6 +324,14 @@ typedef void (*pa_stream_request_cb_t)(pa_stream *p, size_t bytes, void *userdat /** A generic notification callback */ typedef void (*pa_stream_notify_cb_t)(pa_stream *p, void *userdata); +/** A callback for asynchronous meta/policy event messages. Well known + * event names are PA_STREAM_EVENT_REQUEST_CORK and + * PA_STREAM_EVENT_REQUEST_UNCORK. The set of defined events can be + * extended at any time. Also, server modules may introduce additional + * message types so make sure that your callback function ignores messages + * it doesn't know. \since 0.9.15 */ +typedef void (*pa_stream_event_cb_t)(pa_stream *p, const char *name, pa_proplist *pl, void *userdata); + /** Create a new, unconnected stream with the specified name and * sample type. It is recommended to use pa_stream_new_with_proplist() * instead and specify some initial properties. */ @@ -500,6 +508,10 @@ void pa_stream_set_moved_callback(pa_stream *p, pa_stream_notify_cb_t cb, void * * 0.9.8. \since 0.9.8 */ void pa_stream_set_suspended_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata); +/** Set the callback function that is called whenver a meta/policy + * control event is received.\since 0.9.15 */ +void pa_stream_set_event_callback(pa_stream *p, pa_stream_event_cb_t cb, void *userdata); + /** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. */ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata); @@ -518,7 +530,7 @@ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *us * temporarily. Available for playback streams only. */ pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata); -/** Rename the stream. */ +/** Rename the stream.*/ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata); /** Return the current playback/recording time. This is based on the -- cgit