]> code.delx.au - pulseaudio/commitdiff
allow sending meta/policy events to clients
authorLennart Poettering <lennart@poettering.net>
Thu, 12 Feb 2009 02:18:05 +0000 (03:18 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 12 Feb 2009 02:18:05 +0000 (03:18 +0100)
17 files changed:
src/map-file
src/pulse/context.c
src/pulse/context.h
src/pulse/def.h
src/pulse/internal.h
src/pulse/stream.c
src/pulse/stream.h
src/pulsecore/client.c
src/pulsecore/client.h
src/pulsecore/core.h
src/pulsecore/native-common.h
src/pulsecore/protocol-native.c
src/pulsecore/sink-input.c
src/pulsecore/sink-input.h
src/pulsecore/source-output.c
src/pulsecore/source-output.h
src/utils/pacat.c

index ca4e7a72672323bb7f218134eb2756ae719b780b..d9496593c7d884ec6a559432b54dd564b203170d 100644 (file)
@@ -87,6 +87,7 @@ pa_context_set_card_profile_by_index;
 pa_context_set_card_profile_by_name;
 pa_context_set_default_sink;
 pa_context_set_default_source;
+pa_context_set_event_callback;
 pa_context_set_name;
 pa_context_set_sink_input_mute;
 pa_context_set_sink_input_volume;
@@ -232,6 +233,7 @@ pa_stream_proplist_update;
 pa_stream_readable_size;
 pa_stream_ref;
 pa_stream_set_buffer_attr;
+pa_stream_set_event_callback;
 pa_stream_set_latency_update_callback;
 pa_stream_set_monitor_stream;
 pa_stream_set_moved_callback;
index d41e62e28a345e7851a71e807aae97bf2b1e2960..81050914987cba44a7cdf08ed745d62b679b9a82 100644 (file)
@@ -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);
+}
index dfb7e4a10bfbbd06bf599c25845b002b314effbe..2ae4c013562a2d6cbe5c0df534686896dda18a26 100644 (file)
@@ -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);
 
index d4fa821afabdaad37490ae31749f6298e252540f..7f3a0c5d6429e2317f70c4b2b80ee631a852d601 100644 (file)
@@ -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
index 9a2d6457926cf307da0ca77e61dac44d50efb69c..e533625d5340f40607c7502a1a8f37cca7a01936 100644 (file)
@@ -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);
 
index fe2514d9c28f1e2ff619d38bb92478e5f0f7d808..b36bf9b10054fb9ca626c98ba6da963ad905b1b3 100644 (file)
@@ -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;
index 1bec1ebfe5330cac5528a15d5b12f30cf97ccae5..3965e9a2e8aa08b187f0d8567b82cd7c893c7db7 100644 (file)
@@ -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
index 6f3e08e25f5408cb51d8f56a1cf0a2cddb300f40..7ca7b97cc2f51bf7051a6993a5723dbd754b543d 100644 (file)
@@ -73,6 +73,7 @@ pa_client *pa_client_new(pa_core *core, pa_client_new_data *data) {
 
     c->userdata = NULL;
     c->kill = NULL;
+    c->send_event = NULL;
 
     pa_assert_se(pa_idxset_put(core->clients, c, &c->index) >= 0);
 
@@ -144,3 +145,31 @@ void pa_client_update_proplist(pa_client *c, pa_update_mode_t mode, pa_proplist
     pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED], c);
     pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
 }
