From 729f6f0b579be41a650583ac72607f3420db1d34 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 4 Mar 2014 15:03:05 +0200 Subject: [PATCH] context, extension: Add the pa_extension class 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 --- src/Makefile.am | 1 + src/pulse/context.c | 48 +++++++++++++++++++++++- src/pulse/extension.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/pulse/extension.h | 63 +++++++++++++++++++++++++++++++ src/pulse/internal.h | 6 +++ 5 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 src/pulse/extension.c create mode 100644 src/pulse/extension.h diff --git a/src/Makefile.am b/src/Makefile.am index fe6cc53..22b9b81 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/pulse/context.c b/src/pulse/context.c index b8688f2..9c9c3d9 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -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 index 0000000..17e7e6c --- /dev/null +++ b/src/pulse/extension.c @@ -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 +#endif + +#include "extension.h" + +#include + +#include +#include + +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 index 0000000..cadc267 --- /dev/null +++ b/src/pulse/extension.h @@ -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 + +#include + +#include + +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 diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 61095d0..1428fb8 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -23,6 +23,7 @@ USA. ***/ +#include #include #include #include @@ -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); -- 2.7.4