context, extension: Add the pa_extension class 65/21965/1
authorTanu Kaskinen <tanu.kaskinen@linux.intel.com>
Tue, 4 Mar 2014 13:03:05 +0000 (15:03 +0200)
committerIsmo Puustinen <ismo.puustinen@intel.com>
Wed, 28 May 2014 09:40:04 +0000 (12:40 +0300)
pa_extension is an abstraction layer that allows pa_context to manage
the extensions without needing any extension-specific code. This patch
only implements the pa_extension base class, the class isn't used yet
by any actual extensions.

Change-Id: I457b3d0b674b4cfd1d38452d8f8cb51cf6b7b533
Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
src/Makefile.am
src/pulse/context.c
src/pulse/extension.c [new file with mode: 0644]
src/pulse/extension.h [new file with mode: 0644]
src/pulse/internal.h

index fe6cc53..22b9b81 100644 (file)
@@ -813,6 +813,7 @@ libpulse_la_SOURCES = \
                pulse/def.h \
                pulse/direction.c pulse/direction.h \
                pulse/error.c pulse/error.h \
+               pulse/extension.c pulse/extension.h \
                pulse/ext-device-manager.c pulse/ext-device-manager.h \
                pulse/ext-device-restore.c pulse/ext-device-restore.h \
                pulse/ext-stream-restore.c pulse/ext-stream-restore.h \
index b8688f2..9c9c3d9 100644 (file)
@@ -186,14 +186,20 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *
         }
     }
 