+
+void pa_client_send_event(pa_client *c, const char *event, pa_proplist *data) {
+    pa_proplist *pl = NULL;
+    pa_client_send_event_hook_data hook_data;
+
+    pa_assert(c);
+    pa_assert(event);
+
+    if (!c->send_event)
+        return;
+
+    if (!data)
+        data = pl = pa_proplist_new();
+
+    hook_data.client = c;
+    hook_data.data = data;
+    hook_data.event = event;
+
+    if (pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CLIENT_SEND_EVENT], &hook_data) < 0)
+        goto finish;
+
+    c->send_event(c, event, data);
+
+finish:
+
+    if (pl)
+        pa_proplist_free(pl);
+}
index 8ac80dd96e341de9d17365f05ff6a70f93d07ccc..845a8babebd7d1dc580f97251b47b6b618ca1102 100644 (file)
@@ -48,6 +48,8 @@ struct pa_client {
     void *userdata;
 
     void (*kill)(pa_client *c);
+
+    void (*send_event)(pa_client *c, const char *name, pa_proplist *data);
 };
 
 typedef struct pa_client_new_data {
@@ -73,4 +75,12 @@ void pa_client_set_name(pa_client *c, const char *name);
 
 void pa_client_update_proplist(pa_client *c, pa_update_mode_t mode, pa_proplist *p);
 
+void pa_client_send_event(pa_client *c, const char *event, pa_proplist *data);
+
+typedef struct pa_client_send_event_hook_data {
+    pa_client *client;
+    const char *event;
+    pa_proplist *data;
+} pa_client_send_event_hook_data;
+
 #endif
index 1407b1b873093c86cb1ef0843207d8552bd1b890..aca96bbada9cdc898fa6e74b56893053ba2fdc84 100644 (file)
@@ -73,6 +73,7 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
     PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
     PA_CORE_HOOK_SINK_INPUT_SET_VOLUME,
+    PA_CORE_HOOK_SINK_INPUT_SEND_EVENT,
     PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
     PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
     PA_CORE_HOOK_SOURCE_OUTPUT_PUT,
@@ -82,10 +83,12 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH,
     PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
     PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
+    PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT,
     PA_CORE_HOOK_CLIENT_NEW,
     PA_CORE_HOOK_CLIENT_PUT,
     PA_CORE_HOOK_CLIENT_UNLINK,
     PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED,
+    PA_CORE_HOOK_CLIENT_SEND_EVENT,
     PA_CORE_HOOK_CARD_NEW,
     PA_CORE_HOOK_CARD_PUT,
     PA_CORE_HOOK_CARD_UNLINK,
index b31a5da1e5ef471466c8c1d37f431d3a5f806f43..6951e10a59337b092251529e73e8401d5fa6e09a 100644 (file)
@@ -157,6 +157,10 @@ enum {
     PA_COMMAND_GET_CARD_INFO_LIST,
     PA_COMMAND_SET_CARD_PROFILE,
 
+    PA_COMMAND_CLIENT_EVENT,
+    PA_COMMAND_PLAYBACK_STREAM_EVENT,
+    PA_COMMAND_RECORD_STREAM_EVENT,
+
     PA_COMMAND_MAX
 };
 
index 65b2bb97a6072578c1739ea5c87806f4286db0a0..c30326186623ed40ec81c35d930cc07559cecd99 100644 (file)
@@ -207,6 +207,7 @@ static void sink_input_moved_cb(pa_sink_input *i);
 static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
 static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
 static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes);
+static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl);
 
 static void native_connection_send_memblock(pa_native_connection *c);
 static void playback_stream_request_bytes(struct playback_stream*s);
@@ -216,6 +217,7 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk)
 static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend);
 static void source_output_moved_cb(pa_source_output *o);
 static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
+static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl);
 
 static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
 
@@ -636,6 +638,7 @@ static record_stream* record_stream_new(
     s->source_output->get_latency = source_output_get_latency_cb;
     s->source_output->moved = source_output_moved_cb;
     s->source_output->suspend = source_output_suspend_cb;
+    s->source_output->send_event = source_output_send_event_cb;
     s->source_output->userdata = s;
 
     fix_record_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, fragsize);
@@ -1048,6 +1051,7 @@ static playback_stream* playback_stream_new(
     s->sink_input->kill = sink_input_kill_cb;
     s->sink_input->moved = sink_input_moved_cb;
     s->sink_input->suspend = sink_input_suspend_cb;
+    s->sink_input->send_event = sink_input_send_event_cb;
     s->sink_input->userdata = s;
 
     start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
@@ -1493,6 +1497,27 @@ static void sink_input_kill_cb(pa_sink_input *i) {
     playback_stream_unlink(s);
 }
 
+/* Called from main context */
+static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl) {
+    playback_stream *s;
+    pa_tagstruct *t;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    if (s->connection->version < 15)
+      return;
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_EVENT);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_puts(t, event);
+    pa_tagstruct_put_proplist(t, pl);
+    pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
 /* Called from main context */
 static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {
     playback_stream *s;
@@ -1594,6 +1619,27 @@ static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
     return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec);
 }
 
