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