+    c->extensions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
     return c;
 }
 
 static void context_unlink(pa_context *c) {
+    pa_extension *extension;
     pa_stream *s;
 
     pa_assert(c);
 
+    while ((extension = pa_hashmap_first(c->extensions)))
+        pa_extension_kill(extension);
+
     s = c->streams ? pa_stream_ref(c->streams) : NULL;
     while (s) {
         pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
@@ -280,6 +286,9 @@ void pa_context_unref(pa_context *c) {
 }
 
 void pa_context_set_state(pa_context *c, pa_context_state_t st) {
+    pa_extension *extension;
+    void *state;
+
     pa_assert(c);
     pa_assert(PA_REFCNT_VALUE(c) >= 1);
 
@@ -290,6 +299,12 @@ void pa_context_set_state(pa_context *c, pa_context_state_t st) {
 
     c->state = st;
 
+    PA_HASHMAP_FOREACH(extension, c->extensions, state)
+        pa_extension_context_state_changed(extension, 1);
+
+    PA_HASHMAP_FOREACH(extension, c->extensions, state)
+        pa_extension_context_state_changed(extension, 2);
+
     if (c->state_callback)
         c->state_callback(c, c->state_userdata);
 
@@ -1338,6 +1353,7 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t
     pa_context *c = userdata;
     uint32_t idx;
     const char *name;
+    pa_extension *extension;
 
     pa_assert(pd);
     pa_assert(command == PA_COMMAND_EXTENSION);
@@ -1366,7 +1382,16 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t
         pa_ext_stream_restore_command(c, tag, t);
     else if (pa_streq(name, "module-node-manager"))
         pa_ext_node_manager_command(c, tag, t);
-    else
+    else if ((extension = pa_context_get_extension(c, name))) {
+        uint32_t subcommand;
+
+        if (pa_tagstruct_getu32(t, &subcommand) < 0) {
+            pa_context_fail(c, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+
+        pa_extension_process_command(extension, subcommand, tag, t);
+    } else
         pa_log(_("Received message for unknown extension '%s'"), name);
 
 finish:
@@ -1464,3 +1489,24 @@ int pa_context_load_cookie_from_file(pa_context *c, const char *cookie_file_path
 
     return pa_client_conf_load_cookie_from_file(c->conf, cookie_file_path);
 }
+
+pa_extension *pa_context_get_extension(pa_context *context, const char *name) {
+    pa_assert(context);
+    pa_assert(name);
+
+    return pa_hashmap_get(context->extensions, name);
+}
+
+void pa_context_add_extension(pa_context *context, pa_extension *extension) {
+    pa_assert(context);
+    pa_assert(extension);
+
+    pa_assert_se(pa_hashmap_put(context->extensions, extension->name, extension) >= 0);
+}
+
+int pa_context_remove_extension(pa_context *context, pa_extension *extension) {
+    pa_assert(context);
+    pa_assert(extension);
+
+    return pa_hashmap_remove(context->extensions, extension->name) ? 0 : -1;
+}
diff --git a/src/pulse/extension.c b/src/pulse/extension.c
new file mode 100644 (file)
index 0000000..17e7e6c
--- /dev/null
@@ -0,0 +1,100 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2014 Intel Corporation
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "extension.h"
+
+#include <pulsecore/macro.h>
+
+#include <pulse/internal.h>
+#include <pulse/xmalloc.h>
+
+pa_extension *pa_extension_new(pa_context *context, const char *name) {
+    pa_extension *extension = NULL;
+
+    pa_assert(context);
+    pa_assert(name);
+
+    extension = pa_xnew0(pa_extension, 1);
+    extension->context = context;
+    extension->name = pa_xstrdup(name);
+
+    return extension;
+}
+
+void pa_extension_put(pa_extension *extension) {
+    pa_assert(extension);
+    pa_assert(extension->kill);
+
+    pa_context_add_extension(extension->context, extension);
+}
+
+static void extension_unlink(pa_extension *extension) {
+    pa_assert(extension);
+
+    if (extension->unlinked)
+        return;
+
+    extension->unlinked = true;
+
+    pa_context_remove_extension(extension->context, extension);
+}
+
+void pa_extension_free(pa_extension *extension) {
+    pa_assert(extension);
+
+    extension_unlink(extension);
+
+    pa_xfree(extension->name);
+    pa_xfree(extension);
+}
+
+void pa_extension_context_state_changed(pa_extension *extension, unsigned phase) {
+    pa_assert(extension);
+    pa_assert(phase == 1 || phase == 2);
+
+    if (extension->context_state_changed)
+        extension->context_state_changed(extension, phase);
+}
+
+void pa_extension_kill(pa_extension *extension) {
+    pa_assert(extension);
+
+    if (extension->unlinked)
+        return;
+
+    extension->kill(extension);
+}
+
+void pa_extension_process_command(pa_extension *extension, uint32_t command, uint32_t tag, pa_tagstruct *tagstruct) {
+    pa_assert(extension);
+    pa_assert(tagstruct);
+
+    if (extension->process_command)
+        extension->process_command(extension, command, tag, tagstruct);
+    else {
+        pa_log("Unexpected command for extension %s: %u", extension->name, command);
+        pa_context_fail(extension->context, PA_ERR_PROTOCOL);
+    }
+}
diff --git a/src/pulse/extension.h b/src/pulse/extension.h
new file mode 100644 (file)
index 0000000..cadc267
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef fooextensionhfoo
+#define fooextensionhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2014 Intel Corporation
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/context.h>
+
+#include <pulsecore/tagstruct.h>
+
+#include <stdbool.h>
+
+typedef struct pa_extension pa_extension;
+
+struct pa_extension {
+    pa_context *context;
+    char *name;
+    bool unlinked;
+
+    /* This is called when the context state changes. The callback is called
+     * twice for each state change, first with phase = 1 and then with
+     * phase = 2. In the first phase the extension should update its internal
+     * state without calling any application callbacks. In the second phase it
+     * should call the application callbacks (if any). May be NULL. */
+    void (*context_state_changed)(pa_extension *extension, unsigned phase);
+
+    /* Called from pa_extension_kill(). May not be NULL. */
+    void (*kill)(pa_extension *extension);
+
+    /* Called from pa_extension_process_command(). May be NULL, if the
+     * extension doesn't expect any commands from the server. */
+    void (*process_command)(pa_extension *extension, uint32_t command, uint32_t tag, pa_tagstruct *tagstruct);
+
+    void *userdata;
+};
+
+pa_extension *pa_extension_new(pa_context *context, const char *name);
+void pa_extension_put(pa_extension *extension);
+void pa_extension_free(pa_extension *extension);
+
+void pa_extension_context_state_changed(pa_extension *extension, unsigned phase);
+void pa_extension_kill(pa_extension *extension);
+void pa_extension_process_command(pa_extension *extension, uint32_t command, uint32_t tag, pa_tagstruct *tagstruct);
+
+#endif
index 61095d0..1428fb8 100644 (file)
@@ -23,6 +23,7 @@
   USA.
 ***/
 
+#include <pulse/extension.h>
 #include <pulse/mainloop-api.h>
 #include <pulse/context.h>
 #include <pulse/stream.h>
@@ -103,6 +104,8 @@ struct pa_context {
 
     uint32_t client_index;
 
+    pa_hashmap *extensions; /* extension name -> pa_extension */
+
     /* Extension specific data */
     struct {
         pa_ext_device_manager_subscribe_cb_t callback;
@@ -269,6 +272,9 @@ int pa_context_set_error(pa_context *c, int error);
 void pa_context_set_state(pa_context *c, pa_context_state_t st);
 int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, bool fail);
 pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata);
+pa_extension *pa_context_get_extension(pa_context *context, const char *name);
+void pa_context_add_extension(pa_context *context, pa_extension *extension);
+int pa_context_remove_extension(pa_context *context, pa_extension *extension);
 
 void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);