From cc1d8213d6a58b0022017f8d231a346387aab507 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jul 2006 17:36:14 +0000 Subject: add new module "module-gconf" which reads configuration information from gconf. this will be used in my upcoming paconf module git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1150 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/Makefile.am | 24 ++- src/modules/gconf/Makefile | 13 ++ src/modules/gconf/gconf-helper.c | 123 +++++++++++++ src/modules/gconf/module-gconf.c | 386 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 544 insertions(+), 2 deletions(-) create mode 100644 src/modules/gconf/Makefile create mode 100644 src/modules/gconf/gconf-helper.c create mode 100644 src/modules/gconf/module-gconf.c diff --git a/src/Makefile.am b/src/Makefile.am index e0add703..bb08ed78 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -25,6 +25,7 @@ pulseincludedir=$(includedir)/pulse pulsecoreincludedir=$(includedir)/pulsecore pulseconfdir=$(sysconfdir)/pulse +pulselibexecdir=$(libexecdir)/pulse ################################### # Defines # @@ -883,6 +884,14 @@ modlibexec_LTLIBRARIES += \ module-jack-source.la endif +if HAVE_GCONF +modlibexec_LTLIBRARIES += \ + module-gconf.la + +pulselibexec_PROGRAMS = \ + gconf-helper +endif + if OS_IS_WIN32 modlibexec_LTLIBRARIES += \ module-waveout.la @@ -930,8 +939,8 @@ SYMDEF_FILES = \ modules/rtp/module-rtp-recv-symdef.h \ modules/module-jack-sink-symdef.h \ modules/module-jack-source-symdef.h \ - modules/module-volume-restore-symdef.h - + modules/module-volume-restore-symdef.h \ + modules/gconf/module-gconf-symdef.h EXTRA_DIST += $(SYMDEF_FILES) BUILT_SOURCES += $(SYMDEF_FILES) @@ -1171,6 +1180,17 @@ module_jack_source_la_LDFLAGS = -module -avoid-version module_jack_source_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(JACK_LIBS) module_jack_source_la_CFLAGS = $(AM_LIBADD) $(JACK_CFLAGS) +# GConf support +module_gconf_la_SOURCES = modules/gconf/module-gconf.c +module_gconf_la_LDFLAGS = -module -avoid-version +module_gconf_la_LIBADD = $(AM_LIBADD) libpulsecore.la +module_gconf_la_CFLAGS = $(AM_CFLAGS) -DPA_GCONF_HELPER=\"$(pulselibexecdir)/gconf-helper\" + +gconf_helper_SOURCES = modules/gconf/gconf-helper.c +gconf_helper_LDADD = $(AM_LDADD) $(GCONF_LIBS) +gconf_helper_CFLAGS = $(AM_CFLAGS) $(GCONF_CFLAGS) +gconf_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + ################################### # Some minor stuff # ################################### diff --git a/src/modules/gconf/Makefile b/src/modules/gconf/Makefile new file mode 100644 index 00000000..316beb72 --- /dev/null +++ b/src/modules/gconf/Makefile @@ -0,0 +1,13 @@ +# This is a dirty trick just to ease compilation with emacs +# +# This file is not intended to be distributed or anything +# +# So: don't touch it, even better ignore it! + +all: + $(MAKE) -C ../.. + +clean: + $(MAKE) -C ../.. clean + +.PHONY: all clean diff --git a/src/modules/gconf/gconf-helper.c b/src/modules/gconf/gconf-helper.c new file mode 100644 index 00000000..c8b9b144 --- /dev/null +++ b/src/modules/gconf/gconf-helper.c @@ -0,0 +1,123 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#include +#include + +#include +#include + +#define PA_GCONF_ROOT "/system/pulseaudio" +#define PA_GCONF_PATH_MODULES PA_GCONF_ROOT"/modules" + +static void handle_module(GConfClient *client, const char *name) { + gchar p[1024]; + gboolean enabled; + int i; + + snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/enabled", name); + enabled = gconf_client_get_bool(client, p, FALSE); + + printf("%c%s%c", enabled ? '+' : '-', name, 0); + + if (enabled) { + + for (i = 0; i < 10; i++) { + gchar *n, *a; + + snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/name%i", name, i); + if (!(n = gconf_client_get_string(client, p, NULL)) || !*n) + break; + + snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/args%i", name, i); + a = gconf_client_get_string(client, p, NULL); + + printf("%s%c%s%c", n, 0, a ? a : "", 0); + + g_free(n); + g_free(a); + } + + printf("%c", 0); + } + + fflush(stdout); +} + +static void modules_callback( + GConfClient* client, + guint cnxn_id, + GConfEntry *entry, + gpointer user_data) { + + const char *n; + char buf[128]; + + g_assert(strncmp(entry->key, PA_GCONF_PATH_MODULES"/", sizeof(PA_GCONF_PATH_MODULES)) == 0); + + n = entry->key + sizeof(PA_GCONF_PATH_MODULES); + + g_strlcpy(buf, n, sizeof(buf)); + buf[strcspn(buf, "/")] = 0; + + handle_module(client, buf); +} + +int main(int argc, char *argv[]) { + GMainLoop *g; + GConfClient *client; + GSList *modules, *m; + + if (!(client = gconf_client_get_default())) + goto fail; + + gconf_client_add_dir(client, PA_GCONF_ROOT, GCONF_CLIENT_PRELOAD_RECURSIVE, NULL); + gconf_client_notify_add(client, PA_GCONF_PATH_MODULES, modules_callback, NULL, NULL, NULL); + + modules = gconf_client_all_dirs(client, PA_GCONF_PATH_MODULES, NULL); + + for (m = modules; m; m = m->next) { + char *e = strrchr(m->data, '/'); + handle_module(client, e ? e+1 : m->data); + } + + g_slist_free(modules); + + /* Signal the parent that we are now initialized */ + printf("!"); + fflush(stdout); + + g = g_main_loop_new(NULL, FALSE); + g_main_loop_run(g); + g_main_loop_unref(g); + + g_object_unref(G_OBJECT(client)); + + return 0; + +fail: + return 1; +} diff --git a/src/modules/gconf/module-gconf.c b/src/modules/gconf/module-gconf.c new file mode 100644 index 00000000..30e6292e --- /dev/null +++ b/src/modules/gconf/module-gconf.c @@ -0,0 +1,386 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "module-gconf-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("GConf Adapter") +PA_MODULE_VERSION(PACKAGE_VERSION) +PA_MODULE_USAGE("") + +#define MAX_MODULES 10 +#define BUF_MAX 2048 + +#undef PA_GCONF_HELPER +#define PA_GCONF_HELPER "/home/lennart/projects/pulseaudio/src/gconf-helper" + +struct module_info { + char *name; + + unsigned n_indexes; + uint32_t indexes[MAX_MODULES]; +}; + +struct userdata { + pa_core *core; + pa_module *module; + + pa_hashmap *module_infos; + + pid_t pid; + + int fd; + int fd_type; + pa_io_event *io_event; + + char buf[BUF_MAX]; + size_t buf_fill; +}; + +static int fill_buf(struct userdata *u) { + ssize_t r; + assert(u); + + if (u->buf_fill >= BUF_MAX) { + pa_log(__FILE__": read buffer overflow"); + return -1; + } + + if ((r = pa_read(u->fd, u->buf + u->buf_fill, BUF_MAX - u->buf_fill, &u->fd_type)) <= 0) + return -1; + + u->buf_fill += r; + return 0; +} + +static int read_byte(struct userdata *u) { + int ret; + assert(u); + + if (u->buf_fill < 1) + if (fill_buf(u) < 0) + return -1; + + ret = u->buf[0]; + assert(u->buf_fill > 0); + u->buf_fill--; + memmove(u->buf, u->buf+1, u->buf_fill); + return ret; +} + +static char *read_string(struct userdata *u) { + assert(u); + + for (;;) { + char *e; + + if ((e = memchr(u->buf, 0, u->buf_fill))) { + char *ret = pa_xstrdup(u->buf); + u->buf_fill -= e - u->buf +1; + memmove(u->buf, e+1, u->buf_fill); + return ret; + } + + if (fill_buf(u) < 0) + return NULL; + } +} + +static void unload_modules(struct userdata *u, struct module_info*m) { + unsigned i; + + assert(u); + assert(m); + + for (i = 0; i < m->n_indexes; i++) { + pa_log_debug(__FILE__": Unloading module #%i", m->indexes[i]); + pa_module_unload_by_index(u->core, m->indexes[i]); + } + + m->n_indexes = 0; +} + +static void load_module( + struct userdata *u, + struct module_info *m, + const char *module, + const char *args) { + + pa_module *mod; + + assert(u); + assert(m); + assert(module); + + assert(m->n_indexes < MAX_MODULES); + + pa_log_debug(__FILE__": Loading module '%s' with args '%s' due to GConf configuration.", module, args); + + if (!(mod = pa_module_load(u->core, module, args))) { + pa_log(__FILE__": pa_module_load() failed"); + return; + } + + m->indexes[m->n_indexes++] = mod->index; +} + +static void module_info_free(void *p, void *userdata) { + struct module_info *m = p; + struct userdata *u = userdata; + + assert(m); + assert(u); + + unload_modules(u, m); + pa_xfree(m->name); + pa_xfree(m); +} + +static int handle_event(struct userdata *u) { + int opcode; + int ret = 0; + + do { + if ((opcode = read_byte(u)) < 0) + goto fail; + + switch (opcode) { + case '!': + /* The helper tool is now initialized */ + ret = 1; + break; + + case '+': { + char *name; + struct module_info *m; + + if (!(name = read_string(u))) + goto fail; + + if ((m = pa_hashmap_get(u->module_infos, name))) { + unload_modules(u, m); + } else { + m = pa_xnew(struct module_info, 1); + m->name = pa_xstrdup(name); + m->n_indexes = 0; + pa_hashmap_put(u->module_infos, m->name, m); + } + + while (m->n_indexes < MAX_MODULES) { + char *module, *args; + + if (!(module = read_string(u))) { + pa_xfree(name); + goto fail; + } + + if (!*module) { + pa_xfree(module); + break; + } + + if (!(args = read_string(u))) { + pa_xfree(name); + pa_xfree(module); + goto fail; + } + + load_module(u, m, module, args); + + pa_xfree(module); + pa_xfree(args); + } + + pa_xfree(name); + + break; + } + + case '-': { + char *name; + struct module_info *m; + + if (!(name = read_string(u))) + goto fail; + + if ((m = pa_hashmap_get(u->module_infos, name))) { + pa_hashmap_remove(u->module_infos, name); + module_info_free(m, u); + } + + pa_xfree(name); + + break; + } + } + } while (u->buf_fill > 0 && ret == 0); + + return ret; + +fail: + pa_log(__FILE__": Unable to read or parse data from client."); + return -1; +} + +static void io_event_cb( + pa_mainloop_api*a, + pa_io_event* e, + int fd, + pa_io_event_flags_t events, + void *userdata) { + + struct userdata *u = userdata; + + handle_event(u); +} + +static int start_client(const char *n, pid_t *pid) { + pid_t child; + int pipe_fds[2] = { -1, -1 }; + + if (pipe(pipe_fds) < 0) { + pa_log(__FILE__": pipe() failed: %s", pa_cstrerror(errno)); + goto fail; + } + + if ((child = fork()) == (pid_t) -1) { + pa_log(__FILE__": fork() failed: %s", pa_cstrerror(errno)); + goto fail; + } else if (child != 0) { + + /* Parent */ + close(pipe_fds[1]); + + if (pid) + *pid = child; + + return pipe_fds[0]; + } else { + + /* child */ + + close(pipe_fds[0]); + dup2(pipe_fds[1], 1); + + if (pipe_fds[1] != 1) + close(pipe_fds[1]); + + execl(n, n, NULL); + _exit(1); + } + +fail: + if (pipe_fds[0] >= 0) + close(pipe_fds[0]); + + if (pipe_fds[1] >= 0) + close(pipe_fds[1]); + + return -1; +} + +int pa__init(pa_core *c, pa_module*m) { + struct userdata *u; + int r; + + u = pa_xnew(struct userdata, 1); + u->core = c; + u->module = m; + u->module_infos = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + u->pid = (pid_t) -1; + u->fd = -1; + u->fd_type = 0; + u->io_event = NULL; + u->buf_fill = 0; + + if ((u->fd = start_client(PA_GCONF_HELPER, &u->pid)) < 0) + goto fail; + + u->io_event = c->mainloop->io_new( + c->mainloop, + u->fd, + PA_IO_EVENT_INPUT, + io_event_cb, + u); + + do { + if ((r = handle_event(u)) < 0) + goto fail; + + /* Read until the client signalled us that it is ready with + * initialization */ + } while (r != 1); + + return 0; + +fail: + pa__done(c, m); + return -1; +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata *u; + + assert(c); + assert(m); + + if (!(u = m->userdata)) + return; + + if (u->io_event) + c->mainloop->io_free(u->io_event); + + if (u->fd >= 0) + close(u->fd); + + if (u->pid != (pid_t) -1) { + kill(u->pid, SIGTERM); + waitpid(u->pid, NULL, 0); + } + + if (u->module_infos) + pa_hashmap_free(u->module_infos, module_info_free, u); + + pa_xfree(u); +} + -- cgit