[pulseaudio-discuss] [PATCH 1/2] subscription: Add signal receiving capability
Georg Chini
georg at chini.tk
Mon Jul 24 12:19:11 UTC 2017
This patch extends the subscription API, so that signals sent from PulseAudio
can be processed. A signal can be emitted using pa_signal_post().
A client can subscribe to signals by specifying PA_SUBSCRIPTION_MASK_SIGNAL as
one of the subscription mask flags in pa_context_subscribe(). It then needs to
install a signal handler in addtion to (or instead of) the subsription
callback. A new function pa_context_set_signal_callback() has been implemented
to set the signal handler.
The signal handler will receive three arguments in addition to the usual context
and userdata:
sender - string that specifies the origin of the signal
signal - string that specifies the type of the signal
signal_info - optional string for additional information
Implementing signal handling as an extension of the subscription API avoids
unnecessary code duplication.
---
src/map-file | 1 +
src/modules/module-default-device-restore.c | 2 +-
src/modules/module-device-manager.c | 2 +-
src/modules/module-device-restore.c | 2 +-
src/modules/module-stream-restore.c | 2 +-
src/pulse/def.h | 14 +++++-
src/pulse/internal.h | 2 +
src/pulse/subscribe.c | 46 +++++++++++++++---
src/pulse/subscribe.h | 6 +++
src/pulsecore/core-subscribe.c | 72 +++++++++++++++++++++++++----
src/pulsecore/core-subscribe.h | 5 +-
src/pulsecore/protocol-native.c | 20 +++++++-
12 files changed, 150 insertions(+), 24 deletions(-)
diff --git a/src/map-file b/src/map-file
index 93a62b86..ad63350c 100644
--- a/src/map-file
+++ b/src/map-file
@@ -111,6 +111,7 @@ pa_context_set_source_volume_by_index;
pa_context_set_source_volume_by_name;
pa_context_set_state_callback;
pa_context_set_subscribe_callback;
+pa_context_set_signal_callback;
pa_context_stat;
pa_context_subscribe;
pa_context_suspend_sink_by_index;
diff --git a/src/modules/module-default-device-restore.c b/src/modules/module-default-device-restore.c
index adf19ff9..b60d75d0 100644
--- a/src/modules/module-default-device-restore.c
+++ b/src/modules/module-default-device-restore.c
@@ -163,7 +163,7 @@ int pa__init(pa_module *m) {
load(u);
- u->subscription = pa_subscription_new(u->core, PA_SUBSCRIPTION_MASK_SERVER, subscribe_cb, u);
+ u->subscription = pa_subscription_new(u->core, PA_SUBSCRIPTION_MASK_SERVER, subscribe_cb, NULL, u);
return 0;
diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 2a0a67f0..22734377 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -1578,7 +1578,7 @@ int pa__init(pa_module*m) {
u->connection_unlink_hook_slot = pa_hook_connect(&pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_CONNECTION_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) connection_unlink_hook_cb, u);
- u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE|PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
+ u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE|PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, NULL, u);
/* Used to handle device description management */
u->sink_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_new_hook_callback, u);
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index 8d7b34b4..0943b83b 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -1236,7 +1236,7 @@ int pa__init(pa_module*m) {
pa_module_hook_connect(m, &pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_CONNECTION_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) connection_unlink_hook_cb, u);
- u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
+ u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, NULL, u);
if (restore_port) {
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_new_hook_callback, u);
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index eb758330..30bcb04f 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -2443,7 +2443,7 @@ int pa__init(pa_module*m) {
pa_module_hook_connect(m, &pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_CONNECTION_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) connection_unlink_hook_cb, u);
- u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
+ u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, NULL, u);
if (restore_device) {
/* A little bit earlier than module-intended-roles ... */
diff --git a/src/pulse/def.h b/src/pulse/def.h
index 680bdc98..e003a894 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -549,8 +549,14 @@ typedef enum pa_subscription_mask {
PA_SUBSCRIPTION_MASK_CARD = 0x0200U,
/**< Card events. \since 0.9.15 */
- PA_SUBSCRIPTION_MASK_ALL = 0x02ffU
+ PA_SUBSCRIPTION_MASK_ALL = 0x02ffU,
/**< Catch all events */
+
+ PA_SUBSCRIPTION_MASK_SIGNAL = 0x0300U,
+ /**< Signal events */
+
+ PA_SUBSCRIPTION_MASK_ALL_PLUS_SIGNALS = 0x03ffU
+ /**< Catch all events including signals*/
} pa_subscription_mask_t;
/** Subscription event types, as used by pa_context_subscribe() */
@@ -599,6 +605,9 @@ typedef enum pa_subscription_event_type {
PA_SUBSCRIPTION_EVENT_REMOVE = 0x0020U,
/**< An object was removed */
+ PA_SUBSCRIPTION_EVENT_SIGNAL = 0x0040U,
+ /** A signal was issued */
+
PA_SUBSCRIPTION_EVENT_TYPE_MASK = 0x0030U
/**< A mask to extract the event operation from an event value */
@@ -620,6 +629,8 @@ typedef enum pa_subscription_event_type {
#define PA_SUBSCRIPTION_MASK_AUTOLOAD PA_SUBSCRIPTION_MASK_AUTOLOAD
#define PA_SUBSCRIPTION_MASK_CARD PA_SUBSCRIPTION_MASK_CARD
#define PA_SUBSCRIPTION_MASK_ALL PA_SUBSCRIPTION_MASK_ALL
+#define PA_SUBSCRIPTION_MASK_SIGNAL PA_SUBSCRIPTION_MASK_SIGNAL
+#define PA_SUBSCRIPTION_MASK_ALL_PLUS_SIGNALS PA_SUBSCRIPTION_MASK_ALL_PLUS_SIGNALS
#define PA_SUBSCRIPTION_EVENT_SINK PA_SUBSCRIPTION_EVENT_SINK
#define PA_SUBSCRIPTION_EVENT_SOURCE PA_SUBSCRIPTION_EVENT_SOURCE
#define PA_SUBSCRIPTION_EVENT_SINK_INPUT PA_SUBSCRIPTION_EVENT_SINK_INPUT
@@ -634,6 +645,7 @@ typedef enum pa_subscription_event_type {
#define PA_SUBSCRIPTION_EVENT_NEW PA_SUBSCRIPTION_EVENT_NEW
#define PA_SUBSCRIPTION_EVENT_CHANGE PA_SUBSCRIPTION_EVENT_CHANGE
#define PA_SUBSCRIPTION_EVENT_REMOVE PA_SUBSCRIPTION_EVENT_REMOVE
+#define PA_SUBSCRIPTION_EVENT_SIGNAL PA_SUBSCRIPTION_EVENT_SIGNAL
#define PA_SUBSCRIPTION_EVENT_TYPE_MASK PA_SUBSCRIPTION_EVENT_TYPE_MASK
/** \endcond */
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index 01d2b6e4..7d024cb1 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -89,6 +89,8 @@ struct pa_context {
void *subscribe_userdata;
pa_context_event_cb_t event_callback;
void *event_userdata;
+ pa_context_signal_cb_t signal_callback;
+ void *signal_userdata;
pa_mempool *mempool;
diff --git a/src/pulse/subscribe.c b/src/pulse/subscribe.c
index e7cce2e3..6b19fd2a 100644
--- a/src/pulse/subscribe.c
+++ b/src/pulse/subscribe.c
@@ -32,7 +32,6 @@
void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_context *c = userdata;
pa_subscription_event_type_t e;
- uint32_t idx;
pa_assert(pd);
pa_assert(command == PA_COMMAND_SUBSCRIBE_EVENT);
@@ -42,15 +41,39 @@ void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag
pa_context_ref(c);
- if (pa_tagstruct_getu32(t, &e) < 0 ||
- pa_tagstruct_getu32(t, &idx) < 0 ||
- !pa_tagstruct_eof(t)) {
+ if (pa_tagstruct_getu32(t, &e) < 0) {
pa_context_fail(c, PA_ERR_PROTOCOL);
goto finish;
}
- if (c->subscribe_callback)
- c->subscribe_callback(c, e, idx, c->subscribe_userdata);
+ if (e != PA_SUBSCRIPTION_EVENT_SIGNAL) {
+ uint32_t idx;
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (c->subscribe_callback)
+ c->subscribe_callback(c, e, idx, c->subscribe_userdata);
+
+ } else {
+ const char *sender;
+ const char *signal;
+ const char *signal_info;
+
+ if (pa_tagstruct_gets(t, &sender) < 0 ||
+ pa_tagstruct_gets(t, &signal) < 0 ||
+ pa_tagstruct_gets(t, &signal_info) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (c->signal_callback)
+ c->signal_callback(c, sender, signal, signal_info, c->signal_userdata);
+ }
finish:
pa_context_unref(c);
@@ -86,3 +109,14 @@ void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t
c->subscribe_callback = cb;
c->subscribe_userdata = userdata;
}
+
+void pa_context_set_signal_callback(pa_context *c, pa_context_signal_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->signal_callback = cb;
+ c->signal_userdata = userdata;
+}
diff --git a/src/pulse/subscribe.h b/src/pulse/subscribe.h
index b43c8ea4..bd5a5115 100644
--- a/src/pulse/subscribe.h
+++ b/src/pulse/subscribe.h
@@ -72,12 +72,18 @@ PA_C_DECL_BEGIN
/** Subscription event callback prototype */
typedef void (*pa_context_subscribe_cb_t)(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata);
+/** Signal event callback prototype */
+typedef void (*pa_context_signal_cb_t)(pa_context *c, const char *sender, const char *signal, const char *signal_info, void *userdata);
+
/** Enable event notification */
pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata);
/** Set the context specific call back function that is called whenever the state of the daemon changes */
void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata);
+/** Set the context specific call back function that is called whenever pulseaudio sends a signal */
+void pa_context_set_signal_callback(pa_context *c, pa_context_signal_cb_t cb, void *userdata);
+
PA_C_DECL_END
#endif
diff --git a/src/pulsecore/core-subscribe.c b/src/pulsecore/core-subscribe.c
index 61c779b6..151c3c7b 100644
--- a/src/pulsecore/core-subscribe.c
+++ b/src/pulsecore/core-subscribe.c
@@ -42,6 +42,7 @@ struct pa_subscription {
bool dead;
pa_subscription_cb_t callback;
+ pa_signal_event_cb_t signal_callback;
void *userdata;
pa_subscription_mask_t mask;
@@ -53,6 +54,9 @@ struct pa_subscription_event {
pa_subscription_event_type_t type;
uint32_t index;
+ char *sender;
+ char *signal;
+ char *signal_info;
PA_LLIST_FIELDS(pa_subscription_event);
};
@@ -60,7 +64,7 @@ struct pa_subscription_event {
static void sched_event(pa_core *c);
/* Allocate a new subscription object for the given subscription mask. Use the specified callback function and user data */
-pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, pa_subscription_cb_t callback, void *userdata) {
+pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, pa_subscription_cb_t callback, pa_signal_event_cb_t signal_callback, void *userdata) {
pa_subscription *s;
pa_assert(c);
@@ -71,6 +75,7 @@ pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, pa_su
s->core = c;
s->dead = false;
s->callback = callback;
+ s->signal_callback = signal_callback;
s->userdata = userdata;
s->mask = m;
@@ -103,6 +108,9 @@ static void free_event(pa_subscription_event *s) {
s->core->subscription_event_last = s->prev;
PA_LLIST_REMOVE(pa_subscription_event, s->core->subscription_event_queue, s);
+ pa_xfree(s->sender);
+ pa_xfree(s->signal);
+ pa_xfree(s->signal_info);
pa_xfree(s);
}
@@ -142,11 +150,19 @@ static void dump_event(const char * prefix, pa_subscription_event*e) {
[PA_SUBSCRIPTION_EVENT_REMOVE] = "REMOVE"
};
- pa_log_debug("%s event (%s|%s|%u)",
- prefix,
- fac_table[e->type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK],
- type_table[e->type & PA_SUBSCRIPTION_EVENT_TYPE_MASK],
- e->index);
+ if (e->type == PA_SUBSCRIPTION_EVENT_SIGNAL) {
+ pa_log_debug("%s signal (%s|%s)",
+ prefix,
+ e->sender,
+ e->signal);
+ if (e->signal_info)
+ pa_log_debug("Signal info: %s", e->signal_info);
+ } else
+ pa_log_debug("%s event (%s|%s|%u)",
+ prefix,
+ fac_table[e->type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK],
+ type_table[e->type & PA_SUBSCRIPTION_EVENT_TYPE_MASK],
+ e->index);
}
#endif
@@ -168,8 +184,15 @@ static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) {
for (s = c->subscriptions; s; s = s->next) {
- if (!s->dead && pa_subscription_match_flags(s->mask, e->type))
- s->callback(c, e->type, e->index, s->userdata);
+ if (!s->dead) {
+ if ((e->type == PA_SUBSCRIPTION_EVENT_SIGNAL) && (s->mask & PA_SUBSCRIPTION_MASK_SIGNAL)) {
+ if (s->signal_callback)
+ s->signal_callback(c, e->sender, e->signal, e->signal_info, s->userdata);
+ } else if (pa_subscription_match_flags(s->mask, e->type)) {
+ if (s->callback)
+ s->callback(c, e->type, e->index, s->userdata);
+ }
+ }
}
#ifdef DEBUG
@@ -206,7 +229,7 @@ void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t i
pa_subscription_event *e;
pa_assert(c);
- /* No need for queuing subscriptions of no one is listening */
+ /* No need for queuing subscriptions if no one is listening */
if (!c->subscriptions)
return;
@@ -245,7 +268,7 @@ void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t i
}
}
- e = pa_xnew(pa_subscription_event, 1);
+ e = pa_xnew0(pa_subscription_event, 1);
e->core = c;
e->type = t;
e->index = idx;
@@ -259,3 +282,32 @@ void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t i
sched_event(c);
}
+
+/* Append a new signal event to the subscription event queue and schedule a main loop event */
+void pa_signal_post(pa_core *c, const char *sender, const char *signal, const char *signal_info) {
+ pa_subscription_event *e;
+ pa_assert(c);
+ pa_assert(sender);
+ pa_assert(signal);
+
+ /* No need for queuing signals if no one is listening */
+ if (!c->subscriptions)
+ return;
+
+ e = pa_xnew0(pa_subscription_event, 1);
+ e->core = c;
+ e->type = PA_SUBSCRIPTION_EVENT_SIGNAL;
+ e->index = PA_INVALID_INDEX;
+ e->sender = pa_xstrdup(sender);
+ e->signal = pa_xstrdup(signal);
+ e->signal_info = pa_xstrdup(signal_info);
+
+ PA_LLIST_INSERT_AFTER(pa_subscription_event, c->subscription_event_queue, c->subscription_event_last, e);
+ c->subscription_event_last = e;
+
+#ifdef DEBUG
+ dump_event("Queued", e);
+#endif
+
+ sched_event(c);
+}
diff --git a/src/pulsecore/core-subscribe.h b/src/pulsecore/core-subscribe.h
index 6032dc35..1194b0f9 100644
--- a/src/pulsecore/core-subscribe.h
+++ b/src/pulsecore/core-subscribe.h
@@ -27,11 +27,14 @@ typedef struct pa_subscription_event pa_subscription_event;
#include <pulsecore/native-common.h>
typedef void (*pa_subscription_cb_t)(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata);
+typedef void (*pa_signal_event_cb_t)(pa_core *c, const char *sender, const char *signal, const char *signal_info, void *userdata);
-pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, pa_subscription_cb_t cb, void *userdata);
+pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, pa_subscription_cb_t cb, pa_signal_event_cb_t signal_callback, void *userdata);
void pa_subscription_free(pa_subscription*s);
void pa_subscription_free_all(pa_core *c);
void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t idx);
+void pa_signal_post(pa_core *c, const char *sender, const char *signal, const char *signal_info);
+
#endif
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 866e2c64..b0e9b2b4 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -3693,6 +3693,22 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t e, uint3
pa_pstream_send_tagstruct(c->pstream, t);
}
+static void signal_cb(pa_core *core, const char *sender, const char *signal, const char *signal_info, void *userdata) {
+ pa_tagstruct *t;
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+
+ pa_native_connection_assert_ref(c);
+
+ t = pa_tagstruct_new();
+ pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE_EVENT);
+ pa_tagstruct_putu32(t, (uint32_t) -1);
+ pa_tagstruct_putu32(t, PA_SUBSCRIPTION_EVENT_SIGNAL);
+ pa_tagstruct_puts(t, sender);
+ pa_tagstruct_puts(t, signal);
+ pa_tagstruct_puts(t, signal_info);
+ pa_pstream_send_tagstruct(c->pstream, t);
+}
+
static void command_subscribe(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_subscription_mask_t m;
@@ -3707,13 +3723,13 @@ static void command_subscribe(pa_pdispatch *pd, uint32_t command, uint32_t tag,
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, (m & ~PA_SUBSCRIPTION_MASK_ALL) == 0, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, (m & ~PA_SUBSCRIPTION_MASK_ALL_PLUS_SIGNALS) == 0, tag, PA_ERR_INVALID);
if (c->subscription)
pa_subscription_free(c->subscription);
if (m != 0) {
- c->subscription = pa_subscription_new(c->protocol->core, m, subscription_cb, c);
+ c->subscription = pa_subscription_new(c->protocol->core, m, subscription_cb, signal_cb, c);
pa_assert(c->subscription);
} else
c->subscription = NULL;
--
2.11.0
More information about the pulseaudio-discuss
mailing list