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) + * Implement settings persistence (load/save), probably using libxml2 + * 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) diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -547,6 +547,35 @@ 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 + # 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 +778,19 @@ 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 +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) + +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) + +# 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,1954 @@ +/* -*- 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 + +#include "jackcontroller.h" +#include "jackcontroller-glue.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; + } + + 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; +} + +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_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_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); + g_set_error(error, jack_controller_error_quark(), 0, buffer); + + va_end(ap); +} + +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; + + 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; +} + +jack_driver_param_desc_t * +jack_controller_find_parameter_descriptor( + struct jack_controller *controller_ptr, + const gchar *param_name) +{ + unsigned int i; + + for (i = 0; i < controller_ptr->driver_ptr->desc_ptr->nparams ; i++) + { + if (strcmp(controller_ptr->driver_ptr->desc_ptr->params[i].name, param_name) == 0) + { + return controller_ptr->driver_ptr->desc_ptr->params + i; + } + } + + return NULL; +} + +/* search for parameter matching parameter descriptor, return NULL if not found */ +jack_driver_param_t * +jack_controller_find_parameter( + struct jack_controller *controller_ptr, + const jack_driver_param_desc_t *descr_ptr) +{ + JSList * node_ptr; + jack_driver_param_t * param_ptr; + + for (node_ptr = controller_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; +} + +/* search for parameter matching parameter descriptor, return NULL if not found */ +jack_driver_param_t * +jack_controller_find_or_create_parameter( + struct jack_controller *controller_ptr, + const jack_driver_param_desc_t *descr_ptr) +{ + jack_driver_param_t * param_ptr; + + param_ptr = jack_controller_find_parameter(controller_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; + + controller_ptr->driver_ptr->params = jack_slist_append(controller_ptr->driver_ptr->params, param_ptr); + + return param_ptr; +} + +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(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(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(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(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(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(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(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(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(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(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(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(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); + + 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(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(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; + + 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(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(controller_ptr, param_desc_ptr); + if (param_ptr == NULL) + { + jack_controller_error(error, "driver parameter creation failed"); + return FALSE; + } + + param_ptr->value.c = value; + + 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(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(controller_ptr, param_desc_ptr); + if (param_ptr == NULL) + { + jack_controller_error(error, "driver parameter creation failed"); + return FALSE; + } + + param_ptr->value.i = value; + + 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(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(controller_ptr, param_desc_ptr); + if (param_ptr == NULL) + { + jack_controller_error(error, "driver parameter creation failed"); + return FALSE; + } + + param_ptr->value.ui = value; + + 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; + return TRUE; + } + + if (strcmp(parameter, "no-mem-lock") == 0) + { + controller_ptr->do_mlock = !value; /* reverse logic */ + return TRUE; + } + + if (strcmp(parameter, "temporary") == 0) + { + controller_ptr->temporary = value; + return TRUE; + } + + 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; + return TRUE; + } + + if (strcmp(parameter, "no-zombies") == 0) + { + controller_ptr->nozombies = value; + return TRUE; + } + + if (strcmp(parameter, "libs-unlock") == 0) + { + controller_ptr->do_unlock = value; + return TRUE; + } + + jack_controller_error(error, "Unknown engine boolean parameter \"%s\"", parameter); + return FALSE; +} + +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; + return TRUE; + } + + if (strcmp(parameter, "client-timeout") == 0) + { + controller_ptr->client_timeout = value; + return TRUE; + } + + jack_controller_error(error, "Unknown engine sint parameter \"%s\"", parameter); + return FALSE; +} + +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; + return TRUE; + } + + jack_controller_error(error, "Unknown engine uint parameter \"%s\"", parameter); + return FALSE; +} 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,289 @@ +/* -*- 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 "jackcontroller.h" + +FILE *g_logfile; + +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 +log_init() +{ + char path[255]; + const char *home_dir; + struct stat st; + + home_dir = getenv("HOME"); + if (home_dir == NULL) + { + g_printerr("Environment variable HOME not set\n"); + return FALSE; + } + + snprintf(path, sizeof(path), "%s/.jackdbus", home_dir); + + if (stat(path, &st) != 0) + { + if (errno == ENOENT) + { + g_print("Directory \"%s\" does not exist. Creating...\n", path); + if (mkdir(path, 0777) != 0) + { + g_printerr("Failed to create \"%s\" directory: %d (%s)\n", path, errno, strerror(errno)); + return FALSE; + } + } + else + { + g_printerr("Failed to stat \"%s\": %d (%s)\n", path, errno, strerror(errno)); + return FALSE; + } + } + else + { + if (!S_ISDIR(st.st_mode)) + { + g_printerr("\"%s\" exists but is not directory.\n", path); + return FALSE; + } + } + + snprintf(path, sizeof(path), "%s/.jackdbus/jackdbus.log", home_dir); + + g_logfile = fopen(path, "a"); + if (g_logfile == NULL) + { + fprintf(stderr, "Cannot open jackdbus log file \"%s\": %d (%s)\n", path, errno, strerror(errno)); + return FALSE; + } + + 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 (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 (!log_init()) + { + ret = 1; + goto fail; + } + + /* 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."); + + 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: + return ret; +} 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