[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