+/* Called from main context */
+static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl) {
+    record_stream *s;
+    pa_tagstruct *t;
+
+    pa_source_output_assert_ref(o);
+    s = RECORD_STREAM(o->userdata);
+    record_stream_assert_ref(s);
+
+    if (s->connection->version < 15)
+      return;
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_EVENT);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_puts(t, event);
+    pa_tagstruct_put_proplist(t, pl);
+    pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
 /* Called from main context */
 static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {
     record_stream *s;
@@ -4188,6 +4234,25 @@ static void client_kill_cb(pa_client *c) {
     pa_log_info("Connection killed.");
 }
 
+static void client_send_event_cb(pa_client *client, const char*event, pa_proplist *pl) {
+    pa_tagstruct *t;
+    pa_native_connection *c;
+
+    pa_assert(client);
+    c = PA_NATIVE_CONNECTION(client->userdata);
+    pa_native_connection_assert_ref(c);
+
+    if (c->version < 15)
+      return;
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_CLIENT_EVENT);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_puts(t, event);
+    pa_tagstruct_put_proplist(t, pl);
+    pa_pstream_send_tagstruct(c->pstream, t);
+}
+
 /*** module entry points ***/
 
 static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
@@ -4265,6 +4330,7 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati
 
     c->client = client;
     c->client->kill = client_kill_cb;
+    c->client->send_event = client_send_event_cb;
     c->client->userdata = c;
 
     c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool);
index 9c0bf76ad203ad8d78145f16faa4649b383d2bcb..22419ee5b6325e15d19a9eada698cd2b58472b47 100644 (file)
@@ -122,6 +122,7 @@ static void reset_callbacks(pa_sink_input *i) {
     i->get_latency = NULL;
     i->state_change = NULL;
     i->may_move_to = NULL;
+    i->send_event = NULL;
 }
 
 /* Called from main context */
@@ -1446,3 +1447,31 @@ pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) {
 
     return ret;
 }
+
+/* Called from main context */
+void pa_sink_input_send_event(pa_sink_input *i, const char *event, pa_proplist *data) {
+    pa_proplist *pl = NULL;
+    pa_sink_input_send_event_hook_data hook_data;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(event);
+
+    if (!i->send_event)
+        return;
+
+    if (!data)
+        data = pl = pa_proplist_new();
+
+    hook_data.sink_input = i;
+    hook_data.data = data;
+    hook_data.event = event;
+
+    if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT], &hook_data) < 0)
+        goto finish;
+
+    i->send_event(i, event, data);
+
+finish:
+    if (pl)
+        pa_proplist_free(pl);
+}
index 20e2cfa4079119861e5633ec3c701c14bb45ba7c..b4f053197e3df99de7125c1c297b870b1093d272 100644 (file)
@@ -171,6 +171,10 @@ struct pa_sink_input {
      * be allowed */
     pa_bool_t (*may_move_to) (pa_sink_input *i, pa_sink *s); /* may be NULL */
 
+    /* If non-NULL this function is used to dispatch asynchronous
+     * control events. */
+    void (*send_event)(pa_sink_input *i, const char *event, pa_proplist* data);
+
     struct {
         pa_sink_input_state_t state;
         pa_atomic_t drained;
@@ -217,6 +221,12 @@ enum {
     PA_SINK_INPUT_MESSAGE_MAX
 };
 
+typedef struct pa_sink_input_send_event_hook_data {
+    pa_sink_input *sink_input;
+    const char *event;
+    pa_proplist *data;
+} pa_sink_input_send_event_hook_data;
+
 typedef struct pa_sink_input_new_data {
     pa_proplist *proplist;
 
@@ -298,6 +308,8 @@ void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_p
 
 pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
 
+void pa_sink_input_send_event(pa_sink_input *i, const char *name, pa_proplist *data);
+
 int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save);
 pa_bool_t pa_sink_input_may_move(pa_sink_input *i); /* may this sink input move at all? */
 pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest); /* may this sink input move to this sink? */
index f2f2593c713affb03971a44d0204aa0dd4dc92a3..382fb88cc8ad1602583339a98d97ba6cf67a8f02 100644 (file)
@@ -92,6 +92,7 @@ static void reset_callbacks(pa_source_output *o) {
     o->get_latency = NULL;
     o->state_change = NULL;
     o->may_move_to = NULL;
+    o->send_event = NULL;
 }
 
 /* Called from main context */
@@ -867,3 +868,30 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int
 
     return -PA_ERR_NOTIMPLEMENTED;
 }
