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,165 @@ +#!/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 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 driver parameter" + print " dps - set driver 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) + typeno = iface.GetDriverParameterType(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 "driver select 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(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() + 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,1510 @@ +/* -*- 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; + + char *server_name; + int realtime; + int realtime_priority; + int do_mlock; + int temporary; + int verbose; + int client_timeout; /* msecs; if zero, use period size. */ + unsigned int port_max; + int do_unlock; + jack_nframes_t frame_time_offset; + int nozombies; +}; + +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 = 1; + controller_ptr->client_timeout = 0; + 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->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; +} diff --git a/jackd/jackcontroller.h b/jackd/jackcontroller.h new file mode 100644 --- /dev/null +++ b/jackd/jackcontroller.h @@ -0,0 +1,220 @@ +/* -*- 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); + +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,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jackd/jackdbus.c b/jackd/jackdbus.c new file mode 100644 --- /dev/null +++ b/jackd/jackdbus.c @@ -0,0 +1,276 @@ +/* -*- 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 (!log_init()) + { + 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); + 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); + 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