diff --git a/TODO.dbus b/TODO.dbus new file mode 100644 --- /dev/null +++ b/TODO.dbus @@ -0,0 +1,5 @@ + * Provide access to clock source parameter (tricky) + * Provide access to debug-timer parameter (not fully documented - optarg) + * Send signals to (control) apps (status changes, connections, clients, port renames, xruns, error and info logs) + * In client library, when compiled with dbus support, try to start via dbus frontend first (auto-activation) + * Implement configurator supporting multiple user configurations (separate D-Bus object) diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -547,6 +547,39 @@ fi # PKG_CHECK_MODULES inside a --disable-whatever check, you need to # do it somewhere *below* this comment. +AC_ARG_ENABLE(dbus, [ --disable-dbus don't build D-Bus integration ], + TRY_DBUS=$enableval , TRY_DBUS=yes ) +HAVE_DBUS=false +if test "x$TRY_DBUS" = "xyes" +then + PKG_CHECK_MODULES(DBUSGLIB, dbus-glib-1, [AC_DEFINE([HAVE_DBUS], 1, [Defined if D-Bus support needs to be built.]) HAVE_DBUS=true], [true]) +fi + +AM_CONDITIONAL(HAVE_DBUS, $HAVE_DBUS) + +AC_ARG_ENABLE(pkg-config-dbus-service-dir, + [ --enable-pkg-config-dbus-service-dir force D-Bus service install dir to be one returned by pkg-config ], + DBUS_SERVICES_DIR_FORCE_REAL=$enableval , DBUS_SERVICES_DIR_FORCE_REAL=no ) + +if test "x$HAVE_DBUS" = "xtrue" +then + DBUS_SERVICES_DIR_REAL=`$PKG_CONFIG --variable=session_bus_services_dir dbus-1` + if test "x$DBUS_SERVICES_DIR_FORCE_REAL" = "xyes" + then + AC_MSG_WARN([overriding D-Bus service install dir]) + DBUS_SERVICES_DIR="$DBUS_SERVICES_DIR_REAL" + else + AS_AC_EXPAND(DATADIR, $datadir) + DBUS_SERVICES_DIR="$DATADIR/dbus-1/services" + fi + AC_SUBST(DBUS_SERVICES_DIR) + AC_DEFINE_UNQUOTED(DBUS_SERVICES_DIR, "$DBUS_SERVICES_DIR", [Where services dir for DBUS is]) +fi + +HAVE_LIBXML2=false +PKG_CHECK_MODULES(LIBXML2, libxml-2.0, [AC_DEFINE([HAVE_LIBXML2], 1, [Defined if libxml2 is present.]) HAVE_LIBXML2=true], [true]) +AM_CONDITIONAL(HAVE_LIBXML2, $HAVE_LIBXML2) + # Check which backend drivers can be built. The last one successfully # configured becomes the default JACK driver; so the order of # precedence is: alsa, oss, coreaudio, portaudio, dummy. @@ -749,6 +782,20 @@ echo \| echo \| echo \| Default driver backend................................ : $JACK_DEFAULT_DRIVER echo \| Shared memory interface............................... : $JACK_SHM_TYPE +echo \| D-Bus integration .................................... : $HAVE_DBUS +if test "x$HAVE_DBUS" = "xtrue" +then +echo \| D-Bus service install dir............................. : $DBUS_SERVICES_DIR +echo \| Settings persistence.................................. : $HAVE_LIBXML2 +fi echo \| Install prefix........................................ : $prefix echo +if test "x$HAVE_DBUS" = "xtrue" -a "x$DBUS_SERVICES_DIR_REAL" != "x$DBUS_SERVICES_DIR" +then + AC_MSG_WARN([D-Bus session services directory as reported by pkg-config is $DBUS_SERVICES_DIR_REAL]) + AC_MSG_WARN([but service file will be installed in $DBUS_SERVICES_DIR]) + AC_MSG_WARN([You may need to adjust your D-Bus configuration after installing jackdbus]) + AC_MSG_WARN([You can override dbus service install dir]) + AC_MSG_WARN([with --enable-pkg-config-dbus-service-dir option to this script]) +fi diff --git a/example-clients/Makefile.am b/example-clients/Makefile.am --- a/example-clients/Makefile.am +++ b/example-clients/Makefile.am @@ -42,6 +42,10 @@ bin_PROGRAMS = jack_load \ $(JACK_TRANSPORT) \ jack_midisine \ jack_midiseq + +if HAVE_DBUS +bin_SCRIPTS = jack_control +endif noinst_PROGRAMS = jack_thread_wait diff --git a/example-clients/jack_control b/example-clients/jack_control new file mode 100755 --- /dev/null +++ b/example-clients/jack_control @@ -0,0 +1,250 @@ +#!/usr/bin/env python + +name_base = 'org.jackaudio' +controller_interface_name = name_base + '.JackController' +service_name = name_base + '.service' + +import sys +import os +from traceback import print_exc + +import dbus + +def bool_convert(str_value): + if str_value.lower() == "false": + return False + + if str_value.lower() == "off": + return False + + if str_value.lower() == "no": + return False + + if str_value == "0": + return False + + if str_value.lower() == "(null)": + return False + + return bool(str_value) + +def main(): + if len(sys.argv) == 1: + print "Usage: %s [command] [command] ..." % os.path.basename(sys.argv[0]) + print "Commands:" + print " exit - exit jack dbus service (stops jack server if currently running)" +# print " isr - check whether jack server is running, return value is 0 if runing and 1 otherwise" +# print " status - get string describing jack server status" + print " start - start jack server if not currently started" + print " stop - stop jack server if currenly started" + print " dl - get list of available drivers" + print " dg - get currently selected driver" + print " ds - select driver" + print " dp - get parameters of currently selected driver" + print " dpd - get long description for driver parameter" + print " dps - set driver parameter" + print " ep - get engine parameters" + print " epd - get long description for engine parameter" + print " eps - set engine parameter" + sys.exit(0) + + bus = dbus.SessionBus() + + controller = bus.get_object(service_name, "/DefaultController") + iface = dbus.Interface(controller, controller_interface_name) + + # check arguments + index = 1 + while index < len(sys.argv): + arg = sys.argv[index] + index += 1 + try: + if arg == "exit": + print "--- exit" + iface.Exit() + elif arg == "isr": + print "--- isr" + if iface.IsRunning(): + sys.exit(0) + else: + sys.exit(1) + elif arg == 'status': + print "--- status" + print iface.GetStatus() + elif arg == 'start': + print "--- start" + iface.StartServer() + elif arg == 'stop': + print "--- stop" + iface.StopServer() + elif arg == 'test': + print "--- test" + iface.Test() + elif arg == 'ism': + if iface.IsManuallyActivated(): + print "Manually activated" + else: + print "Automatically activated" + elif arg == 'dl': + print "--- drivers list" + drivers = iface.GetAvailableDrivers() + for driver in drivers: + print driver + elif arg == 'dg': + print "--- get selected driver" + driver = iface.GetSelectedDriver() + if not driver: + print "no driver selected" + else: + print driver + elif arg == 'ds': + if index >= len(sys.argv): + print "driver select command requires driver name argument" + sys.exit() + + arg = sys.argv[index] + index += 1 + + print "--- driver select \"%s\"" % arg + iface.SelectDriver(arg) + elif arg == 'dp': + print "--- get driver parameters (type:isset:default:value)" + params = iface.GetDriverParameterNames() + for param in params: + descr = iface.GetDriverParameterShortDescription(param) + typestr = iface.GetDriverParameterTypeString(param) + + if typestr == "str": + isset, default, value = iface.GetDriverParameterValueString(param) + elif typestr == "bool": + isset, default, value = iface.GetDriverParameterValueBool(param) + value = bool(value) + default = bool(default) + elif typestr == "char": + isset, default, value = iface.GetDriverParameterValueChar(param) + elif typestr == "sint": + isset, default, value = iface.GetDriverParameterValueInt(param) + value = int(value) + default = int(default) + elif typestr == "uint": + isset, default, value = iface.GetDriverParameterValueUint(param) + value = int(value) + default = int(default) + else: + isset = None + default = None + value = None + + isset = bool(isset) + + print "%20s: %s (%s:%s:%s:%s)" %(param, descr, typestr, isset, default, value) + elif arg == 'dpd': + if index >= len(sys.argv): + print "get driver parameter long description command requires driver name argument" + sys.exit() + + param = sys.argv[index] + index += 1 + + print "--- get driver parameter description (%s)" % param + descr = iface.GetDriverParameterLongDescription(param) + print descr, + elif arg == 'dps': + if index + 1 >= len(sys.argv): + print "driver parameter set command requires parametr name and value arguments" + sys.exit() + + param = sys.argv[index] + index += 1 + value = sys.argv[index] + index += 1 + + print "--- driver param set \"%s\" -> \"%s\"" % (param, value) + + typestr = iface.GetDriverParameterTypeString(param) + + if typestr == "str": + iface.SetDriverParameterValueString(param, value) + elif typestr == "bool": + iface.SetDriverParameterValueBool(param, bool_convert(value)) + elif typestr == "char": + iface.SetDriverParameterValueChar(param, value) + elif typestr == "sint": + iface.SetDriverParameterValueInt(param, int(value)) + elif typestr == "uint": + iface.SetDriverParameterValueUint(param, int(value)) + else: + print "driver parameter \"%s\" is of unknown type \"%s\"" % (param, typestr) + sys.exit() + elif arg == 'ep': + print "--- get engine parameters (type:value)" + params = iface.GetEngineParameterNames() + #print params + for param in params: + descr = iface.GetEngineParameterShortDescription(param) + typestr = iface.GetEngineParameterTypeString(param) + #print descr + #print typestr + + if typestr == "str": + value = iface.GetEngineParameterValueString(param) + elif typestr == "bool": + value = iface.GetEngineParameterValueBool(param) + value = bool(value) + elif typestr == "char": + value = iface.GetEngineParameterValueChar(param) + elif typestr == "sint": + value = iface.GetEngineParameterValueInt(param) + value = int(value) + elif typestr == "uint": + value = iface.GetEngineParameterValueUint(param) + value = int(value) + else: + value = None + + print "%20s: %s (%s:%s)" %(param, descr, typestr, value) + elif arg == 'epd': + if index >= len(sys.argv): + print "get engine parameter long description command requires driver name argument" + sys.exit() + + param = sys.argv[index] + index += 1 + + print "--- get engine parameter description (%s)" % param + descr = iface.GetEngineParameterLongDescription(param) + print descr, + elif arg == 'eps': + if index + 1 >= len(sys.argv): + print "engine parameter set command requires parametr name and value arguments" + sys.exit() + + param = sys.argv[index] + index += 1 + value = sys.argv[index] + index += 1 + + print "--- engine param set \"%s\" -> \"%s\"" % (param, value) + + typestr = iface.GetEngineParameterTypeString(param) + + if typestr == "str": + iface.SetEngineParameterValueString(param, value) + elif typestr == "bool": + iface.SetEngineParameterValueBool(param, bool_convert(value)) + elif typestr == "char": + iface.SetEngineParameterValueChar(param, value) + elif typestr == "sint": + iface.SetEngineParameterValueInt(param, int(value)) + elif typestr == "uint": + iface.SetEngineParameterValueUint(param, int(value)) + else: + print "engine parameter \"%s\" is of unknown type \"%s\"" % (param, typestr) + sys.exit() + else: + print "Unknown command '%s'" % arg + except dbus.DBusException, e: + print "DBus exception: %s" % str(e) + +if __name__ == '__main__': + main() diff --git a/jackd/Makefile.am b/jackd/Makefile.am --- a/jackd/Makefile.am +++ b/jackd/Makefile.am @@ -21,14 +21,21 @@ endif bin_PROGRAMS = jackd $(CAP_PROGS) -AM_CFLAGS = $(JACK_CFLAGS) -DJACK_LOCATION=\"$(bindir)\" +if HAVE_DBUS +bin_PROGRAMS += jackdbus +endif -jackd_SOURCES = jackd.c engine.c clientengine.c transengine.c -jackd_LDADD = ../libjack/libjack.la $(CAP_LIBS) @OS_LDFLAGS@ +AM_CFLAGS = $(JACK_CFLAGS) -DJACK_LOCATION=\"$(bindir)\" $(DBUSGLIB_CFLAGS) $(LIBXML2_CFLAGS) + +COMMON_SOURCES = engine.c clientengine.c transengine.c +COMMON_LIBS = ../libjack/libjack.la $(CAP_LIBS) @OS_LDFLAGS@ + +jackd_SOURCES = jackd.c $(COMMON_SOURCES) +jackd_LDADD = $(COMMON_LIBS) noinst_HEADERS = jack_md5.h md5.h md5_loc.h \ clientengine.h transengine.h -BUILT_SOURCES = jack_md5.h +BUILT_SOURCES = jack_md5.h jackcontroller-glue.h jack_md5.h: jackd if STRIPPED_JACKD @@ -39,6 +46,26 @@ jackstart_SOURCES = jackstart.c md5.c jackstart_SOURCES = jackstart.c md5.c jackstart_LDFLAGS = -lcap +if HAVE_DBUS +jackcontroller-glue.h: jackcontroller.xml + dbus-binding-tool --mode=glib-server --prefix=jackcontroller jackcontroller.xml > jackcontroller-glue.h + +jackdbus_SOURCES = jackdbus.c $(COMMON_SOURCES) jackcontroller.c jackcontroller.h jackcontroller.xml +jackdbus_LDADD = $(COMMON_LIBS) $(DBUSGLIB_LIBS) $(LIBXML2_LIBS) + +# Dbus service file +servicedir = $(DBUS_SERVICES_DIR) +service_in_files = org.jackaudio.service.in +service_DATA = $(service_in_files:.service.in=.service) + +# Rule to make the service file with bindir expanded +$(service_DATA): $(service_in_files) Makefile + @sed -e "s|\@bindir\@|$(bindir)|" $< > $@ + +jackdbus_EXTRA_DIST = $(service_in_files) + +endif + man_MANS = jackd.1 jackstart.1 EXTRA_DIST = $(man_MANS) diff --git a/jackd/jackcontroller.c b/jackd/jackcontroller.c new file mode 100644 --- /dev/null +++ b/jackd/jackcontroller.c @@ -0,0 +1,2851 @@ +/* -*- mode: c; c-file-style: "bsd"; -*- */ +/* + Copyright (C) 2007 Nedko Arnaudov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License. + + This program 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 General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#if HAVE_LIBXML2 +#include +#include +#include + +/* XPath expression used for engine options selection */ +#define XPATH_ENGINE_OPTIONS_EXPRESSION "/jack/engine/option" + +/* XPath expression used for drivers selection */ +#define XPATH_DRIVERS_EXPRESSION "/jack/drivers/driver" + +/* XPath expression used for driver options selection */ +#define XPATH_DRIVER_OPTIONS_EXPRESSION "/jack/drivers/driver[@name = '%s']/option" +#endif + +#include "jackcontroller.h" +#include "jackcontroller-glue.h" + +#include "jackdbus.h" + +struct jack_controller_driver +{ + char * filename; + jack_driver_desc_t * desc_ptr; + JSList * params; +}; + +struct jack_controller +{ + gboolean dispose_has_run; + + GMainLoop *loop; + gboolean manually_started; + + unsigned int drivers_count; + JSList *drivers; + + /* current driver, NULL if not driver is selected */ + struct jack_controller_driver *driver_ptr; + + jack_engine_t *engine; + + int realtime; /* boolean, whether to be "realtime" */ + int realtime_priority; + + char *server_name; + + int do_mlock; /* boolean, whether to do memory locking */ + int temporary; /* boolean, whether to exit once all clients have closed their connections */ + int verbose; /* boolean, whether to be verbose */ + int client_timeout; /* msecs; if zero, use period size. */ + unsigned int port_max; /* maximum number of ports the JACK server can manage */ + int do_unlock; /* boolean, whether to unlock libraries GTK+, QT, FLTK, Wine */ + jack_nframes_t frame_time_offset; + int nozombies; /* boolean, whether to prevent from ever kicking out clients because they were too slow */ +}; + +/** Parameter types, intentionally similar to jack_driver_param_type_t */ +typedef enum +{ + JackParamInt = 1, + JackParamUInt, + JackParamChar, + JackParamString, + JackParamBool + +} jack_param_type_t; + +struct jack_engine_parameter_descriptor +{ + const char *name; + jack_param_type_t type; + const char *description_short; + const char *description_long; +}; + +struct jack_engine_parameter_descriptor jack_engine_parameters[] = +{ + {"realtime", JackParamBool, "Whether to use realtime mode", "Use realtime scheduling. This is needed for reliable low-latency performance. On most systems, it requires JACK to run with special scheduler and memory allocation privileges, which may be obtained in several ways. On Linux you should use PAM."}, + {"realtime-priority", JackParamInt, "Scheduler priority when running in realtime mode.", NULL}, + {"no-mem-lock", JackParamBool, "Do not attempt to lock memory, even in realtime mode.", NULL}, + {"temporary", JackParamBool, "Exit once all clients have closed their connections.", NULL}, + {"verbose", JackParamBool, "Verbose mode", NULL}, + {"client-timeout", JackParamInt, "Client timeout limit in milliseconds", "Client timeout limit in milliseconds. In realtime mode the client timeout must be smaller than the watchdog timeout (5000 msec)."}, + {"no-zombies", JackParamBool, "Prevent JACK from ever kicking out clients because they were too slow.", "Prevent JACK from ever kicking out clients because they were too slow. JACK and its clients are still subject to the supervision of the watchdog thread or its equivalent."}, + {"port-max", JackParamUInt, "Maximum number of ports the JACK server can manage", NULL}, + {"libs-unlock", JackParamBool, "Unlock libraries GTK+, QT, FLTK, Wine.", NULL}, +}; + +#define JACK_ENGINE_PARAMS_COUNT (sizeof(jack_engine_parameters)/sizeof(struct jack_engine_parameter_descriptor)) + +gboolean +jack_controller_load_descriptor( + struct jack_controller * controller_ptr, + struct jack_controller_driver * driver_ptr) +{ + jack_driver_desc_t * descriptor; + JackDriverDescFunction so_get_descriptor; + void * dlhandle; + const char * dlerr; + int err; + + if (controller_ptr->verbose) { + jack_info ("getting driver descriptor from %s", driver_ptr->filename); + } + + dlhandle = dlopen(driver_ptr->filename, RTLD_NOW|RTLD_GLOBAL); + if (dlhandle == NULL) { + jack_error("could not open driver .so '%s': %s", driver_ptr->filename, dlerror()); + return FALSE; + } + + so_get_descriptor = (JackDriverDescFunction) + dlsym(dlhandle, "driver_get_descriptor"); + + dlerr = dlerror(); + if (dlerr != NULL) { + jack_error("cannot find driver_get_descriptor symbol: %s", dlerr); + dlclose(dlhandle); + return FALSE; + } + + descriptor = so_get_descriptor(); + if (descriptor == NULL) { + jack_error("driver from '%s' returned NULL descriptor", driver_ptr->filename); + dlclose(dlhandle); + return FALSE; + } + + err = dlclose(dlhandle); + if (err != 0) { + jack_error("error closing driver .so '%s': %s", driver_ptr->filename, dlerror()); + free(descriptor->params); + free(descriptor); + return FALSE; + } + + /* for some mad reason we are storing filename in descriptor allocated by driver + instead of reusing dlhandle when another dlsym() call is needed */ + snprintf (descriptor->file, sizeof(descriptor->file), "%s", driver_ptr->filename); + + driver_ptr->desc_ptr = descriptor; + + return TRUE; +} + +static +gboolean +jack_drivers_load( + struct jack_controller *controller_ptr) +{ + struct dirent * dir_entry; + DIR * dir_stream; + const char * ptr; + int err; + char* driver_dir; + struct jack_controller_driver * driver_ptr; + struct jack_controller_driver * other_driver_ptr; + JSList * node_ptr; + + if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) { + driver_dir = ADDON_DIR; + } + + if (controller_ptr->verbose) { + jack_info ("searching for drivers in %s", driver_dir); + } + + /* search through the driver_dir and add get descriptors + from the .so files in it */ + dir_stream = opendir (driver_dir); + if (!dir_stream) { + jack_error ("could not open driver directory %s: %s", + driver_dir, strerror (errno)); + return FALSE; + } + + while ( (dir_entry = readdir (dir_stream)) ) { + /* check the filename is of the right format */ + if (strncmp ("jack_", dir_entry->d_name, 5) != 0) { + continue; + } + +#if HAVE_LIBXML2 + /* temporary disable ffado driver until we make it work with libxml2 loaded by jackdbus */ + if (strcmp ("jack_firewire.so", dir_entry->d_name) == 0) { + continue; + } +#endif + + ptr = strrchr (dir_entry->d_name, '.'); + if (!ptr) { + continue; + } + ptr++; + if (strncmp ("so", ptr, 2) != 0) { + continue; + } + + driver_ptr = malloc(sizeof(struct jack_controller_driver)); + if (driver_ptr == NULL) + { + jack_error("memory allocation of jack_controller_driver structure failed."); + continue; + } + + driver_ptr->filename = malloc(strlen(driver_dir) + 1 + strlen(dir_entry->d_name) + 1); + + sprintf(driver_ptr->filename, "%s/%s", driver_dir, dir_entry->d_name); + + if (!jack_controller_load_descriptor(controller_ptr, driver_ptr)) + { + goto dealloc_driver; + } + + /* check it doesn't exist already */ + for (node_ptr = controller_ptr->drivers; node_ptr != NULL; node_ptr = jack_slist_next(node_ptr)) + { + other_driver_ptr = (struct jack_controller_driver *)node_ptr->data; + + if (strcmp(driver_ptr->desc_ptr->name, other_driver_ptr->desc_ptr->name) == 0) + { + jack_error( + "the drivers in '%s' and '%s' both have the name '%s'; using the first", + other_driver_ptr->filename, + driver_ptr->filename, + driver_ptr->desc_ptr->name); + goto dealloc_descriptor; + } + } + + driver_ptr->params = NULL; + + controller_ptr->drivers = jack_slist_append(controller_ptr->drivers, driver_ptr); + controller_ptr->drivers_count++; + + continue; + + dealloc_descriptor: + free(driver_ptr->desc_ptr->params); + free(driver_ptr->desc_ptr); + + dealloc_driver: + free(driver_ptr->filename); + free(driver_ptr); + } + + err = closedir (dir_stream); + if (err) { + jack_error ("error closing driver directory %s: %s", + driver_dir, strerror (errno)); + } + + if (controller_ptr->drivers_count == 0) + { + jack_error ("could not find any drivers in %s!", driver_dir); + return FALSE; + } + + return TRUE; +} + +static void +jack_cleanup_files (const char *server_name) +{ + DIR *dir; + struct dirent *dirent; + char dir_name[PATH_MAX+1] = ""; + jack_server_dir (server_name, dir_name); + + /* On termination, we remove all files that jackd creates so + * subsequent attempts to start jackd will not believe that an + * instance is already running. If the server crashes or is + * terminated with SIGKILL, this is not possible. So, cleanup + * is also attempted when jackd starts. + * + * There are several tricky issues. First, the previous JACK + * server may have run for a different user ID, so its files + * may be inaccessible. This is handled by using a separate + * JACK_TMP_DIR subdirectory for each user. Second, there may + * be other servers running with different names. Each gets + * its own subdirectory within the per-user directory. The + * current process has already registered as `server_name', so + * we know there is no other server actively using that name. + */ + + /* nothing to do if the server directory does not exist */ + if ((dir = opendir (dir_name)) == NULL) { + return; + } + + /* unlink all the files in this directory, they are mine */ + while ((dirent = readdir (dir)) != NULL) { + + char fullpath[PATH_MAX+1]; + + if ((strcmp (dirent->d_name, ".") == 0) + || (strcmp (dirent->d_name, "..") == 0)) { + continue; + } + + snprintf (fullpath, sizeof (fullpath), "%s/%s", + dir_name, dirent->d_name); + + if (unlink (fullpath)) { + jack_error ("cannot unlink `%s' (%s)", fullpath, + strerror (errno)); + } + } + + closedir (dir); + + /* now, delete the per-server subdirectory, itself */ + if (rmdir (dir_name)) { + jack_error ("cannot remove `%s' (%s)", dir_name, + strerror (errno)); + } + + /* finally, delete the per-user subdirectory, if empty */ + if (rmdir (jack_user_dir ())) { + if (errno != ENOTEMPTY) { + jack_error ("cannot remove `%s' (%s)", + jack_user_dir (), strerror (errno)); + } + } +} + +#define JACK_CONTROLLER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), JACK_CONTROLLER_TYPE, struct jack_controller)) + +static void +jack_controller_dispose(GObject * obj) +{ + struct jack_controller * controller_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(obj); + + if (controller_ptr->dispose_has_run) + { + /* If dispose did already run, return. */ + return; + } + + /* Make sure dispose does not run twice. */ + controller_ptr->dispose_has_run = TRUE; + + /* + * In dispose, you are supposed to free all types referenced from this + * object which might themselves hold a reference to self. Generally, + * the most simple solution is to unref all members on which you own a + * reference. + */ + + /* Chain up to the parent class */ + G_OBJECT_CLASS(g_type_class_peek_parent(G_OBJECT_GET_CLASS(obj)))->dispose(obj); +} + +static void +jack_controller_finalize(GObject * obj) +{ +// struct jack_controller * self = JACK_CONTROLLER_GET_PRIVATE(obj); + + /* + * Here, complete object destruction. + * You might not need to do much... + */ + //g_free(self->private); + + /* Chain up to the parent class */ + G_OBJECT_CLASS(g_type_class_peek_parent(G_OBJECT_GET_CLASS(obj)))->finalize(obj); +} + +static void +jack_controller_class_init( + gpointer class_ptr, + gpointer class_data_ptr) +{ + G_OBJECT_CLASS(class_ptr)->dispose = jack_controller_dispose; + G_OBJECT_CLASS(class_ptr)->finalize = jack_controller_finalize; + + g_type_class_add_private(G_OBJECT_CLASS(class_ptr), sizeof(struct jack_controller)); + dbus_g_object_type_install_info (JACK_CONTROLLER_TYPE, &dbus_glib_jackcontroller_object_info); +} + +static void +jack_controller_init( + GTypeInstance * instance, + gpointer g_class) +{ + struct jack_controller * controller_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(instance); + + controller_ptr->dispose_has_run = FALSE; + + controller_ptr->loop = NULL; + controller_ptr->manually_started = FALSE; + + controller_ptr->drivers = NULL; + controller_ptr->drivers_count = 0; + + controller_ptr->engine = NULL; + controller_ptr->server_name = jack_default_server_name(); + controller_ptr->realtime = 0; + controller_ptr->realtime_priority = 10; + controller_ptr->do_mlock = 1; + controller_ptr->temporary = 0; + controller_ptr->verbose = 0; + controller_ptr->client_timeout = 500; + controller_ptr->port_max = 256; + controller_ptr->do_unlock = 0; + controller_ptr->frame_time_offset = 0; + controller_ptr->nozombies = 0; +} + +GType jack_controller_get_type() +{ + static GType type = 0; + if (type == 0) + { + type = g_type_register_static_simple( + G_TYPE_OBJECT, + "jack_controller_type", + sizeof(JackControllerClass), + jack_controller_class_init, + sizeof(JackController), + jack_controller_init, + 0); + } + + return type; +} + +GQuark +jack_controller_error_quark() +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("jack_controller_error"); + + return quark; +} + +gboolean +jack_controller_test( + JackController * object_ptr, + GError ** error) +{ + jack_info("Test!"); + g_set_error(error, jack_controller_error_quark(), 0, "Muhahaha!"); + return FALSE; +} + +gboolean +jack_controller_exit( + JackController * object_ptr, + GError ** error) +{ + struct jack_controller * controller_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + jack_info("Exit!"); + g_main_quit(controller_ptr->loop); + return TRUE; +} + +gboolean +jack_controller_is_manually_activated( + JackController * object_ptr, + gboolean * manually_started_ptr, + GError ** error) +{ + struct jack_controller * controller_ptr; + + jack_info("Is manually started?"); + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + *manually_started_ptr = controller_ptr->manually_started; + return TRUE; +} + +void +jack_controller_error( + GError **error, + const gchar *format, + ...) +{ + va_list ap; + char buffer[300]; + + va_start(ap, format); + + vsnprintf(buffer, sizeof(buffer), format, ap); + + jack_error_callback(buffer); + if (error != NULL) + { + g_set_error(error, jack_controller_error_quark(), 0, buffer); + } + + va_end(ap); +} + +jack_driver_param_desc_t * +jack_controller_find_parameter_descriptor( + struct jack_controller_driver *driver_ptr, + jack_driver_param_t * param_ptr) +{ + unsigned int i; + + for (i = 0; i < driver_ptr->desc_ptr->nparams ; i++) + { + if (driver_ptr->desc_ptr->params[i].character == param_ptr->character) + { + return driver_ptr->desc_ptr->params + i; + } + } + + return NULL; +} + +jack_driver_param_desc_t * +jack_controller_find_parameter_descriptor_by_name( + struct jack_controller_driver *driver_ptr, + const char *param_name) +{ + unsigned int i; + + for (i = 0; i < driver_ptr->desc_ptr->nparams ; i++) + { + if (strcmp(driver_ptr->desc_ptr->params[i].name, param_name) == 0) + { + return driver_ptr->desc_ptr->params + i; + } + } + + return NULL; +} + +jack_driver_param_desc_t * +jack_controller_find_parameter_descriptor_by_name_current_driver( + struct jack_controller *controller_ptr, + const gchar *param_name) +{ + return jack_controller_find_parameter_descriptor_by_name(controller_ptr->driver_ptr, param_name); +} + +/* search for parameter matching parameter descriptor, return NULL if not found */ +jack_driver_param_t * +jack_controller_find_parameter( + struct jack_controller_driver *driver_ptr, + const jack_driver_param_desc_t *descr_ptr) +{ + JSList * node_ptr; + jack_driver_param_t * param_ptr; + + for (node_ptr = driver_ptr->params; node_ptr != NULL; node_ptr = jack_slist_next(node_ptr)) + { + param_ptr = (jack_driver_param_t *)node_ptr->data; + + if (param_ptr->character == descr_ptr->character) + { + return param_ptr; + } + } + + return NULL; +} + +jack_driver_param_t * +jack_controller_find_parameter_current_driver( + struct jack_controller *controller_ptr, + const jack_driver_param_desc_t *descr_ptr) +{ + return jack_controller_find_parameter(controller_ptr->driver_ptr, descr_ptr); +} + +/* search for parameter matching parameter descriptor, if not found, create new one */ +jack_driver_param_t * +jack_controller_find_or_create_parameter( + struct jack_controller_driver *driver_ptr, + const jack_driver_param_desc_t *descr_ptr) +{ + jack_driver_param_t * param_ptr; + + param_ptr = jack_controller_find_parameter(driver_ptr, descr_ptr); + if (param_ptr != NULL) + { + return param_ptr; + } + + param_ptr = malloc(sizeof(jack_driver_param_t)); + if (param_ptr == NULL) + { + jack_error("Allocation of jack_driver_param_t structure failed."); + return NULL; + } + + param_ptr->character = descr_ptr->character; + param_ptr->value = descr_ptr->value; + + driver_ptr->params = jack_slist_append(driver_ptr->params, param_ptr); + + return param_ptr; +} + +/* search for parameter matching parameter descriptor, if not found, create new one */ +jack_driver_param_t * +jack_controller_find_or_create_parameter_current_driver( + struct jack_controller *controller_ptr, + const jack_driver_param_desc_t *descr_ptr) +{ + return jack_controller_find_or_create_parameter(controller_ptr->driver_ptr, descr_ptr); +} + +#if HAVE_LIBXML2 + +gboolean +jack_controller_settings_write_option( + xmlTextWriterPtr writer, + const char *name, + const char *content, + GError **error) +{ + if (xmlTextWriterStartElement(writer, BAD_CAST "option") == -1) + { + jack_controller_error(error, "xmlTextWriterStartElement() failed."); + return FALSE; + } + + if (xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST name) == -1) + { + jack_controller_error(error, "xmlTextWriterWriteAttribute() failed."); + return FALSE; + } + + if (xmlTextWriterWriteString(writer, BAD_CAST content) == -1) + { + jack_controller_error(error, "xmlTextWriterWriteString() failed."); + return FALSE; + } + + if (xmlTextWriterEndElement(writer) == -1) + { + jack_controller_error(error, "xmlTextWriterEndElement() failed."); + return FALSE; + } + + return TRUE; +} + +gboolean +jack_controller_settings_write_engine( + struct jack_controller * controller_ptr, + xmlTextWriterPtr writer, + GError **error) +{ + gchar value_str[50]; + +/* jack_info("engine settings begin"); */ +/* if (xmlTextWriterWriteComment(writer, BAD_CAST "engine parameters") == -1) */ +/* { */ +/* jack_controller_error(error, "xmlTextWriterWriteComment() failed."); */ +/* return FALSE; */ +/* } */ + + if (xmlTextWriterStartElement(writer, BAD_CAST "engine") == -1) + { + jack_controller_error(error, "xmlTextWriterStartElement() failed."); + return FALSE; + } + + if (controller_ptr->driver_ptr != NULL) + { + if (!jack_controller_settings_write_option(writer, "driver", controller_ptr->driver_ptr->desc_ptr->name, error)) + { + return FALSE; + } + } + + if (!jack_controller_settings_write_option(writer, "realtime", controller_ptr->realtime ? "true" : "false", error)) + { + return FALSE; + } + + sprintf(value_str, "%d", controller_ptr->realtime_priority); + if (!jack_controller_settings_write_option(writer, "realtime-priority", value_str, error)) + { + return FALSE; + } + + if (!jack_controller_settings_write_option(writer, "no-mem-lock", controller_ptr->do_mlock ? "false" : "true", error)) /* reverse */ + { + return FALSE; + } + + if (!jack_controller_settings_write_option(writer, "temporary", controller_ptr->temporary ? "true" : "false", error)) + { + return FALSE; + } + + if (!jack_controller_settings_write_option(writer, "verbose", controller_ptr->verbose ? "true" : "false", error)) + { + return FALSE; + } + + sprintf(value_str, "%d", controller_ptr->client_timeout); + if (!jack_controller_settings_write_option(writer, "client-timeout", value_str, error)) + { + return FALSE; + } + + if (!jack_controller_settings_write_option(writer, "no-zombies", controller_ptr->nozombies ? "true" : "false", error)) + { + return FALSE; + } + + sprintf(value_str, "%d", controller_ptr->port_max); + if (!jack_controller_settings_write_option(writer, "port-max", value_str, error)) + { + return FALSE; + } + + if (!jack_controller_settings_write_option(writer, "libs-unlock", controller_ptr->do_unlock ? "true" : "false", error)) + { + return FALSE; + } + + if (xmlTextWriterEndElement(writer) == -1) + { + jack_controller_error(error, "xmlTextWriterEndElement() failed."); + return FALSE; + } + +/* jack_info("engine settings end"); */ + return TRUE; +} + +gboolean +jack_controller_settings_write_driver( + struct jack_controller * controller_ptr, + xmlTextWriterPtr writer, + struct jack_controller_driver *driver_ptr, + GError **error) +{ + JSList *node_ptr; + jack_driver_param_t *param_ptr; + jack_driver_param_desc_t *param_desc_ptr; + gchar value_str[50]; + +/* if (xmlTextWriterWriteComment(writer, BAD_CAST "driver parameters") == -1) */ +/* { */ +/* jack_controller_error(error, "xmlTextWriterWriteComment() failed."); */ +/* return FALSE; */ +/* } */ + + if (xmlTextWriterStartElement(writer, BAD_CAST "driver") == -1) + { + jack_controller_error(error, "xmlTextWriterStartElement() failed."); + return FALSE; + } + + if (xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST driver_ptr->desc_ptr->name) == -1) + { + jack_controller_error(error, "xmlTextWriterWriteAttribute() failed."); + return FALSE; + } + + for (node_ptr = driver_ptr->params; node_ptr != NULL; node_ptr = jack_slist_next(node_ptr)) + { + param_ptr = (jack_driver_param_t *)node_ptr->data; + param_desc_ptr = jack_controller_find_parameter_descriptor(driver_ptr, param_ptr); + +/* if (xmlTextWriterWriteComment(writer, BAD_CAST param_desc_ptr->short_desc) == -1) */ +/* { */ +/* jack_controller_error(error, "xmlTextWriterWriteComment() failed."); */ +/* return FALSE; */ +/* } */ + + switch (param_desc_ptr->type) + { + case JackDriverParamInt: + sprintf(value_str, "%d", param_ptr->value.i); + if (!jack_controller_settings_write_option(writer, param_desc_ptr->name, value_str, error)) + { + return FALSE; + } + break; + case JackDriverParamUInt: + sprintf(value_str, "%u", param_ptr->value.ui); + if (!jack_controller_settings_write_option(writer, param_desc_ptr->name, value_str, error)) + { + return FALSE; + } + break; + case JackDriverParamChar: + sprintf(value_str, "%c", param_ptr->value.c); + if (!jack_controller_settings_write_option(writer, param_desc_ptr->name, value_str, error)) + { + return FALSE; + } + break; + case JackDriverParamString: + if (!jack_controller_settings_write_option(writer, param_desc_ptr->name, param_ptr->value.str, error)) + { + return FALSE; + } + break; + case JackDriverParamBool: + if (!jack_controller_settings_write_option(writer, param_desc_ptr->name, param_ptr->value.i ? "true" : "false", error)) + { + return FALSE; + } + break; + default: + jack_error("parameter of unknown type %d", param_desc_ptr->type); + } + } + + if (xmlTextWriterEndElement(writer) == -1) + { + jack_controller_error(error, "xmlTextWriterEndElement() failed."); + return FALSE; + } + + return TRUE; +} + +gboolean +jack_controller_settings_write_drivers( + struct jack_controller * controller_ptr, + xmlTextWriterPtr writer, + GError **error) +{ + JSList * node_ptr; + struct jack_controller_driver *driver_ptr; + + node_ptr = controller_ptr->drivers; + + if (xmlTextWriterStartElement(writer, BAD_CAST "drivers") == -1) + { + jack_controller_error(error, "xmlTextWriterStartElement() failed."); + return FALSE; + } + + while (node_ptr != NULL) + { + driver_ptr = (struct jack_controller_driver *)node_ptr->data; + + if (!jack_controller_settings_write_driver( + controller_ptr, + writer, + driver_ptr, + error)) + { + return FALSE; + } + + node_ptr = jack_slist_next(node_ptr); + } + + if (xmlTextWriterEndElement(writer) == -1) + { + jack_controller_error(error, "xmlTextWriterEndElement() failed."); + return FALSE; + } + + return TRUE; +} + +gboolean +jack_controller_settings_save( + struct jack_controller * controller_ptr, + GError **error) +{ + xmlTextWriterPtr writer; + gchar *filename; + gboolean ret; + time_t timestamp; + char timestamp_str[28]; + + time(×tamp); + timestamp_str[0] = ' '; + ctime_r(×tamp, timestamp_str + 1); + timestamp_str[25] = ' '; + + ret = FALSE; + + filename = g_strdup_printf("%s" JACKDBUS_CONF, g_jackdbus_dir); + if (filename == NULL) + { + jack_controller_error(error, "Out of memory."); + goto fail; + } + + jack_info("saving settings to \"%s\"", filename); + + writer = xmlNewTextWriterFilename(filename, 0); + if (writer == NULL) + { + jack_controller_error(error, "Error creating the xml writer."); + goto fail_free_filename; + } + + if (xmlTextWriterSetIndent(writer, 1) == -1) + { + jack_controller_error(error, "xmlTextWriterSetIndent() failed."); + goto fail_free_writter; + } + + if (xmlTextWriterStartDocument(writer, NULL, NULL, NULL) == -1) + { + jack_controller_error(error, "xmlTextWriterStartDocument() failed."); + goto fail_free_writter; + } + + if (xmlTextWriterWriteComment( + writer, + BAD_CAST + "\n" + "JACK settings, as persisted by D-Bus object.\n" + "You probably don't want to edit this because\n" + "it will be overwritten next time jackdbus saves.\n") == -1) + { + jack_controller_error(error, "xmlTextWriterWriteComment() failed."); + goto fail_free_writter; + } + + if (xmlTextWriterWriteComment(writer, BAD_CAST timestamp_str) == -1) + { + jack_controller_error(error, "xmlTextWriterWriteComment() failed."); + goto fail_free_writter; + } + + if (xmlTextWriterStartElement(writer, BAD_CAST "jack") == -1) + { + jack_controller_error(error, "xmlTextWriterStartElement() failed."); + goto fail_free_writter; + } + + if (!jack_controller_settings_write_engine(controller_ptr, writer, error)) + { + goto fail_free_writter; + } + + if (!jack_controller_settings_write_drivers(controller_ptr, writer, error)) + { + goto fail_free_writter; + } + + if (xmlTextWriterEndElement(writer) == -1) + { + jack_controller_error(error, "xmlTextWriterStartElement() failed."); + goto fail_free_writter; + } + + if (xmlTextWriterEndDocument(writer) == -1) + { + jack_controller_error(error, "xmlTextWriterEndDocument() failed."); + goto fail_free_writter; + } + + ret = TRUE; + +fail_free_writter: + xmlFreeTextWriter(writer); + +fail_free_filename: + g_free(filename); + +fail: + return ret; +} + +void +jack_controller_settings_set_bool_option( + const char *value_str, + int *value_ptr, + gboolean reverse) +{ + if (strcmp(value_str, "true") == 0) + { + *value_ptr = !reverse; + } + else if (strcmp(value_str, "false") == 0) + { + *value_ptr = reverse; + } + else + { + jack_error("ignoring unknown bool value \"%s\"", value_str); + } +} + +void +jack_controller_settings_set_sint_option( + const char *value_str, + int *value_ptr) +{ + *value_ptr = atoi(value_str); +} + +void +jack_controller_settings_set_uint_option( + const char *value_str, + unsigned int *value_ptr) +{ + *value_ptr = strtoul(value_str, NULL, 10); +} + +void +jack_controller_settings_set_char_option( + const char *value_str, + char *value_ptr) +{ + if (value_str[0] == 0 || value_str[1] != 0) + { + jack_error("invalid char option value \"%s\"", value_str); + return; + } + + *value_ptr = *value_str; +} + +void +jack_controller_settings_set_string_option( + const char *value_str, + char *value_ptr, + size_t max_size) +{ + size_t size; + + size = strlen(value_str); + + if (size >= max_size) + { + jack_error("string option value \"%s\" is too long, max is %u chars (including terminating zero)", value_str, (unsigned int)max_size); + return; + } + + strcpy(value_ptr, value_str); +} + +void +jack_controller_settings_read_engine( + struct jack_controller * controller_ptr, + xmlXPathContextPtr xpath_ctx_ptr) +{ + xmlXPathObjectPtr xpath_obj_ptr; + xmlBufferPtr content_buffer_ptr; + int i; + const char *option_name; + const char *option_value; + struct jack_controller_driver * driver_ptr; + JSList * node_ptr; + + /* Evaluate xpath expression */ + xpath_obj_ptr = xmlXPathEvalExpression((const xmlChar *)XPATH_ENGINE_OPTIONS_EXPRESSION, xpath_ctx_ptr); + if (xpath_obj_ptr == NULL) + { + jack_error("Unable to evaluate XPath expression \"%s\"", XPATH_ENGINE_OPTIONS_EXPRESSION); + goto exit; + } + + if (xpath_obj_ptr->nodesetval == NULL || xpath_obj_ptr->nodesetval->nodeNr == 0) + { + jack_error("XPath \"%s\" evaluation returned no data", XPATH_ENGINE_OPTIONS_EXPRESSION); + goto free_xpath_obj; + } + + content_buffer_ptr = xmlBufferCreate(); + if (content_buffer_ptr == NULL) + { + jack_error("xmlBufferCreate() failed."); + goto free_xpath_obj; + } + + for (i = 0 ; i < xpath_obj_ptr->nodesetval->nodeNr ; i++) + { + //jack_info("engine option \"%s\" at index %d", xmlGetProp(xpath_obj_ptr->nodesetval->nodeTab[i], BAD_CAST "name"), i); + + if (xmlNodeBufGetContent(content_buffer_ptr, xpath_obj_ptr->nodesetval->nodeTab[i]) == -1) + { + jack_error("xmlNodeBufGetContent() failed."); + goto next_option; + } + + option_name = (const char *)xmlGetProp(xpath_obj_ptr->nodesetval->nodeTab[i], BAD_CAST "name"); + option_value = (const char *)xmlBufferContent(content_buffer_ptr); + + jack_info("engine option \"%s\" with value \"%s\"", option_name, option_value); + + if (strcmp(option_name, "driver") == 0) + { + for (node_ptr = controller_ptr->drivers ; node_ptr != NULL ; node_ptr = jack_slist_next(node_ptr)) + { + driver_ptr = (struct jack_controller_driver *)node_ptr->data; + + if (strcmp(driver_ptr->desc_ptr->name, option_value) == 0) { + jack_info("driver \"%s\" selected", driver_ptr->desc_ptr->name); + controller_ptr->driver_ptr = driver_ptr; + goto next_option; + } + } + + jack_error("unknown driver '%s'", option_value); + } + else if (strcmp(option_name, "realtime") == 0) + { + jack_controller_settings_set_bool_option(option_value, &controller_ptr->realtime, FALSE); + } + else if (strcmp(option_name, "realtime-priority") == 0) + { + jack_controller_settings_set_sint_option(option_value, &controller_ptr->realtime_priority); + } + else if (strcmp(option_name, "no-mem-lock") == 0) + { + jack_controller_settings_set_bool_option(option_value, &controller_ptr->do_mlock, TRUE); + } + else if (strcmp(option_name, "temporary") == 0) + { + jack_controller_settings_set_bool_option(option_value, &controller_ptr->temporary, FALSE); + } + else if (strcmp(option_name, "verbose") == 0) + { + jack_controller_settings_set_bool_option(option_value, &controller_ptr->verbose, FALSE); + } + else if (strcmp(option_name, "client-timeout") == 0) + { + jack_controller_settings_set_sint_option(option_value, &controller_ptr->client_timeout); + } + else if (strcmp(option_name, "no-zombies") == 0) + { + jack_controller_settings_set_bool_option(option_value, &controller_ptr->nozombies, FALSE); + } + else if (strcmp(option_name, "port-max") == 0) + { + jack_controller_settings_set_uint_option(option_value, &controller_ptr->port_max); + } + else if (strcmp(option_name, "libs-unlock") == 0) + { + jack_controller_settings_set_bool_option(option_value, &controller_ptr->do_unlock, FALSE); + } + else + { + jack_error("ignoring unknown option \"%s\" with value \"%s\"", option_name, option_value); + } + + next_option: + xmlBufferEmpty(content_buffer_ptr); + } + +//free_buffer: + xmlBufferFree(content_buffer_ptr); + +free_xpath_obj: + xmlXPathFreeObject(xpath_obj_ptr); + +exit: + return; +} + +void +jack_controller_settings_read_driver( + struct jack_controller * controller_ptr, + xmlXPathContextPtr xpath_ctx_ptr, + struct jack_controller_driver * driver_ptr) +{ + gchar *xpath; + xmlXPathObjectPtr xpath_obj_ptr; + xmlBufferPtr content_buffer_ptr; + int i; + const char *option_name; + const char *option_value; + jack_driver_param_desc_t * param_desc_ptr; + jack_driver_param_t * param_ptr; + int value_int; + unsigned int value_uint; + + jack_info("reading options for driver \"%s\"", driver_ptr->desc_ptr->name); + + xpath = g_strdup_printf(XPATH_DRIVER_OPTIONS_EXPRESSION, driver_ptr->desc_ptr->name); + if (xpath == NULL) + { + jack_error("Out of memory."); + goto exit; + } + + //jack_info("xpath = \"%s\"", xpath); + + /* Evaluate xpath expression */ + xpath_obj_ptr = xmlXPathEvalExpression((const xmlChar *)xpath, xpath_ctx_ptr); + if (xpath_obj_ptr == NULL) + { + jack_error("Unable to evaluate XPath expression \"%s\"", xpath); + goto free_xpath; + } + + if (xpath_obj_ptr->nodesetval == NULL || xpath_obj_ptr->nodesetval->nodeNr == 0) + { + //jack_info("XPath \"%s\" evaluation returned no data", xpath); + goto free_xpath_obj; + } + + content_buffer_ptr = xmlBufferCreate(); + if (content_buffer_ptr == NULL) + { + jack_error("xmlBufferCreate() failed."); + goto free_xpath_obj; + } + + for (i = 0 ; i < xpath_obj_ptr->nodesetval->nodeNr ; i++) + { + //jack_info("driver option \"%s\" at index %d", xmlGetProp(xpath_obj_ptr->nodesetval->nodeTab[i], BAD_CAST "name"), i); + + if (xmlNodeBufGetContent(content_buffer_ptr, xpath_obj_ptr->nodesetval->nodeTab[i]) == -1) + { + jack_error("xmlNodeBufGetContent() failed."); + goto next_option; + } + + option_name = (const char *)xmlGetProp(xpath_obj_ptr->nodesetval->nodeTab[i], BAD_CAST "name"); + option_value = (const char *)xmlBufferContent(content_buffer_ptr); + + jack_info("driver option \"%s\" with value \"%s\"", option_name, option_value); + + param_desc_ptr = jack_controller_find_parameter_descriptor_by_name(driver_ptr, option_name); + if (param_desc_ptr == NULL) + { + jack_error("Unknown parameter \"%s\" of driver \"%s\"", option_name, driver_ptr->desc_ptr->name); + goto next_option; + } + + param_ptr = jack_controller_find_or_create_parameter(driver_ptr, param_desc_ptr); + if (param_ptr == NULL) + { + jack_error("driver parameter creation failed"); + goto next_option; + } + + switch (param_desc_ptr->type) + { + case JackDriverParamInt: + jack_controller_settings_set_sint_option(option_value, &value_int); + param_ptr->value.i = value_int; + break; + case JackDriverParamUInt: + jack_controller_settings_set_uint_option(option_value, &value_uint); + param_ptr->value.ui = value_uint; + break; + case JackDriverParamChar: + jack_controller_settings_set_char_option(option_value, ¶m_ptr->value.c); + break; + case JackDriverParamString: + jack_controller_settings_set_string_option(option_value, param_ptr->value.str, sizeof(param_ptr->value.str)); + break; + case JackDriverParamBool: + jack_controller_settings_set_bool_option(option_value, &value_int, FALSE); + param_ptr->value.i = value_int; + break; + default: + jack_error("Parameter \"%s\" of driver \"%s\" is of unknown type %d", + param_desc_ptr->name, + driver_ptr->desc_ptr->name, + param_desc_ptr->type); + } + + next_option: + xmlBufferEmpty(content_buffer_ptr); + } + +//free_buffer: + xmlBufferFree(content_buffer_ptr); + +free_xpath_obj: + xmlXPathFreeObject(xpath_obj_ptr); + +free_xpath: + g_free(xpath); + +exit: + return; +} + +void +jack_controller_settings_read_drivers( + struct jack_controller * controller_ptr, + xmlXPathContextPtr xpath_ctx_ptr) +{ + xmlXPathObjectPtr xpath_obj_ptr; + int i; + const char *driver_name; + struct jack_controller_driver * driver_ptr; + JSList * node_ptr; + + /* Evaluate xpath expression */ + xpath_obj_ptr = xmlXPathEvalExpression((const xmlChar *)XPATH_DRIVERS_EXPRESSION, xpath_ctx_ptr); + if (xpath_obj_ptr == NULL) + { + jack_error("Unable to evaluate XPath expression \"%s\"", XPATH_DRIVERS_EXPRESSION); + goto exit; + } + + if (xpath_obj_ptr->nodesetval == NULL || xpath_obj_ptr->nodesetval->nodeNr == 0) + { + jack_error("XPath \"%s\" evaluation returned no data", XPATH_DRIVERS_EXPRESSION); + goto free_xpath_obj; + } + + for (i = 0 ; i < xpath_obj_ptr->nodesetval->nodeNr ; i++) + { + driver_name = (const char *)xmlGetProp(xpath_obj_ptr->nodesetval->nodeTab[i], BAD_CAST "name"); + //jack_info("setting for driver \"%s\" found", driver_name); + for (node_ptr = controller_ptr->drivers ; node_ptr != NULL ; node_ptr = jack_slist_next(node_ptr)) + { + driver_ptr = (struct jack_controller_driver *)node_ptr->data; + + if (strcmp(driver_ptr->desc_ptr->name, driver_name) == 0) { + jack_controller_settings_read_driver(controller_ptr, xpath_ctx_ptr, driver_ptr); + goto next_driver; + } + } + + jack_error("ignoring settings for unknown driver \"%s\"", driver_name); + + next_driver: + ; + } + +free_xpath_obj: + xmlXPathFreeObject(xpath_obj_ptr); + +exit: + return; +} + +void +jack_controller_settings_load( + struct jack_controller * controller_ptr) +{ + gchar *filename; + xmlDocPtr doc_ptr; + xmlXPathContextPtr xpath_ctx_ptr; + + filename = g_strdup_printf("%s" JACKDBUS_CONF, g_jackdbus_dir); + if (filename == NULL) + { + jack_error("Out of memory."); + goto exit; + } + + jack_info("loading settings from \"%s\"", filename); + + doc_ptr = xmlParseFile(filename); + if (doc_ptr == NULL) + { + jack_error("Failed to parse playlist \"%s\"", filename); + goto free_filename; + } + + /* Create xpath evaluation context */ + xpath_ctx_ptr = xmlXPathNewContext(doc_ptr); + if (xpath_ctx_ptr == NULL) + { + jack_error("Unable to create new XPath context"); + goto free_doc; + } + + jack_controller_settings_read_engine(controller_ptr, xpath_ctx_ptr); + jack_controller_settings_read_drivers(controller_ptr, xpath_ctx_ptr); + + xmlXPathFreeContext(xpath_ctx_ptr); + +free_doc: + xmlFreeDoc(doc_ptr); + +free_filename: + g_free(filename); + +exit: + return; +} + +void +jack_controller_settings_save_auto( + struct jack_controller * controller_ptr) +{ + jack_controller_settings_save(controller_ptr, NULL); +} + +#else /* HAVE_LIBXML2 */ + +gboolean +jack_controller_settings_save( + struct jack_controller * controller_ptr, + GError **error) +{ + jack_error("jackdbus compiled without settings persistence"); + return TRUE; +} + +void +jack_controller_settings_load( + struct jack_controller * controller_ptr) +{ +} + +void +jack_controller_settings_save_auto( + struct jack_controller * controller_ptr) +{ +} + +#endif + + +void +jack_controller_run( + JackController * object_ptr) +{ + struct jack_controller * controller_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + controller_ptr->loop = g_main_loop_new(NULL, FALSE); + controller_ptr->manually_started = FALSE; + + if (!jack_drivers_load(controller_ptr)) + { + return; + } + + jack_controller_settings_load(controller_ptr); + + jack_info("Starting loop..."); + g_main_loop_run(controller_ptr->loop); + + if (controller_ptr->engine != NULL) + { + jack_controller_stop_server(object_ptr, NULL); + + } +} + +gboolean +jack_controller_start_server( + JackController * object_ptr, + GError ** error) +{ + struct jack_controller * controller_ptr; + int rc; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + jack_info("Starting jack server..."); + + if (controller_ptr->engine != NULL) + { + jack_info("Already started."); + return TRUE; + } + + if (controller_ptr->driver_ptr == NULL) + { + jack_controller_error(error, "Select driver first!"); + goto fail; + } + + rc = jack_register_server(controller_ptr->server_name); + switch (rc) + { + case EEXIST: + jack_controller_error(error, "`%s' server already active", controller_ptr->server_name); + goto fail; + case ENOSPC: + jack_controller_error(error, "too many servers already active"); + goto fail; + case ENOMEM: + jack_controller_error(error, "no access to shm registry"); + goto fail; + } + + if (controller_ptr->verbose) + { + jack_info ("server `%s' registered", controller_ptr->server_name); + } + + /* clean up shared memory and files from any previous + * instance of this server name */ + jack_cleanup_shm(); + jack_cleanup_files(controller_ptr->server_name); + + if (!controller_ptr->realtime && controller_ptr->client_timeout == 0) + controller_ptr->client_timeout = 500; /* 0.5 sec; usable when non realtime. */ + + /* get the engine/driver started */ + + controller_ptr->engine = jack_engine_new( + controller_ptr->realtime, + controller_ptr->realtime_priority, + controller_ptr->do_mlock, + controller_ptr->do_unlock, + controller_ptr->server_name, + controller_ptr->temporary, + controller_ptr->verbose, + controller_ptr->client_timeout, + controller_ptr->port_max, + getpid(), + controller_ptr->frame_time_offset, + controller_ptr->nozombies, + NULL); + if (controller_ptr->engine == NULL) + { + jack_controller_error(error, "Cannot create engine!"); + goto fail_unregister_server; + } + + jack_info("loading driver \"%s\" ...", controller_ptr->driver_ptr->desc_ptr->name); + + if (jack_engine_load_driver(controller_ptr->engine, controller_ptr->driver_ptr->desc_ptr, controller_ptr->driver_ptr->params)) { + jack_controller_error(error, "cannot load driver module %s", controller_ptr->driver_ptr->desc_ptr->name); + goto fail_delete_engine; + } + + if (controller_ptr->engine->driver->start(controller_ptr->engine->driver) != 0) { + jack_controller_error(error, "cannot start \"%s\" driver", controller_ptr->driver_ptr->desc_ptr->name); + goto fail_delete_engine; + } + + return TRUE; + +fail_unregister_server: + if (controller_ptr->verbose) + { + jack_info("cleaning up shared memory"); + } + + jack_cleanup_shm(); + + if (controller_ptr->verbose) + { + jack_info("cleaning up files"); + } + + jack_cleanup_files(controller_ptr->server_name); + + if (controller_ptr->verbose) + { + jack_info("unregistering server `%s'", controller_ptr->server_name); + } + + jack_unregister_server(controller_ptr->server_name); + +fail_delete_engine: + jack_engine_delete(controller_ptr->engine); + controller_ptr->engine = NULL; + +fail: + return FALSE; +} + +gboolean +jack_controller_stop_server( + JackController * object_ptr, + GError ** error) +{ + struct jack_controller * controller_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (controller_ptr->engine == NULL) + { + return TRUE; + } + + jack_info("Stop jack server..."); + + jack_engine_delete(controller_ptr->engine); + + /* clean up shared memory and files from this server instance */ + if (controller_ptr->verbose) + { + jack_info("cleaning up shared memory"); + } + + jack_cleanup_shm(); + + if (controller_ptr->verbose) + { + jack_info("cleaning up files"); + } + + jack_cleanup_files(controller_ptr->server_name); + + if (controller_ptr->verbose) + { + jack_info("unregistering server `%s'", controller_ptr->server_name); + } + + jack_unregister_server(controller_ptr->server_name); + + controller_ptr->engine = NULL; + + return TRUE; +} + +gboolean +jack_controller_get_available_drivers( + JackController * object_ptr, + gchar *** drivers_list, + GError ** error) +{ + struct jack_controller * controller_ptr; + gchar ** array; + unsigned int i; + struct jack_controller_driver * driver_ptr; + JSList * node_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + array = g_malloc((controller_ptr->drivers_count + 1) * sizeof(gchar *)); + + node_ptr = controller_ptr->drivers; + i = 0; + + while (node_ptr != NULL) + { + assert(i < controller_ptr->drivers_count); + + driver_ptr = (struct jack_controller_driver *)node_ptr->data; + + array[i] = g_strdup(driver_ptr->desc_ptr->name); + + node_ptr = jack_slist_next(node_ptr); + i++; + } + + assert(i == controller_ptr->drivers_count); + array[i] = NULL; + + *drivers_list = array; + + return TRUE; +} + +gboolean +jack_controller_get_selected_driver( + JackController * object_ptr, + gchar ** driver_name, + GError ** error) +{ + struct jack_controller * controller_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (controller_ptr->driver_ptr == NULL) + { + *driver_name = NULL; + } + else + { + *driver_name = g_strdup(controller_ptr->driver_ptr->desc_ptr->name); + } + + return TRUE; +} + +gboolean +jack_controller_select_driver( + JackController * object_ptr, + const gchar * driver_name, + GError ** error) +{ + struct jack_controller * controller_ptr; + struct jack_controller_driver * driver_ptr; + JSList * node_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + for (node_ptr = controller_ptr->drivers ; node_ptr != NULL ; node_ptr = jack_slist_next(node_ptr)) + { + driver_ptr = (struct jack_controller_driver *)node_ptr->data; + + if (strcmp(driver_ptr->desc_ptr->name, driver_name) == 0) { + goto found; + } + } + + jack_controller_error(error, "unknown driver '%s'", driver_name); + return FALSE; + +found: + jack_info("driver \"%s\" selected", driver_ptr->desc_ptr->name); + + controller_ptr->driver_ptr = driver_ptr; + + jack_controller_settings_save_auto(controller_ptr); + + return TRUE; +} + +gboolean +jack_controller_get_driver_parameter_names( + JackController * object_ptr, + gchar *** params_list, + GError ** error) +{ + struct jack_controller * controller_ptr; + gchar ** array; + unsigned int i; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (controller_ptr->driver_ptr == NULL) + { + jack_controller_error(error, "Select driver before operating on its parameters!"); + return FALSE; + } + + if (controller_ptr->verbose) + { + jack_info("Getting parameters for driver \"%s\"", controller_ptr->driver_ptr->desc_ptr->name); + } + + array = g_malloc((controller_ptr->driver_ptr->desc_ptr->nparams + 1) * sizeof(gchar *)); + + for (i = 0; i < controller_ptr->driver_ptr->desc_ptr->nparams ; i++) + { + array[i] = g_strdup(controller_ptr->driver_ptr->desc_ptr->params[i].name); + } + + array[i] = NULL; + + *params_list = array; + + return TRUE; +} + +gboolean +jack_controller_get_driver_parameter_short_description( + JackController * object_ptr, + const gchar * parameter, + gchar ** description, + GError ** error) +{ + struct jack_controller * controller_ptr; + unsigned int i; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (controller_ptr->driver_ptr == NULL) + { + jack_controller_error(error, "Select driver before operating on its parameters!"); + return FALSE; + } + + for (i = 0; i < controller_ptr->driver_ptr->desc_ptr->nparams ; i++) + { + if (strcmp(controller_ptr->driver_ptr->desc_ptr->params[i].name, parameter) == 0) + { + *description = g_strdup(controller_ptr->driver_ptr->desc_ptr->params[i].short_desc); + return TRUE; + } + } + + jack_controller_error(error, "Unknown parameter \"%s\" of driver \"%s\"", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; +} + +gboolean +jack_controller_get_driver_parameter_long_description( + JackController * object_ptr, + const gchar * parameter, + gchar ** description, + GError ** error) +{ + struct jack_controller * controller_ptr; + unsigned int i; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (controller_ptr->driver_ptr == NULL) + { + jack_controller_error(error, "Select driver before operating on its parameters!"); + return FALSE; + } + + for (i = 0; i < controller_ptr->driver_ptr->desc_ptr->nparams ; i++) + { + if (strcmp(controller_ptr->driver_ptr->desc_ptr->params[i].name, parameter) == 0) + { + *description = g_strdup(controller_ptr->driver_ptr->desc_ptr->params[i].long_desc); + return TRUE; + } + } + + jack_controller_error(error, "Unknown parameter \"%s\" of driver \"%s\"", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; +} + +gboolean +jack_controller_get_driver_parameter_type( + JackController * object_ptr, + const gchar * parameter, + guchar * type, + GError ** error) +{ + struct jack_controller * controller_ptr; + unsigned int i; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (controller_ptr->driver_ptr == NULL) + { + jack_controller_error(error, "Select driver before operating on its parameters!"); + return FALSE; + } + + for (i = 0; i < controller_ptr->driver_ptr->desc_ptr->nparams ; i++) + { + if (strcmp(controller_ptr->driver_ptr->desc_ptr->params[i].name, parameter) == 0) + { + *type = controller_ptr->driver_ptr->desc_ptr->params[i].type; + return TRUE; + } + } + + jack_controller_error(error, "Unknown parameter \"%s\" of driver \"%s\"", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; +} + +gboolean +jack_controller_get_driver_parameter_type_string( + JackController * object_ptr, + const gchar * parameter, + gchar ** type, + GError ** error) +{ + struct jack_controller * controller_ptr; + unsigned int i; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (controller_ptr->driver_ptr == NULL) + { + jack_controller_error(error, "Select driver before operating on its parameters!"); + return FALSE; + } + + for (i = 0; i < controller_ptr->driver_ptr->desc_ptr->nparams ; i++) + { + if (strcmp(controller_ptr->driver_ptr->desc_ptr->params[i].name, parameter) == 0) + { + switch (controller_ptr->driver_ptr->desc_ptr->params[i].type) + { + case JackDriverParamInt: + *type = g_strdup("sint"); + return TRUE; + case JackDriverParamUInt: + *type = g_strdup("uint"); + return TRUE; + case JackDriverParamChar: + *type = g_strdup("char"); + return TRUE; + case JackDriverParamString: + *type = g_strdup("str"); + return TRUE; + case JackDriverParamBool: + *type = g_strdup("bool"); + return TRUE; + } + + jack_controller_error( + error, + "Parameter \"%s\" of driver \"%s\" is of unknown type %d", + parameter, + controller_ptr->driver_ptr->desc_ptr->name, + controller_ptr->driver_ptr->desc_ptr->params[i].type); + return FALSE; + } + } + + jack_controller_error(error, "Unknown parameter \"%s\" of driver \"%s\"", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; +} + +gboolean +jack_controller_get_driver_parameter_value_string( + JackController * object_ptr, + const gchar * parameter, + gboolean * isset, + gchar ** default_value, + gchar ** value, + GError ** error) +{ + struct jack_controller * controller_ptr; + jack_driver_param_desc_t * param_desc_ptr; + jack_driver_param_t * param_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (controller_ptr->driver_ptr == NULL) + { + jack_controller_error(error, "Select driver before operating on its parameters!"); + return FALSE; + } + + param_desc_ptr = jack_controller_find_parameter_descriptor_by_name_current_driver(controller_ptr, parameter); + if (param_desc_ptr == NULL) + { + jack_controller_error(error, "Unknown parameter \"%s\" of driver \"%s\"", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + if (param_desc_ptr->type != JackDriverParamString) + { + jack_controller_error(error, "Parameter \"%s\" of driver \"%s\" is not string", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + *default_value = g_strdup(param_desc_ptr->value.str); + + param_ptr = jack_controller_find_parameter_current_driver(controller_ptr, param_desc_ptr); + if (param_ptr == NULL) + { + *isset = FALSE; + *value = g_strdup(*default_value); + } + else + { + *isset = TRUE; + *value = g_strdup(param_ptr->value.str); + } + + return TRUE; +} + +gboolean +jack_controller_get_driver_parameter_value_bool( + JackController * object_ptr, + const gchar * parameter, + gboolean * isset, + gboolean * default_value, + gboolean * value, + GError ** error) +{ + struct jack_controller * controller_ptr; + jack_driver_param_desc_t * param_desc_ptr; + jack_driver_param_t * param_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (controller_ptr->driver_ptr == NULL) + { + jack_controller_error(error, "Select driver before operating on its parameters!"); + return FALSE; + } + + param_desc_ptr = jack_controller_find_parameter_descriptor_by_name_current_driver(controller_ptr, parameter); + if (param_desc_ptr == NULL) + { + jack_controller_error(error, "Unknown parameter \"%s\" of driver \"%s\"", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + if (param_desc_ptr->type != JackDriverParamBool) + { + jack_controller_error(error, "Parameter \"%s\" of driver \"%s\" is not boolean", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + *default_value = param_desc_ptr->value.i ? TRUE : FALSE; + + param_ptr = jack_controller_find_parameter_current_driver(controller_ptr, param_desc_ptr); + if (param_ptr == NULL) + { + *isset = FALSE; + *value = *default_value; + } + else + { + *isset = TRUE; + *value = param_ptr->value.i ? TRUE : FALSE; + } + + return TRUE; +} + +gboolean +jack_controller_get_driver_parameter_value_char( + JackController * object_ptr, + const gchar * parameter, + gboolean * isset, + guchar * default_value, + guchar * value, + GError ** error) +{ + struct jack_controller * controller_ptr; + jack_driver_param_desc_t * param_desc_ptr; + jack_driver_param_t * param_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (controller_ptr->driver_ptr == NULL) + { + jack_controller_error(error, "Select driver before operating on its parameters!"); + return FALSE; + } + + param_desc_ptr = jack_controller_find_parameter_descriptor_by_name_current_driver(controller_ptr, parameter); + if (param_desc_ptr == NULL) + { + jack_controller_error(error, "Unknown parameter \"%s\" of driver \"%s\"", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + if (param_desc_ptr->type != JackDriverParamChar) + { + jack_controller_error(error, "Parameter \"%s\" of driver \"%s\" is not char", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + *default_value = param_desc_ptr->value.c; + + param_ptr = jack_controller_find_parameter_current_driver(controller_ptr, param_desc_ptr); + if (param_ptr == NULL) + { + *isset = FALSE; + *value = *default_value; + } + else + { + *isset = TRUE; + *value = param_ptr->value.c; + } + + return TRUE; +} + +gboolean +jack_controller_get_driver_parameter_value_int( + JackController * object_ptr, + const gchar * parameter, + gboolean * isset, + gint * default_value, + gint * value, + GError ** error) +{ + struct jack_controller * controller_ptr; + jack_driver_param_desc_t * param_desc_ptr; + jack_driver_param_t * param_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (controller_ptr->driver_ptr == NULL) + { + jack_controller_error(error, "Select driver before operating on its parameters!"); + return FALSE; + } + + param_desc_ptr = jack_controller_find_parameter_descriptor_by_name_current_driver(controller_ptr, parameter); + if (param_desc_ptr == NULL) + { + jack_controller_error(error, "Unknown parameter \"%s\" of driver \"%s\"", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + if (param_desc_ptr->type != JackDriverParamInt) + { + jack_controller_error(error, "Parameter \"%s\" of driver \"%s\" is not int", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + *default_value = param_desc_ptr->value.i; + + param_ptr = jack_controller_find_parameter_current_driver(controller_ptr, param_desc_ptr); + if (param_ptr == NULL) + { + *isset = FALSE; + *value = *default_value; + } + else + { + *isset = TRUE; + *value = param_ptr->value.i; + } + + return TRUE; +} + +gboolean +jack_controller_get_driver_parameter_value_uint( + JackController * object_ptr, + const gchar * parameter, + gboolean * isset, + guint * default_value, + guint * value, + GError ** error) +{ + struct jack_controller * controller_ptr; + jack_driver_param_desc_t * param_desc_ptr; + jack_driver_param_t * param_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (controller_ptr->driver_ptr == NULL) + { + jack_controller_error(error, "Select driver before operating on its parameters!"); + return FALSE; + } + + param_desc_ptr = jack_controller_find_parameter_descriptor_by_name_current_driver(controller_ptr, parameter); + if (param_desc_ptr == NULL) + { + jack_controller_error(error, "Unknown parameter \"%s\" of driver \"%s\"", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + if (param_desc_ptr->type != JackDriverParamUInt) + { + jack_controller_error(error, "Parameter \"%s\" of driver \"%s\" is not uint", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + *default_value = param_desc_ptr->value.ui; + + param_ptr = jack_controller_find_parameter_current_driver(controller_ptr, param_desc_ptr); + if (param_ptr == NULL) + { + *isset = FALSE; + *value = *default_value; + } + else + { + *isset = TRUE; + *value = param_ptr->value.ui; + } + + return TRUE; +} + +gboolean +jack_controller_set_driver_parameter_value_string( + JackController * object_ptr, + const gchar * parameter, + gchar * value, + GError ** error) +{ + struct jack_controller * controller_ptr; + jack_driver_param_desc_t * param_desc_ptr; + jack_driver_param_t * param_ptr; + size_t value_len; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (controller_ptr->driver_ptr == NULL) + { + jack_controller_error(error, "Select driver before operating on its parameters!"); + return FALSE; + } + + param_desc_ptr = jack_controller_find_parameter_descriptor_by_name_current_driver(controller_ptr, parameter); + if (param_desc_ptr == NULL) + { + jack_controller_error(error, "Unknown parameter \"%s\" of driver \"%s\"", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + if (param_desc_ptr->type != JackDriverParamString) + { + jack_controller_error(error, "Parameter \"%s\" of driver \"%s\" is not string", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + value_len = strlen(value); + + if (value_len > JACK_DRIVER_PARAM_STRING_MAX) + { + jack_controller_error( + error, + "Max size of string parameter values is %u chars but value supplied is %u chars", + JACK_DRIVER_PARAM_STRING_MAX, + value_len); + return FALSE; + } + + jack_info("Setting parameter \"%s\" of driver \"%s\" to \"%s\"", parameter, controller_ptr->driver_ptr->desc_ptr->name, value); + + param_ptr = jack_controller_find_or_create_parameter_current_driver(controller_ptr, param_desc_ptr); + if (param_ptr == NULL) + { + jack_controller_error(error, "driver parameter creation failed"); + return FALSE; + } + + memcpy(param_ptr->value.str, value, value_len + 1); + + jack_controller_settings_save_auto(controller_ptr); + + return TRUE; +} + +gboolean +jack_controller_set_driver_parameter_value_bool( + JackController * object_ptr, + const gchar * parameter, + gboolean value, + GError ** error) +{ + struct jack_controller * controller_ptr; + jack_driver_param_desc_t * param_desc_ptr; + jack_driver_param_t * param_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (controller_ptr->driver_ptr == NULL) + { + jack_controller_error(error, "Select driver before operating on its parameters!"); + return FALSE; + } + + param_desc_ptr = jack_controller_find_parameter_descriptor_by_name_current_driver(controller_ptr, parameter); + if (param_desc_ptr == NULL) + { + jack_controller_error(error, "Unknown parameter \"%s\" of driver \"%s\"", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + if (param_desc_ptr->type != JackDriverParamBool) + { + jack_controller_error(error, "Parameter \"%s\" of driver \"%s\" is not boolean", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + jack_info("Setting parameter \"%s\" of driver \"%s\" to %s", parameter, controller_ptr->driver_ptr->desc_ptr->name, value ? "true" : "false"); + + param_ptr = jack_controller_find_or_create_parameter_current_driver(controller_ptr, param_desc_ptr); + if (param_ptr == NULL) + { + jack_controller_error(error, "driver parameter creation failed"); + return FALSE; + } + + param_ptr->value.i = value ? TRUE : FALSE; + + jack_controller_settings_save_auto(controller_ptr); + + return TRUE; +} + +gboolean +jack_controller_set_driver_parameter_value_char( + JackController * object_ptr, + const gchar * parameter, + guchar value, + GError ** error) +{ + struct jack_controller * controller_ptr; + jack_driver_param_desc_t * param_desc_ptr; + jack_driver_param_t * param_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (controller_ptr->driver_ptr == NULL) + { + jack_controller_error(error, "Select driver before operating on its parameters!"); + return FALSE; + } + + param_desc_ptr = jack_controller_find_parameter_descriptor_by_name_current_driver(controller_ptr, parameter); + if (param_desc_ptr == NULL) + { + jack_controller_error(error, "Unknown parameter \"%s\" of driver \"%s\"", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + if (param_desc_ptr->type != JackDriverParamChar) + { + jack_controller_error(error, "Parameter \"%s\" of driver \"%s\" is not char", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + jack_info("Setting parameter \"%s\" of driver \"%s\" to %c", parameter, controller_ptr->driver_ptr->desc_ptr->name, value); + + param_ptr = jack_controller_find_or_create_parameter_current_driver(controller_ptr, param_desc_ptr); + if (param_ptr == NULL) + { + jack_controller_error(error, "driver parameter creation failed"); + return FALSE; + } + + param_ptr->value.c = value; + + jack_controller_settings_save_auto(controller_ptr); + + return TRUE; +} + +gboolean +jack_controller_set_driver_parameter_value_int( + JackController * object_ptr, + const gchar * parameter, + gint value, + GError ** error) +{ + struct jack_controller * controller_ptr; + jack_driver_param_desc_t * param_desc_ptr; + jack_driver_param_t * param_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (controller_ptr->driver_ptr == NULL) + { + jack_controller_error(error, "Select driver before operating on its parameters!"); + return FALSE; + } + + param_desc_ptr = jack_controller_find_parameter_descriptor_by_name_current_driver(controller_ptr, parameter); + if (param_desc_ptr == NULL) + { + jack_controller_error(error, "Unknown parameter \"%s\" of driver \"%s\"", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + if (param_desc_ptr->type != JackDriverParamInt) + { + jack_controller_error(error, "Parameter \"%s\" of driver \"%s\" is not sint", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + jack_info("Setting parameter \"%s\" of driver \"%s\" to %d", parameter, controller_ptr->driver_ptr->desc_ptr->name, value); + + param_ptr = jack_controller_find_or_create_parameter_current_driver(controller_ptr, param_desc_ptr); + if (param_ptr == NULL) + { + jack_controller_error(error, "driver parameter creation failed"); + return FALSE; + } + + param_ptr->value.i = value; + + jack_controller_settings_save_auto(controller_ptr); + + return TRUE; +} + +gboolean +jack_controller_set_driver_parameter_value_uint( + JackController * object_ptr, + const gchar * parameter, + guint value, + GError ** error) +{ + struct jack_controller * controller_ptr; + jack_driver_param_desc_t * param_desc_ptr; + jack_driver_param_t * param_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (controller_ptr->driver_ptr == NULL) + { + jack_controller_error(error, "Select driver before operating on its parameters!"); + return FALSE; + } + + param_desc_ptr = jack_controller_find_parameter_descriptor_by_name_current_driver(controller_ptr, parameter); + if (param_desc_ptr == NULL) + { + jack_controller_error(error, "Unknown parameter \"%s\" of driver \"%s\"", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + if (param_desc_ptr->type != JackDriverParamUInt) + { + jack_controller_error(error, "Parameter \"%s\" of driver \"%s\" is not uint", parameter, controller_ptr->driver_ptr->desc_ptr->name); + return FALSE; + } + + jack_info("Setting parameter \"%s\" of driver \"%s\" to %u", parameter, controller_ptr->driver_ptr->desc_ptr->name, value); + + param_ptr = jack_controller_find_or_create_parameter_current_driver(controller_ptr, param_desc_ptr); + if (param_ptr == NULL) + { + jack_controller_error(error, "driver parameter creation failed"); + return FALSE; + } + + param_ptr->value.ui = value; + + jack_controller_settings_save_auto(controller_ptr); + + return TRUE; +} + +gboolean +jack_controller_get_engine_parameter_names( + JackController * object_ptr, + gchar *** params_list, + GError ** error) +{ + struct jack_controller * controller_ptr; + gchar **array; + unsigned int i; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (controller_ptr->verbose) + { + jack_info("Getting engine parameters"); + } + + array = g_malloc((JACK_ENGINE_PARAMS_COUNT + 1) * sizeof(gchar *)); + + for (i = 0; i < JACK_ENGINE_PARAMS_COUNT ; i++) + { + array[i] = g_strdup(jack_engine_parameters[i].name); + } + + array[i] = NULL; + + *params_list = array; + + return TRUE; +} + +gboolean +jack_controller_get_engine_parameter_short_description( + JackController * object_ptr, + const gchar * parameter, + gchar ** description, + GError ** error) +{ + unsigned int i; + + for (i = 0; i < JACK_ENGINE_PARAMS_COUNT ; i++) + { + if (strcmp(jack_engine_parameters[i].name, parameter) == 0) + { + *description = g_strdup(jack_engine_parameters[i].description_short); + return TRUE; + } + } + + jack_controller_error(error, "Unknown engine parameter \"%s\"", parameter); + return FALSE; +} + +gboolean +jack_controller_get_engine_parameter_long_description( + JackController * object_ptr, + const gchar * parameter, + gchar ** description, + GError ** error) +{ + unsigned int i; + + for (i = 0; i < JACK_ENGINE_PARAMS_COUNT ; i++) + { + if (strcmp(jack_engine_parameters[i].name, parameter) == 0) + { + if (jack_engine_parameters[i].description_long == NULL) + { + *description = g_strdup(jack_engine_parameters[i].description_short); + } + else + { + *description = g_strdup(jack_engine_parameters[i].description_long); + } + + return TRUE; + } + } + + jack_controller_error(error, "Unknown engine parameter \"%s\"", parameter); + return FALSE; +} + +gboolean +jack_controller_get_engine_parameter_type( + JackController * object_ptr, + const gchar * parameter, + guchar * type, + GError ** error) +{ + unsigned int i; + + for (i = 0; i < JACK_ENGINE_PARAMS_COUNT ; i++) + { + if (strcmp(jack_engine_parameters[i].name, parameter) == 0) + { + *type = jack_engine_parameters[i].type; + return TRUE; + } + } + + jack_controller_error(error, "Unknown engine parameter \"%s\"", parameter); + return FALSE; +} + +gboolean +jack_controller_get_engine_parameter_type_string( + JackController * object_ptr, + const gchar * parameter, + gchar ** type, + GError ** error) +{ + unsigned int i; + + for (i = 0; i < JACK_ENGINE_PARAMS_COUNT ; i++) + { + if (strcmp(jack_engine_parameters[i].name, parameter) == 0) + { + switch (jack_engine_parameters[i].type) + { + case JackParamInt: + *type = g_strdup("sint"); + return TRUE; + case JackParamUInt: + *type = g_strdup("uint"); + return TRUE; + case JackParamChar: + *type = g_strdup("char"); + return TRUE; + case JackParamString: + *type = g_strdup("str"); + return TRUE; + case JackParamBool: + *type = g_strdup("bool"); + return TRUE; + } + + jack_controller_error( + error, + "Engine parameter \"%s\" is of unknown type %d", + parameter, + jack_engine_parameters[i].type); + return FALSE; + } + } + + jack_controller_error(error, "Unknown engine parameter \"%s\"", parameter); + return FALSE; +} + +gboolean +jack_controller_get_engine_parameter_value_string( + JackController * object_ptr, + const gchar * parameter, + gchar ** value, + GError ** error) +{ + jack_controller_error(error, "Unknown engine string parameter \"%s\"", parameter); + return FALSE; +} + +gboolean +jack_controller_set_engine_parameter_value_string( + JackController * object_ptr, + const gchar * parameter, + gchar * value, + GError ** error) +{ + jack_controller_error(error, "Unknown engine string parameter \"%s\"", parameter); + return FALSE; +} + +gboolean +jack_controller_get_engine_parameter_value_bool( + JackController * object_ptr, + const gchar * parameter, + gboolean * value, + GError ** error) +{ + struct jack_controller * controller_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (strcmp(parameter, "realtime") == 0) + { + *value = controller_ptr->realtime ? TRUE : FALSE; + return TRUE; + } + + if (strcmp(parameter, "no-mem-lock") == 0) + { + *value = controller_ptr->do_mlock ? FALSE : TRUE; /* reverse logic */ + return TRUE; + } + + if (strcmp(parameter, "temporary") == 0) + { + *value = controller_ptr->temporary ? TRUE : FALSE; + return TRUE; + } + + if (strcmp(parameter, "verbose") == 0) + { + *value = controller_ptr->verbose ? TRUE : FALSE; + return TRUE; + } + + if (strcmp(parameter, "no-zombies") == 0) + { + *value = controller_ptr->nozombies ? TRUE : FALSE; + return TRUE; + } + + if (strcmp(parameter, "libs-unlock") == 0) + { + *value = controller_ptr->do_unlock ? TRUE : FALSE; + return TRUE; + } + + jack_controller_error(error, "Unknown engine boolean parameter \"%s\"", parameter); + return FALSE; +} + +gboolean +jack_controller_set_engine_parameter_value_bool( + JackController * object_ptr, + const gchar * parameter, + gboolean value, + GError ** error) +{ + struct jack_controller * controller_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (controller_ptr->verbose) + { + jack_info("Setting boolean parameter \"%s\" to %s", parameter, value ? "true" : "false"); + } + + if (strcmp(parameter, "realtime") == 0) + { + controller_ptr->realtime = value; + goto save; + } + + if (strcmp(parameter, "no-mem-lock") == 0) + { + controller_ptr->do_mlock = !value; /* reverse logic */ + goto save; + } + + if (strcmp(parameter, "temporary") == 0) + { + controller_ptr->temporary = value; + goto save; + } + + if (strcmp(parameter, "verbose") == 0) + { + if (!controller_ptr->verbose && value) + { + jack_info("Setting boolean parameter \"%s\" to %s", parameter, value ? "true" : "false"); + } + + controller_ptr->verbose = value; + goto save; + } + + if (strcmp(parameter, "no-zombies") == 0) + { + controller_ptr->nozombies = value; + goto save; + } + + if (strcmp(parameter, "libs-unlock") == 0) + { + controller_ptr->do_unlock = value; + goto save; + } + + jack_controller_error(error, "Unknown engine boolean parameter \"%s\"", parameter); + return FALSE; + +save: + jack_controller_settings_save_auto(controller_ptr); + return TRUE; +} + +gboolean +jack_controller_get_engine_parameter_value_char( + JackController * object_ptr, + const gchar * parameter, + guchar * value, + GError ** error) +{ + jack_controller_error(error, "Unknown engine char parameter \"%s\"", parameter); + return FALSE; +} + +gboolean +jack_controller_set_engine_parameter_value_char( + JackController * object_ptr, + const gchar * parameter, + guchar value, + GError ** error) +{ + jack_controller_error(error, "Unknown engine char parameter \"%s\"", parameter); + return FALSE; +} + +gboolean +jack_controller_get_engine_parameter_value_int( + JackController * object_ptr, + const gchar * parameter, + gint * value, + GError ** error) +{ + struct jack_controller * controller_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (strcmp(parameter, "realtime-priority") == 0) + { + *value = controller_ptr->realtime_priority; + return TRUE; + } + + if (strcmp(parameter, "client-timeout") == 0) + { + *value = controller_ptr->client_timeout; + return TRUE; + } + + jack_controller_error(error, "Unknown engine sint parameter \"%s\"", parameter); + return FALSE; +} + +gboolean +jack_controller_set_engine_parameter_value_int( + JackController * object_ptr, + const gchar * parameter, + gint value, + GError ** error) +{ + struct jack_controller * controller_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (strcmp(parameter, "realtime-priority") == 0) + { + controller_ptr->realtime_priority = value; + goto save; + } + + if (strcmp(parameter, "client-timeout") == 0) + { + controller_ptr->client_timeout = value; + goto save; + } + + jack_controller_error(error, "Unknown engine sint parameter \"%s\"", parameter); + return FALSE; + +save: + jack_controller_settings_save_auto(controller_ptr); + return TRUE; +} + +gboolean +jack_controller_get_engine_parameter_value_uint( + JackController * object_ptr, + const gchar * parameter, + guint * value, + GError ** error) +{ + struct jack_controller * controller_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (strcmp(parameter, "port-max") == 0) + { + *value = controller_ptr->port_max; + return TRUE; + } + + jack_controller_error(error, "Unknown engine uint parameter \"%s\"", parameter); + return FALSE; +} + +gboolean +jack_controller_set_engine_parameter_value_uint( + JackController * object_ptr, + const gchar * parameter, + guint value, + GError ** error) +{ + struct jack_controller * controller_ptr; + + controller_ptr = JACK_CONTROLLER_GET_PRIVATE(object_ptr); + + if (strcmp(parameter, "port-max") == 0) + { + controller_ptr->port_max = value; + goto save; + } + + jack_controller_error(error, "Unknown engine uint parameter \"%s\"", parameter); + return FALSE; + +save: + jack_controller_settings_save_auto(controller_ptr); + return TRUE; +} diff --git a/jackd/jackcontroller.h b/jackd/jackcontroller.h new file mode 100644 --- /dev/null +++ b/jackd/jackcontroller.h @@ -0,0 +1,324 @@ +/* -*- mode: c; c-file-style: "bsd"; -*- */ +/* + Copyright (C) 2007 Nedko Arnaudov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License. + + This program 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 General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef JACKCONTROLLER_H__2CC80B1E_8D5D_45E3_A9D8_9086DDF68BB5__INCLUDED +#define JACKCONTROLLER_H__2CC80B1E_8D5D_45E3_A9D8_9086DDF68BB5__INCLUDED + +G_BEGIN_DECLS + +#define JACK_CONTROLLER_TYPE (jack_controller_get_type()) +#define JACK_CONTROLLER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), JACK_CONTROLLER_TYPE, JackController)) +#define JACK_CONTROLLER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), JACK_CONTROLLER_TYPE, JackControllerClass)) +#define JACK_IS_CONTROLLER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), JACK_CONTROLLER_TYPE)) +#define JACK_IS_CONTROLLER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), JACK_CONTROLLER_TYPE)) +#define JACK_CONTROLLER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), JACK_CONTROLLER_TYPE, JackControllerClass)) + +#define JACK_TYPE_CONTROLLER JACK_CONTROLLER_TYPE + +typedef struct _JackController JackController; +typedef struct _JackControllerClass JackControllerClass; + +struct _JackController { + GObject parent; + /* instance members */ +}; + +struct _JackControllerClass { + GObjectClass parent; + /* class members */ +}; + +/* used by JACK_CONTROLLER_TYPE */ +GType jack_controller_get_type(); + +void +jack_controller_run( + JackController * object_ptr); + +gboolean +jack_controller_test( + JackController * object_ptr, + GError ** error); + +gboolean +jack_controller_exit( + JackController * object_ptr, + GError ** error); + +gboolean +jack_controller_is_manually_activated( + JackController * object_ptr, + gboolean * manually_started_ptr, + GError ** error); + +gboolean +jack_controller_start_server( + JackController * object_ptr, + GError ** error); + +gboolean +jack_controller_stop_server( + JackController * object_ptr, + GError ** error); + +gboolean +jack_controller_is_manually_activated( + JackController * object_ptr, + gboolean * manually_started_ptr, + GError ** error); + +gboolean +jack_controller_get_available_drivers( + JackController * object_ptr, + gchar *** drivers_list, + GError ** error); + +gboolean +jack_controller_get_selected_driver( + JackController * object_ptr, + gchar ** driver_name, + GError ** error); + +gboolean +jack_controller_select_driver( + JackController * object_ptr, + const gchar * driver_name, + GError ** error); + +gboolean +jack_controller_get_driver_parameter_names( + JackController * object_ptr, + gchar *** params_list, + GError ** error); + +gboolean +jack_controller_get_driver_parameter_short_description( + JackController * object_ptr, + const gchar * parameter, + gchar ** description, + GError ** error); + +gboolean +jack_controller_get_driver_parameter_long_description( + JackController * object_ptr, + const gchar * parameter, + gchar ** description, + GError ** error); + +gboolean +jack_controller_get_driver_parameter_type( + JackController * object_ptr, + const gchar * parameter, + guchar * type, + GError ** error); + +gboolean +jack_controller_get_driver_parameter_type_string( + JackController * object_ptr, + const gchar * parameter, + gchar ** type, + GError ** error); + +gboolean +jack_controller_get_driver_parameter_value_string( + JackController * object_ptr, + const gchar * parameter, + gboolean * isset, + gchar ** default_value, + gchar ** value, + GError ** error); + +gboolean +jack_controller_get_driver_parameter_value_bool( + JackController * object_ptr, + const gchar * parameter, + gboolean * isset, + gboolean * default_value, + gboolean * value, + GError ** error); + +gboolean +jack_controller_get_driver_parameter_value_char( + JackController * object_ptr, + const gchar * parameter, + gboolean * isset, + guchar * default_value, + guchar * value, + GError ** error); + +gboolean +jack_controller_get_driver_parameter_value_int( + JackController * object_ptr, + const gchar * parameter, + gboolean * isset, + gint * default_value, + gint * value, + GError ** error); + +gboolean +jack_controller_get_driver_parameter_value_uint( + JackController * object_ptr, + const gchar * parameter, + gboolean * isset, + guint * default_value, + guint * value, + GError ** error); + +gboolean +jack_controller_set_driver_parameter_value_string( + JackController * object_ptr, + const gchar * parameter, + gchar * value, + GError ** error); + +gboolean +jack_controller_set_driver_parameter_value_bool( + JackController * object_ptr, + const gchar * parameter, + gboolean value, + GError ** error); + +gboolean +jack_controller_set_driver_parameter_value_char( + JackController * object_ptr, + const gchar * parameter, + guchar value, + GError ** error); + +gboolean +jack_controller_set_driver_parameter_value_int( + JackController * object_ptr, + const gchar * parameter, + gint value, + GError ** error); + +gboolean +jack_controller_set_driver_parameter_value_uint( + JackController * object_ptr, + const gchar * parameter, + guint value, + GError ** error); + +gboolean +jack_controller_get_engine_parameter_names( + JackController * object_ptr, + gchar *** params_list, + GError ** error); + +gboolean +jack_controller_get_engine_parameter_short_description( + JackController * object_ptr, + const gchar * parameter, + gchar ** description, + GError ** error); + +gboolean +jack_controller_get_engine_parameter_long_description( + JackController * object_ptr, + const gchar * parameter, + gchar ** description, + GError ** error); + +gboolean +jack_controller_get_engine_parameter_type( + JackController * object_ptr, + const gchar * parameter, + guchar * type, + GError ** error); + +gboolean +jack_controller_get_engine_parameter_type_string( + JackController * object_ptr, + const gchar * parameter, + gchar ** type, + GError ** error); + +gboolean +jack_controller_get_engine_parameter_value_string( + JackController * object_ptr, + const gchar * parameter, + gchar ** value, + GError ** error); + +gboolean +jack_controller_get_engine_parameter_value_bool( + JackController * object_ptr, + const gchar * parameter, + gboolean * value, + GError ** error); + +gboolean +jack_controller_get_engine_parameter_value_char( + JackController * object_ptr, + const gchar * parameter, + guchar * value, + GError ** error); + +gboolean +jack_controller_get_engine_parameter_value_int( + JackController * object_ptr, + const gchar * parameter, + gint * value, + GError ** error); + +gboolean +jack_controller_get_engine_parameter_value_uint( + JackController * object_ptr, + const gchar * parameter, + guint * value, + GError ** error); + +gboolean +jack_controller_set_engine_parameter_value_string( + JackController * object_ptr, + const gchar * parameter, + gchar * value, + GError ** error); + +gboolean +jack_controller_set_engine_parameter_value_bool( + JackController * object_ptr, + const gchar * parameter, + gboolean value, + GError ** error); + +gboolean +jack_controller_set_engine_parameter_value_char( + JackController * object_ptr, + const gchar * parameter, + guchar value, + GError ** error); + +gboolean +jack_controller_set_engine_parameter_value_int( + JackController * object_ptr, + const gchar * parameter, + gint value, + GError ** error); + +gboolean +jack_controller_set_engine_parameter_value_uint( + JackController * object_ptr, + const gchar * parameter, + guint value, + GError ** error); + +G_END_DECLS + +#endif /* #ifndef JACKCONTROLLER_H__2CC80B1E_8D5D_45E3_A9D8_9086DDF68BB5__INCLUDED */ diff --git a/jackd/jackcontroller.xml b/jackd/jackcontroller.xml new file mode 100644 --- /dev/null +++ b/jackd/jackcontroller.xml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jackd/jackdbus.c b/jackd/jackdbus.c new file mode 100644 --- /dev/null +++ b/jackd/jackdbus.c @@ -0,0 +1,345 @@ +/* -*- mode: c; c-file-style: "bsd"; -*- */ +/* + Copyright (C) 2007 Nedko Arnaudov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License. + + This program 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 General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_LIBXML2 +#include +#endif + +#include "jackdbus.h" +#include "jackcontroller.h" + +FILE *g_logfile; +gchar *g_jackdbus_dir; + +void +jack_dbus_info (const char *msg) +{ + time_t timestamp; + char timestamp_str[26]; + + time(×tamp); + ctime_r(×tamp, timestamp_str); + timestamp_str[24] = 0; + + fprintf (g_logfile, "%s: %s\n", timestamp_str, msg); + fflush(g_logfile); +} + +void +jack_dbus_error (const char *msg) +{ + time_t timestamp; + char timestamp_str[26]; + + time(×tamp); + ctime_r(×tamp, timestamp_str); + timestamp_str[24] = 0; + + fprintf (g_logfile, "%s: ERROR: %s\n", timestamp_str, msg); + fflush(g_logfile); +} + +gboolean +paths_init() +{ + const char *home_dir; + struct stat st; + + home_dir = getenv("HOME"); + if (home_dir == NULL) + { + g_printerr("Environment variable HOME not set\n"); + goto fail; + } + + g_jackdbus_dir = g_strdup_printf("%s" JACKDBUS_DIR, home_dir); + if (g_jackdbus_dir == NULL) + { + g_printerr("Out of memory\n"); + goto fail; + } + + if (stat(g_jackdbus_dir, &st) != 0) + { + if (errno == ENOENT) + { + g_print("Directory \"%s\" does not exist. Creating...\n", g_jackdbus_dir); + if (mkdir(g_jackdbus_dir, 0777) != 0) + { + g_printerr("Failed to create \"%s\" directory: %d (%s)\n", g_jackdbus_dir, errno, strerror(errno)); + goto fail_free; + } + } + else + { + g_printerr("Failed to stat \"%s\": %d (%s)\n", g_jackdbus_dir, errno, strerror(errno)); + goto fail_free; + } + } + else + { + if (!S_ISDIR(st.st_mode)) + { + g_printerr("\"%s\" exists but is not directory.\n", g_jackdbus_dir); + goto fail_free; + } + } + + return TRUE; + +fail_free: + g_free(g_jackdbus_dir); + +fail: + return FALSE; +} + +void +paths_uninit() +{ + g_free(g_jackdbus_dir); +} + +gboolean +log_init() +{ + gchar *log_filename; + + log_filename = g_strdup_printf("%s" JACKDBUS_LOG, g_jackdbus_dir); + if (log_filename == NULL) + { + g_printerr("Out of memory\n"); + return FALSE; + } + + g_logfile = fopen(log_filename, "a"); + if (g_logfile == NULL) + { + fprintf(stderr, "Cannot open jackdbus log file \"%s\": %d (%s)\n", log_filename, errno, strerror(errno)); + g_free(log_filename); + return FALSE; + } + + g_free(log_filename); + + return TRUE; +} + +void +log_uninit() +{ + fclose(g_logfile); +} + +static void +do_nothing_handler (int sig) +{ + /* this is used by the child (active) process, but it never + gets called unless we are already shutting down after + another signal. + */ + char buf[64]; + snprintf (buf, sizeof(buf), + "received signal %d during shutdown (ignored)\n", sig); + write (1, buf, strlen (buf)); +} + +int +main (int argc, char **argv) +{ + DBusGConnection *connection; + GError *error; + JackController *controller_ptr; + DBusGProxy *bus_proxy; + guint request_name_result; + int ret; + sigset_t signals; + sigset_t allsignals; + struct sigaction action; + int i; + +#if HAVE_LIBXML2 + /* + * this initialize the library and check potential ABI mismatches + * between the version it was compiled for and the actual shared + * library used. + */ + LIBXML_TEST_VERSION; +#endif + + if (argc != 2 || strcmp(argv[1], "auto") != 0) + { + ret = 0; + fprintf( + stderr, + "jackdbus should be auto-executed by D-Bus message bus daemon.\n" + "If you want to run it manually anyway, specify \"auto\" as only parameter\n"); + goto fail; + } + + if (!paths_init()) + { + ret = 1; + goto fail; + } + + if (!log_init()) + { + ret = 1; + goto fail_uninit_paths; + } + + /* ensure that we are in our own process group so that + kill (SIG, -pgrp) does the right thing. + */ + + setsid(); + + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + + /* what's this for? + + POSIX says that signals are delivered like this: + + * if a thread has blocked that signal, it is not + a candidate to receive the signal. + * of all threads not blocking the signal, pick + one at random, and deliver the signal. + + this means that a simple-minded multi-threaded program can + expect to get POSIX signals delivered randomly to any one + of its threads, + + here, we block all signals that we think we might receive + and want to catch. all "child" threads will inherit this + setting. if we create a thread that calls sigwait() on the + same set of signals, implicitly unblocking all those + signals. any of those signals that are delivered to the + process will be delivered to that thread, and that thread + alone. this makes cleanup for a signal-driven exit much + easier, since we know which thread is doing it and more + importantly, we are free to call async-unsafe functions, + because the code is executing in normal thread context + after a return from sigwait(). + */ + + sigemptyset(&signals); + sigaddset(&signals, SIGHUP); + sigaddset(&signals, SIGINT); + sigaddset(&signals, SIGQUIT); + sigaddset(&signals, SIGPIPE); + sigaddset(&signals, SIGTERM); + sigaddset(&signals, SIGUSR1); + sigaddset(&signals, SIGUSR2); + + /* all child threads will inherit this mask unless they + * explicitly reset it + */ + + pthread_sigmask (SIG_BLOCK, &signals, 0); + + /* install a do-nothing handler because otherwise pthreads + behaviour is undefined when we enter sigwait. + */ + + sigfillset (&allsignals); + action.sa_handler = do_nothing_handler; + action.sa_mask = allsignals; + action.sa_flags = SA_RESTART|SA_RESETHAND; + + for (i = 1; i < NSIG; i++) { + if (sigismember (&signals, i)) { + sigaction (i, &action, 0); + } + } + + g_type_init (); + + jack_set_error_function (jack_dbus_error); + jack_set_info_function (jack_dbus_info); + + jack_info("------------------"); + jack_info("Controller activated. Version " VERSION); + + error = NULL; + connection = dbus_g_bus_get (DBUS_BUS_SESSION, + &error); + if (connection == NULL) + { + g_printerr ("Failed to open connection to bus: %s\n", + error->message); + g_error_free (error); + ret = 1; + goto fail_uninit_log; + } + + bus_proxy = dbus_g_proxy_new_for_name ( + connection, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus"); + + if (!dbus_g_proxy_call (bus_proxy, "RequestName", &error, + G_TYPE_STRING, "org.jackaudio.service", + G_TYPE_UINT, 0, + G_TYPE_INVALID, + G_TYPE_UINT, &request_name_result, + G_TYPE_INVALID)) + { + g_printerr ("Failed to acquire org.jackaudio.service %s", error->message); + ret = 1; + goto fail_uninit_log; + } + + controller_ptr = g_object_new(JACK_CONTROLLER_TYPE, NULL); + + dbus_g_connection_register_g_object (connection, + "/DefaultController", + G_OBJECT(controller_ptr)); + + jack_controller_run(controller_ptr); + + g_object_unref(controller_ptr); + + jack_info("Controller deactivated."); + + ret = 0; + +fail_uninit_log: + log_uninit(); + +fail_uninit_paths: + paths_uninit(); + +fail: + return ret; +} diff --git a/jackd/jackdbus.h b/jackd/jackdbus.h new file mode 100644 --- /dev/null +++ b/jackd/jackdbus.h @@ -0,0 +1,29 @@ +/* -*- mode: c; c-file-style: "bsd"; -*- */ +/* + Copyright (C) 2007 Nedko Arnaudov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License. + + This program 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 General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef JACKDBUS_H__3DB2458F_44B2_43EA_882A_9F888DF71A88__INCLUDED +#define JACKDBUS_H__3DB2458F_44B2_43EA_882A_9F888DF71A88__INCLUDED + +#define JACKDBUS_DIR "/.jackdbus" +#define JACKDBUS_LOG "/jackdbus.log" +#define JACKDBUS_CONF "/conf.xml" + +extern gchar *g_jackdbus_dir; + +#endif /* #ifndef JACKDBUS_H__3DB2458F_44B2_43EA_882A_9F888DF71A88__INCLUDED */ diff --git a/jackd/org.jackaudio.service.in b/jackd/org.jackaudio.service.in new file mode 100644 --- /dev/null +++ b/jackd/org.jackaudio.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.jackaudio.service +Exec=@bindir@/jackdbus auto