+
+void pa_source_output_send_event(pa_source_output *o, const char *event, pa_proplist *data) {
+    pa_proplist *pl = NULL;
+    pa_source_output_send_event_hook_data hook_data;
+
+    pa_source_output_assert_ref(o);
+    pa_assert(event);
+
+    if (!o->send_event)
+        return;
+
+    if (!data)
+        data = pl = pa_proplist_new();
+
+    hook_data.source_output = o;
+    hook_data.data = data;
+    hook_data.event = event;
+
+    if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT], &hook_data) < 0)
+        goto finish;
+
+    o->send_event(o, event, data);
+
+finish:
+    if (pl)
+        pa_proplist_free(pl);
+}
index aba2510b1abbae2b6c63929c21aae9a551a746a9..9369568c89d5c95633d2f0f5a8d05b3da85896ab 100644 (file)
@@ -143,6 +143,10 @@ struct pa_source_output {
      * will not be allowed */
     pa_bool_t (*may_move_to) (pa_source_output *o, pa_source *s); /* may be NULL */
 
+    /* If non-NULL this function is used to dispatch asynchronous
+     * control events. */
+    void (*send_event)(pa_source_output *o, const char *event, pa_proplist* data);
+
     struct {
         pa_source_output_state_t state;
 
@@ -177,6 +181,12 @@ enum {
     PA_SOURCE_OUTPUT_MESSAGE_MAX
 };
 
+typedef struct pa_source_output_send_event_hook_data {
+    pa_source_output *source_output;
+    const char *event;
+    pa_proplist *data;
+} pa_source_output_send_event_hook_data;
+
 typedef struct pa_source_output_new_data {
     pa_proplist *proplist;
     pa_sink_input *direct_on_input;
@@ -233,6 +243,8 @@ void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode
 
 pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o);
 
+void pa_source_output_send_event(pa_source_output *o, const char *name, pa_proplist *data);
+
 pa_bool_t pa_source_output_may_move(pa_source_output *o);
 pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest);
 int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t save);
index 2d88e7f97f17b9c7b85d9659c72846d18eeacb96..10015ce44697f4a1e8672e1585069869e2f8a97a 100644 (file)
@@ -238,6 +238,18 @@ static void stream_moved_callback(pa_stream *s, void *userdata) {
         fprintf(stderr, _("Stream moved to device %s (%u, %ssuspended).%s \n"), pa_stream_get_device_name(s), pa_stream_get_device_index(s), pa_stream_is_suspended(s) ? "" : _("not "),  CLEAR_LINE);
 }
 
+static void stream_event_callback(pa_stream *s, const char *name, pa_proplist *pl, void *userdata) {
+    char *t;
+
+    assert(s);
+    assert(name);
+    assert(pl);
+
+    t = pa_proplist_to_string_sep(pl, ", ");
+    fprintf(stderr, "Got event '%s', properties '%s'\n", name, t);
+    pa_xfree(t);
+}
+
 /* This is called whenever the context status changes */
 static void context_state_callback(pa_context *c, void *userdata) {
     assert(c);
@@ -271,6 +283,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
             pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL);
             pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL);
             pa_stream_set_started_callback(stream, stream_started_callback, NULL);
+            pa_stream_set_event_callback(stream, stream_event_callback, NULL);
 
             if (latency > 0) {
                 memset(&buffer_attr, 0, sizeof(buffer_attr));