summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--example-clients/Makefile.am5
-rw-r--r--example-clients/simple_session_client.c193
-rw-r--r--jack/Makefile.am1
-rw-r--r--jack/engine.h13
-rw-r--r--jack/internal.h32
-rw-r--r--jack/jack.h1
-rw-r--r--jack/session.h229
-rw-r--r--jack/types.h21
-rw-r--r--jack/varargs.h3
-rw-r--r--jackd/clientengine.c99
-rw-r--r--jackd/engine.c242
-rw-r--r--libjack/client.c227
-rw-r--r--libjack/local.h5
-rw-r--r--python/libjack.py363
-rwxr-xr-xpython/sessionmanager.py277
-rw-r--r--python/state.py136
-rw-r--r--tools/Makefile.am5
-rw-r--r--tools/connect.c55
-rw-r--r--tools/session_notify.c180
19 files changed, 2047 insertions, 40 deletions
diff --git a/example-clients/Makefile.am b/example-clients/Makefile.am
index 40eb530..9f1ef53 100644
--- a/example-clients/Makefile.am
+++ b/example-clients/Makefile.am
@@ -13,6 +13,7 @@ dist-check-sndfile:
endif
bin_PROGRAMS = jack_simple_client \
+ jack_simple_session_client \
jack_transport_client \
jack_impulse_grabber \
jack_metro \
@@ -33,6 +34,10 @@ jack_simple_client_SOURCES = simple_client.c
jack_simple_client_LDFLAGS = @OS_LDFLAGS@
jack_simple_client_LDADD = $(top_builddir)/libjack/libjack.la
+jack_simple_session_client_SOURCES = simple_session_client.c
+jack_simple_session_client_LDFLAGS = @OS_LDFLAGS@
+jack_simple_session_client_LDADD = $(top_builddir)/libjack/libjack.la
+
jack_transport_client_SOURCES = transport_client.c
jack_transport_client_LDFLAGS = @OS_LDFLAGS@
jack_transport_client_LDADD = $(top_builddir)/libjack/libjack.la
diff --git a/example-clients/simple_session_client.c b/example-clients/simple_session_client.c
new file mode 100644
index 0000000..76b0cc9
--- /dev/null
+++ b/example-clients/simple_session_client.c
@@ -0,0 +1,193 @@
+/** @file simple_session_client.c
+ *
+ * @brief This simple client demonstrates the most basic features of JACK
+ * as they would be used by many applications.
+ * this version also adds session manager functionality.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <jack/jack.h>
+#include <jack/session.h>
+
+jack_port_t *input_port;
+jack_port_t *output_port;
+jack_client_t *client;
+
+int simple_quit = 0;
+
+/**
+ * The process callback for this JACK application is called in a
+ * special realtime thread once for each audio cycle.
+ *
+ * This client does nothing more than copy data from its input
+ * port to its output port. It will exit when stopped by
+ * the user (e.g. using Ctrl-C on a unix-ish operating system)
+ */
+int
+process (jack_nframes_t nframes, void *arg)
+{
+ jack_default_audio_sample_t *in, *out;
+
+ in = jack_port_get_buffer (input_port, nframes);
+ out = jack_port_get_buffer (output_port, nframes);
+ memcpy (out, in,
+ sizeof (jack_default_audio_sample_t) * nframes);
+
+ return 0;
+}
+
+void
+session_callback (jack_session_event_t *event, void *arg)
+{
+ char retval[100];
+ printf ("session notification\n");
+ printf ("path %s, uuid %s, type: %s\n", event->session_dir, event->client_uuid, event->type == JackSessionSave ? "save" : "quit");
+
+
+ snprintf (retval, 100, "jack_simple_client %s", event->client_uuid);
+ event->command_line = strdup (retval);
+
+ jack_session_reply( client, event );
+
+ if (event->type == JackSessionSaveAndQuit) {
+ simple_quit = 1;
+ }
+
+ jack_session_event_free (event);
+}
+
+/**
+ * JACK calls this shutdown_callback if the server ever shuts down or
+ * decides to disconnect the client.
+ */
+void
+jack_shutdown (void *arg)
+{
+ exit (1);
+}
+
+int
+main (int argc, char *argv[])
+{
+ const char **ports;
+ const char *client_name = "simple";
+ jack_status_t status;
+
+ /* open a client connection to the JACK server */
+
+ if( argc == 1 )
+ client = jack_client_open (client_name, JackNullOption, &status );
+ else if( argc == 2 )
+ client = jack_client_open (client_name, JackSessionID, &status, argv[1] );
+
+ if (client == NULL) {
+ fprintf (stderr, "jack_client_open() failed, "
+ "status = 0x%2.0x\n", status);
+ if (status & JackServerFailed) {
+ fprintf (stderr, "Unable to connect to JACK server\n");
+ }
+ exit (1);
+ }
+ if (status & JackServerStarted) {
+ fprintf (stderr, "JACK server started\n");
+ }
+ if (status & JackNameNotUnique) {
+ client_name = jack_get_client_name(client);
+ fprintf (stderr, "unique name `%s' assigned\n", client_name);
+ }
+
+ /* tell the JACK server to call `process()' whenever
+ there is work to be done.
+ */
+
+ jack_set_process_callback (client, process, 0);
+
+ /* tell the JACK server to call `jack_shutdown()' if
+ it ever shuts down, either entirely, or if it
+ just decides to stop calling us.
+ */
+
+ jack_on_shutdown (client, jack_shutdown, 0);
+
+ /* tell the JACK server to call `session_callback()' if
+ the session is saved.
+ */
+
+ jack_set_session_callback (client, session_callback, NULL);
+
+ /* display the current sample rate.
+ */
+
+ printf ("engine sample rate: %" PRIu32 "\n",
+ jack_get_sample_rate (client));
+
+ /* create two ports */
+
+ input_port = jack_port_register (client, "input",
+ JACK_DEFAULT_AUDIO_TYPE,
+ JackPortIsInput, 0);
+ output_port = jack_port_register (client, "output",
+ JACK_DEFAULT_AUDIO_TYPE,
+ JackPortIsOutput, 0);
+
+ if ((input_port == NULL) || (output_port == NULL)) {
+ fprintf(stderr, "no more JACK ports available\n");
+ exit (1);
+ }
+
+ /* Tell the JACK server that we are ready to roll. Our
+ * process() callback will start running now. */
+
+ if (jack_activate (client)) {
+ fprintf (stderr, "cannot activate client");
+ exit (1);
+ }
+
+ /* Connect the ports. You can't do this before the client is
+ * activated, because we can't make connections to clients
+ * that aren't running. Note the confusing (but necessary)
+ * orientation of the driver backend ports: playback ports are
+ * "input" to the backend, and capture ports are "output" from
+ * it.
+ */
+
+ ports = jack_get_ports (client, NULL, NULL,
+ JackPortIsPhysical|JackPortIsOutput);
+ if (ports == NULL) {
+ fprintf(stderr, "no physical capture ports\n");
+ exit (1);
+ }
+
+ if (jack_connect (client, ports[0], jack_port_name (input_port))) {
+ fprintf (stderr, "cannot connect input ports\n");
+ }
+
+ free (ports);
+
+ ports = jack_get_ports (client, NULL, NULL,
+ JackPortIsPhysical|JackPortIsInput);
+ if (ports == NULL) {
+ fprintf(stderr, "no physical playback ports\n");
+ exit (1);
+ }
+
+ if (jack_connect (client, jack_port_name (output_port), ports[0])) {
+ fprintf (stderr, "cannot connect output ports\n");
+ }
+
+ free (ports);
+
+ /* keep running until until we get a quit event */
+
+ while (!simple_quit)
+ sleep(1);
+
+
+ jack_client_close (client);
+ exit (0);
+}
diff --git a/jack/Makefile.am b/jack/Makefile.am
index 44c378c..8da1399 100644
--- a/jack/Makefile.am
+++ b/jack/Makefile.am
@@ -7,6 +7,7 @@ libjackinclude_HEADERS = \
jack.h \
ringbuffer.h \
statistics.h \
+ session.h \
thread.h \
timestamps.h \
transport.h \
diff --git a/jack/engine.h b/jack/engine.h
index bab00ba..46e0d51 100644
--- a/jack/engine.h
+++ b/jack/engine.h
@@ -51,6 +51,11 @@ typedef struct _jack_port_buffer_list {
jack_port_buffer_info_t *info; /* jack_buffer_info_t array */
} jack_port_buffer_list_t;
+typedef struct _jack_reserved_name {
+ jack_client_id_t uuid;
+ char name[JACK_CLIENT_NAME_SIZE];
+} jack_reserved_name_t;
+
#define JACKD_WATCHDOG_TIMEOUT 10000
#define JACKD_CLIENT_EVENT_TIMEOUT 2000
@@ -109,6 +114,11 @@ struct _jack_engine {
char fifo_prefix[PATH_MAX+1];
int *fifo;
unsigned long fifo_size;
+
+ /* session handling */
+ int session_reply_fd;
+ int session_pending_replies;
+
unsigned long external_client_cnt;
int rtpriority;
volatile char freewheeling;
@@ -130,6 +140,7 @@ struct _jack_engine {
/* these lists are protected by `client_lock' */
JSList *clients;
JSList *clients_waiting;
+ JSList *reserved_client_names;
jack_port_internal_t *internal_ports;
jack_client_internal_t *timebase_client;
@@ -234,5 +245,7 @@ void jack_port_registration_notify (jack_engine_t *, jack_port_id_t, int);
void jack_port_release (jack_engine_t *engine, jack_port_internal_t *);
void jack_sort_graph (jack_engine_t *engine);
int jack_stop_freewheeling (jack_engine_t* engine, int engine_exiting);
+jack_client_internal_t *
+jack_client_by_name (jack_engine_t *engine, const char *name);
#endif /* __jack_engine_h__ */
diff --git a/jack/internal.h b/jack/internal.h
index ebcfcb7..801ddc9 100644
--- a/jack/internal.h
+++ b/jack/internal.h
@@ -58,6 +58,7 @@ extern void jack_info (const char *fmt, ...);
#include <jack/types.h>
#include <jack/port.h>
#include <jack/transport.h>
+#include <jack/session.h>
#include <jack/thread.h>
extern jack_thread_creator_t jack_thread_creator;
@@ -217,14 +218,15 @@ typedef enum {
StartFreewheel,
StopFreewheel,
ClientRegistered,
- ClientUnregistered
+ ClientUnregistered,
+ SaveSession
} JackEventType;
typedef struct {
JackEventType type;
union {
uint32_t n;
- char name[JACK_CLIENT_NAME_SIZE];
+ char name[JACK_PORT_NAME_SIZE];
jack_port_id_t port_id;
jack_port_id_t self_id;
} x;
@@ -252,9 +254,12 @@ typedef enum {
typedef volatile struct {
volatile jack_client_id_t id; /* w: engine r: engine and client */
+ volatile jack_client_id_t uid; /* w: engine r: engine and client */
volatile jack_nframes_t nframes; /* w: engine r: client */
volatile jack_client_state_t state; /* w: engine and client r: engine */
volatile char name[JACK_CLIENT_NAME_SIZE];
+ volatile char session_command[JACK_PORT_NAME_SIZE];
+ volatile jack_session_flags_t session_flags;
volatile ClientType type; /* w: engine r: engine and client */
volatile int8_t active; /* w: engine r: engine and client */
volatile int8_t dead; /* r/w: engine */
@@ -292,6 +297,7 @@ typedef volatile struct {
volatile uint8_t freewheel_cb_cbset;
volatile uint8_t client_register_cbset;
volatile uint8_t thread_cb_cbset;
+ volatile uint8_t session_cbset;
} POST_PACKED_STRUCTURE jack_client_control_t;
@@ -301,6 +307,7 @@ typedef struct {
int32_t load;
ClientType type;
jack_options_t options;
+ jack_client_id_t uuid;
char name[JACK_CLIENT_NAME_SIZE];
char object_path[PATH_MAX+1];
@@ -370,7 +377,11 @@ typedef enum {
IntClientName = 21,
IntClientUnload = 22,
RecomputeTotalLatencies = 23,
- RecomputeTotalLatency = 24
+ RecomputeTotalLatency = 24,
+ SessionNotify = 25,
+ GetClientByUUID = 26,
+ ReserveName = 30,
+ SessionReply = 31
} RequestType;
struct _jack_request {
@@ -391,6 +402,11 @@ struct _jack_request {
char destination_port[JACK_PORT_NAME_SIZE];
} POST_PACKED_STRUCTURE connect;
struct {
+ char path[JACK_PORT_NAME_SIZE];
+ jack_session_event_type_t type;
+ char target[JACK_CLIENT_NAME_SIZE];
+ } POST_PACKED_STRUCTURE session;
+ struct {
int32_t nports;
const char **ports; /* this is only exposed to internal clients, so there
is no 64/32 issue. external clients read the ports
@@ -408,6 +424,10 @@ struct _jack_request {
int32_t conditional;
} POST_PACKED_STRUCTURE timebase;
struct {
+ char name[JACK_CLIENT_NAME_SIZE];
+ jack_client_id_t uuid;
+ } POST_PACKED_STRUCTURE reservename;
+ struct {
//jack_options_t options;
uint32_t options;
jack_client_id_t id;
@@ -447,6 +467,8 @@ typedef struct _jack_client_internal {
int (*initialize)(jack_client_t*, const char*); /* int. clients only */
void (*finish)(void *); /* internal clients only */
int error;
+
+ int session_reply_pending;
#ifdef JACK_USE_MACH_THREADS
/* specific resources for server/client real-time thread communication */
@@ -456,10 +478,6 @@ typedef struct _jack_client_internal {
int portnum;
#endif /* JACK_USE_MACH_THREADS */
- /* external clients: set by libjack
- * internal clients: set by engine */
- //int (*deliver_request)(void*, jack_request_t*); /* JOQ: 64/32 bug! */
- //void *deliver_arg;
jack_client_t *private_client;
} jack_client_internal_t;
diff --git a/jack/jack.h b/jack/jack.h
index ac50a12..21174dc 100644
--- a/jack/jack.h
+++ b/jack/jack.h
@@ -406,7 +406,6 @@ int jack_set_graph_order_callback (jack_client_t *,
*/
int jack_set_xrun_callback (jack_client_t *,
JackXRunCallback xrun_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT;
-
/*@}*/
/**
diff --git a/jack/session.h b/jack/session.h
new file mode 100644
index 0000000..8c13bfa
--- /dev/null
+++ b/jack/session.h
@@ -0,0 +1,229 @@
+/*
+ Copyright (C) 2001 Paul Davis
+ Copyright (C) 2004 Jack O'Quin
+ Copyright (C) 2010 Torben Hohn
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef __jack_session_h__
+#define __jack_session_h__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <jack/types.h>
+#include <jack/weakmacros.h>
+
+/**
+ * @defgroup SessionClientFunctions Session API for clients.
+ * @{
+ */
+
+
+/**
+ * session event types.
+ *
+ * if a client cant save templates, i might just do a normal save.
+ *
+ * the rationale, why there is no quit without save, is that a client
+ * might refuse to quit when it has unsaved data.
+ * however some other clients might have already quit.
+ * this results in too much confusion, so we just dont support that.
+ * the session manager can check, if the saved state is different from a previous
+ * save, and just remove the saved stuff.
+ *
+ * (an inquiry function, whether a quit is ok, followed by a quit event
+ * would have a race)
+ */
+enum JackSessionEventType {
+ JackSessionSave = 1,
+ JackSessionSaveAndQuit = 2,
+ JackSessionSaveTemplate = 3
+};
+
+typedef enum JackSessionEventType jack_session_event_type_t;
+
+enum JackSessionFlags {
+ /**
+ * an error occured while saving.
+ */
+ JackSessionSaveError = 0x01,
+
+ /**
+ * this reply indicates that a client is part of a multiclient application.
+ * the command reply is left empty. but the session manager should still
+ * consider this client part of a session. it will come up due to invocation of another
+ * client.
+ */
+ JackSessionChildClient = 0x02
+};
+
+typedef enum JackSessionFlags jack_session_flags_t;
+
+struct _jack_session_event {
+ /**
+ * the actual type of this session event.
+ */
+ jack_session_event_type_t type;
+
+ /**
+ * session_directory with trailing separator
+ * this is per client. so the client can do whatever it likes in here.
+ */
+ const char *session_dir;
+
+ /**
+ * client_uuid which must be specified to jack_client_open on session reload.
+ * client can specify it in the returned commandline as an option, or just save it
+ * with the state file.
+ */
+ const char *client_uuid;
+
+ /**
+ * the command_line is the reply of the client.
+ * it specifies in a platform dependent way, how the client must be restarted upon session reload.
+ *
+ * probably it should contain ${SESSION_DIR} instead of the actual session dir.
+ * this would basically make the session dir moveable.
+ *
+ * ownership of the memory is handed to jack.
+ * initially set to NULL by jack;
+ */
+ char *command_line;
+
+ /**
+ * flags to be set by the client. normally left 0.
+ */
+ jack_session_flags_t flags;
+};
+
+typedef struct _jack_session_event jack_session_event_t;
+
+/**
+ * Prototype for the client supplied function that is called
+ * whenever a session notification is sent via jack_session_notify().
+ *
+ * The session_id must be passed to jack_client_open on session reload (this can be
+ * done by specifying it somehow on the returned command line).
+ *
+ * @param event the event_structure.
+ * @param arg pointer to a client supplied structure
+ */
+typedef void (*JackSessionCallback)(jack_session_event_t *event, void *arg);
+
+/**
+ * Tell the JACK server to call @a save_callback the session handler wants
+ * to save.
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_set_session_callback(jack_client_t *client,
+ JackSessionCallback session_callback,
+ void *arg) JACK_WEAK_EXPORT;
+
+/**
+ * reply to a session_event
+ *
+ * this can either be called directly from the callback, or later from a different thread.
+ * so its possible to just stick the event pointer into a pipe and execute the save code
+ * from the gui thread.
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+
+int jack_session_reply( jack_client_t *client, jack_session_event_t *event ) JACK_WEAK_EXPORT;
+
+
+/**
+ * free memory used by a jack_session_event_t
+ * this also frees the memory used by the command_line pointer.
+ * if its non NULL.
+ */
+
+void jack_session_event_free (jack_session_event_t *event);
+
+/*@}*/
+
+
+/**
+ * @defgroup JackSessionManagerAPI this API is intended for a sessionmanager.
+ * this API could be server specific. if we dont reach consensus here,
+ * we can just drop it.
+ * i know its a bit clumsy.
+ * but this api isnt required to be as stable as the client api.
+ * @{
+ */
+
+typedef struct {
+ const char *uuid;
+ const char *client_name;
+ const char *command;
+ jack_session_flags_t flags;
+} jack_session_command_t;
+
+/**
+ * send a save or quit event, to all clients listening for session
+ * callbacks. the returned strings of the clients are accumulated and
+ * returned as an array of jack_session_command_t.
+ * its terminated by ret[i].uuid == NULL
+ * target == NULL means send to all interested clients. otherwise a clientname
+ */
+
+jack_session_command_t *jack_session_notify (jack_client_t* client,
+ const char *target,
+ jack_session_event_type_t type,
+ const char *path ) JACK_WEAK_EXPORT;
+
+/**
+ * free the memory allocated by a session command.
+ */
+
+void jack_session_commands_free (jack_session_command_t *cmds) JACK_WEAK_EXPORT;
+
+/**
+ * get the sessionid for a client name.
+ * the sessionmanager needs this to reassociate a client_name to the session_id.
+ */
+
+char *jack_get_uuid_for_client_name( jack_client_t *client, const char *client_name ) JACK_WEAK_EXPORT;
+
+/**
+ * get the client name for a session_id.
+ * in order to snapshot the graph connections, the sessionmanager needs to map
+ * session_ids to client names.
+ */
+
+char *jack_get_client_name_by_uuid( jack_client_t *client, const char *client_uuid ) JACK_WEAK_EXPORT;
+
+/**
+ * reserve a client name and associate it to a uuid.
+ * when a client later call jack_client_open() and specifies the uuid,
+ * jackd will assign the reserved name.
+ * this allows a session manager to know in advance under which client name
+ * its managed clients will appear.
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+
+int
+jack_reserve_client_name( jack_client_t *client, const char *name, const char *uuid ) JACK_WEAK_EXPORT;
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/jack/types.h b/jack/types.h
index ea49057..91d2523 100644
--- a/jack/types.h
+++ b/jack/types.h
@@ -113,11 +113,16 @@ enum JackOptions {
* Pass optional <em>(char *) load_init</em> string to the
* jack_initialize() entry point of an internal client.
*/
- JackLoadInit = 0x10
+ JackLoadInit = 0x10,
+
+ /**
+ * pass a SessionID Token this allows the sessionmanager to identify the client again.
+ */
+ JackSessionID = 0x20
};
/** Valid options for opening an external client. */
-#define JackOpenOptions (JackServerName|JackNoStartServer|JackUseExactName)
+#define JackOpenOptions (JackSessionID|JackServerName|JackNoStartServer|JackUseExactName)
/** Valid options for loading an internal client. */
#define JackLoadOptions (JackLoadInit|JackLoadName|JackUseExactName)
@@ -331,14 +336,14 @@ typedef void (*JackPortConnectCallback)(jack_port_id_t a, jack_port_id_t b, int
*
* @param starting non-zero if we start starting to freewheel, zero otherwise
* @param arg pointer to a client supplied structure
- */
+ */
typedef void (*JackFreewheelCallback)(int starting, void *arg);
typedef void *(*JackThreadCallback)(void* arg);
/**
* Prototype for the client supplied function that is called
- * whenever jackd is shutdown. Note that after server shutdown,
+ * whenever jackd is shutdown. Note that after server shutdown,
* the client pointer is *not* deallocated by libjack,
* the application is responsible to properly use jack_client_close()
* to release client ressources. Warning: jack_client_close() cannot be
@@ -351,15 +356,15 @@ typedef void (*JackShutdownCallback)(void *arg);
/**
* Prototype for the client supplied function that is called
- * whenever jackd is shutdown. Note that after server shutdown,
+ * whenever jackd is shutdown. Note that after server shutdown,
* the client pointer is *not* deallocated by libjack,
* the application is responsible to properly use jack_client_close()
* to release client ressources. Warning: jack_client_close() cannot be
* safely used inside the shutdown callback and has to be called outside of
* the callback context.
-
- * @param code a shuntdown code
- * @param reason a string discribing the shuntdown reason (backend failure, server crash... etc...)
+ *
+ * @param code a shutdown code
+ * @param reason a string describing the shutdown reason (backend failure, server crash... etc...)
* @param arg pointer to a client supplied structure
*/
typedef void (*JackInfoShutdownCallback)(jack_status_t code, const char* reason, void *arg);
diff --git a/jack/varargs.h b/jack/varargs.h
index a2e6a91..ae9e58a 100644
--- a/jack/varargs.h
+++ b/jack/varargs.h
@@ -29,6 +29,7 @@ typedef struct {
char *server_name; /* server name */
char *load_name; /* load module name */
char *load_init; /* initialization string */
+ char *sess_uuid;
} jack_varargs_t;
static inline void
@@ -53,6 +54,8 @@ jack_varargs_parse (jack_options_t options, va_list ap, jack_varargs_t *va)
va->load_name = va_arg(ap, char *);
if ((options & JackLoadInit))
va->load_init = va_arg(ap, char *);
+ if ((options & JackSessionID))
+ va->sess_uuid = va_arg(ap, char *);
}
#ifdef __cplusplus
diff --git a/jackd/clientengine.c b/jackd/clientengine.c
index a1599e1..3be58fe 100644
--- a/jackd/clientengine.c
+++ b/jackd/clientengine.c
@@ -109,6 +109,7 @@ static void
jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client)
{
JSList *node;
+ jack_client_id_t finalizer=0;
/* caller must write-hold the client lock */
@@ -120,6 +121,20 @@ jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client)
jack_zombify_client (engine, client);
}
+ if (client->session_reply_pending) {
+ engine->session_pending_replies -= 1;
+
+ if (engine->session_pending_replies == 0) {
+ if (write (engine->session_reply_fd, &finalizer, sizeof (finalizer))
+ < (ssize_t) sizeof (finalizer)) {
+ jack_error ("cannot write SessionNotify result "
+ "to client via fd = %d (%s)",
+ engine->session_reply_fd, strerror (errno));
+ }
+ engine->session_reply_fd = -1;
+ }
+ }
+
if (client->control->type == ClientExternal) {
/* try to force the server thread to return from poll */
@@ -342,7 +357,7 @@ jack_client_unload (jack_client_internal_t *client)
}
}
-static jack_client_internal_t *
+jack_client_internal_t *
jack_client_by_name (jack_engine_t *engine, const char *name)
{
jack_client_internal_t *client = NULL;
@@ -406,6 +421,18 @@ jack_client_internal_by_id (jack_engine_t *engine, jack_client_id_t id)
return client;
}
+int
+jack_client_name_reserved( jack_engine_t *engine, const char *name )
+{
+ JSList *node;
+ for (node = engine->reserved_client_names; node; node = jack_slist_next (node)) {
+ jack_reserved_name_t *reservation = (jack_reserved_name_t *) node->data;
+ if( !strcmp( reservation->name, name ) )
+ return 1;
+ }
+ return 0;
+}
+
/* generate a unique client name
*
* returns 0 if successful, updates name in place
@@ -428,7 +455,7 @@ jack_generate_unique_name (jack_engine_t *engine, char *name)
name[tens] = '0';
name[ones] = '1';
name[length] = '\0';
- while (jack_client_by_name (engine, name)) {
+ while (jack_client_by_name (engine, name) || jack_client_name_reserved( engine, name )) {
if (name[ones] == '9') {
if (name[tens] == '9') {
jack_error ("client %s has 99 extra"
@@ -456,7 +483,7 @@ jack_client_name_invalid (jack_engine_t *engine, char *name,
* startup. There are no other clients at that point, anyway.
*/
- if (jack_client_by_name (engine, name)) {
+ if (jack_client_by_name (engine, name) || jack_client_name_reserved(engine, name )) {
*status |= JackNameNotUnique;
@@ -480,7 +507,7 @@ jack_client_name_invalid (jack_engine_t *engine, char *name,
* internal and external clients. */
static jack_client_internal_t *
jack_setup_client_control (jack_engine_t *engine, int fd,
- ClientType type, const char *name)
+ ClientType type, const char *name, jack_client_id_t uuid)
{
jack_client_internal_t *client;
@@ -530,10 +557,13 @@ jack_setup_client_control (jack_engine_t *engine, int fd,
client->control->dead = FALSE;
client->control->timed_out = 0;
client->control->id = engine->next_client_id++;
+ client->control->uid = uuid;
strcpy ((char *) client->control->name, name);
client->subgraph_start_fd = -1;
client->subgraph_wait_fd = -1;
+ client->session_reply_pending = FALSE;
+
client->control->process_cbset = FALSE;
client->control->bufsize_cbset = FALSE;
client->control->srate_cbset = FALSE;
@@ -543,6 +573,7 @@ jack_setup_client_control (jack_engine_t *engine, int fd,
client->control->graph_order_cbset = FALSE;
client->control->client_register_cbset = FALSE;
client->control->thread_cb_cbset = FALSE;
+ client->control->session_cbset = FALSE;
#if 0
if (type != ClientExternal) {
@@ -578,9 +609,23 @@ jack_setup_client_control (jack_engine_t *engine, int fd,
return client;
}
+static void
+jack_ensure_uuid_unique (jack_engine_t *engine, jack_client_id_t uuid)
+{
+ JSList *node;
+
+ jack_lock_graph (engine);
+ for (node=engine->clients; node; node=jack_slist_next (node)) {
+ jack_client_internal_t *client = (jack_client_internal_t *) node->data;
+ if (client->control->uid == uuid)
+ client->control->uid = 0;
+ }
+ jack_unlock_graph (engine);
+}
+
/* set up all types of clients */
static jack_client_internal_t *
-setup_client (jack_engine_t *engine, ClientType type, char *name,
+setup_client (jack_engine_t *engine, ClientType type, char *name, jack_client_id_t uuid,
jack_options_t options, jack_status_t *status, int client_fd,
const char *object_path, const char *object_data)
{
@@ -591,9 +636,12 @@ setup_client (jack_engine_t *engine, ClientType type, char *name,
if (jack_client_name_invalid (engine, name, options, status))
return NULL;
+ if (uuid != 0)
+ jack_ensure_uuid_unique (engine, uuid);
+
/* create a client struct for this name */
if ((client = jack_setup_client_control (engine, client_fd,
- type, name)) == NULL) {
+ type, name, uuid )) == NULL) {
*status |= (JackFailure|JackInitFailure);
jack_error ("cannot create new client object");
return NULL;
@@ -687,7 +735,7 @@ jack_create_driver_client (jack_engine_t *engine, char *name)
snprintf (req.name, sizeof (req.name), "%s", name);
pthread_mutex_lock (&engine->request_lock);
- client = setup_client (engine, ClientDriver, name, JackUseExactName,
+ client = setup_client (engine, ClientDriver, name, 0, JackUseExactName,
&status, -1, NULL, NULL);
pthread_mutex_unlock (&engine->request_lock);
@@ -715,6 +763,22 @@ handle_unload_client (jack_engine_t *engine, jack_client_id_t id)
return status;
}
+static char *
+jack_get_reserved_name( jack_engine_t *engine, jack_client_id_t uuid )
+{
+ JSList *node;
+ for (node = engine->reserved_client_names; node; node = jack_slist_next (node)) {
+ jack_reserved_name_t *reservation = (jack_reserved_name_t *) node->data;
+ if( reservation->uuid== uuid ) {
+ char *retval = strdup( reservation->name );
+ free( reservation );
+ engine->reserved_client_names =
+ jack_slist_remove( engine->reserved_client_names, reservation );
+ return retval;
+ }
+ }
+ return 0;
+}
int
jack_client_create (jack_engine_t *engine, int client_fd)
{
@@ -762,7 +826,14 @@ jack_client_create (jack_engine_t *engine, int client_fd)
}
pthread_mutex_lock (&engine->request_lock);
- client = setup_client (engine, req.type, req.name,
+ if( req.uuid ) {
+ char *res_name = jack_get_reserved_name( engine, req.uuid );
+ if( res_name ) {
+ snprintf( req.name, sizeof(req.name), "%s", res_name );
+ free(res_name);
+ }
+ }
+ client = setup_client (engine, req.type, req.name, req.uuid,
req.options, &res.status, client_fd,
req.object_path, req.object_data);
pthread_mutex_unlock (&engine->request_lock);
@@ -818,7 +889,7 @@ int
jack_client_activate (jack_engine_t *engine, jack_client_id_t id)
{
jack_client_internal_t *client;
- JSList *node;
+ JSList *node, *node2;
int ret = -1;
jack_lock_graph (engine);
@@ -843,11 +914,18 @@ jack_client_activate (jack_engine_t *engine, jack_client_id_t id)
++engine->external_client_cnt);
jack_sort_graph (engine);
+ // send delayed notifications for ports.
+ for (node2 = client->ports; node2; node2 = jack_slist_next (node2)) {
+ jack_port_internal_t *port = (jack_port_internal_t *) node2->data;
+ jack_port_registration_notify (engine, port->shared->id, TRUE);
+ }
+
ret = 0;
break;
}
}
+
jack_unlock_graph (engine);
return ret;
}
@@ -886,6 +964,7 @@ jack_client_deactivate (jack_engine_t *engine, jack_client_id_t id)
return ret;
}
+
int
jack_mark_client_socket_error (jack_engine_t *engine, int fd)
{
@@ -968,7 +1047,7 @@ jack_intclient_load_request (jack_engine_t *engine, jack_request_t *req)
req->x.intclient.path, req->x.intclient.init,
req->x.intclient.options);
- client = setup_client (engine, ClientInternal, req->x.intclient.name,
+ client = setup_client (engine, ClientInternal, req->x.intclient.name, 0,
req->x.intclient.options, &status, -1,
req->x.intclient.path, req->x.intclient.init);
diff --git a/jackd/engine.c b/jackd/engine.c
index 6ae9c42..6bd68b7 100644
--- a/jackd/engine.c
+++ b/jackd/engine.c
@@ -134,7 +134,12 @@ static void jack_check_acyclic (jack_engine_t* engine);
static void jack_compute_all_port_total_latencies (jack_engine_t *engine);
static void jack_compute_port_total_latency (jack_engine_t *engine, jack_port_shared_t*);
static void jack_engine_signal_problems (jack_engine_t* engine);
+static int jack_do_session_notify (jack_engine_t *engine, jack_request_t *req, int reply_fd );
static int jack_check_client_status (jack_engine_t* engine);
+static void jack_do_get_client_by_uuid ( jack_engine_t *engine, jack_request_t *req);
+static void jack_do_reserve_name ( jack_engine_t *engine, jack_request_t *req);
+static void jack_do_session_reply (jack_engine_t *engine, jack_request_t *req );
+
static inline int
jack_rolling_interval (jack_time_t period_usecs)
@@ -1353,6 +1358,31 @@ do_request (jack_engine_t *engine, jack_request_t *req, int *reply_fd)
req->status = 0;
break;
+ case GetClientByUUID:
+ jack_rdlock_graph (engine);
+ jack_do_get_client_by_uuid (engine, req);
+ jack_unlock_graph (engine);
+ break;
+ case ReserveName:
+ jack_rdlock_graph (engine);
+ jack_do_reserve_name (engine, req);
+ jack_unlock_graph (engine);
+ break;
+ case SessionReply:
+ jack_rdlock_graph (engine);
+ jack_do_session_reply (engine, req);
+ jack_unlock_graph (engine);
+ break;
+ case SessionNotify:
+ jack_rdlock_graph (engine);
+ if ((req->status =
+ jack_do_session_notify (engine, req, *reply_fd))
+ >= 0) {
+ /* we have already replied, don't do it again */
+ *reply_fd = -1;
+ }
+ jack_unlock_graph (engine);
+ break;
default:
/* some requests are handled entirely on the client
* side, by adjusting the shared memory area(s) */
@@ -1757,6 +1787,9 @@ jack_engine_new (int realtime, int rtpriority, int do_mlock, int do_unlock,
engine->nozombies = nozombies;
engine->removing_clients = 0;
+ engine->session_reply_fd = -1;
+ engine->session_pending_replies = 0;
+
engine->audio_out_cnt = 0;
engine->audio_in_cnt = 0;
engine->midi_out_cnt = 0;
@@ -1771,6 +1804,7 @@ jack_engine_new (int realtime, int rtpriority, int do_mlock, int do_unlock,
pthread_mutex_init (&engine->problem_lock, 0);
engine->clients = 0;
+ engine->reserved_client_names = 0;
engine->pfd_size = 0;
engine->pfd_max = 0;
@@ -2492,6 +2526,201 @@ jack_deliver_event_to_all (jack_engine_t *engine, jack_event_t *event)
jack_unlock_graph (engine);
}
+static jack_client_id_t jack_engine_get_max_uuid( jack_engine_t *engine )
+{
+ JSList *node;
+ jack_client_id_t retval = 0;
+ for (node = engine->clients; node; node = jack_slist_next (node)) {
+ jack_client_internal_t* client = (jack_client_internal_t*) node->data;
+ if( client->control->uid > retval )
+ retval = client->control->uid;
+ }
+ return retval;
+}
+
+static void jack_do_get_client_by_uuid ( jack_engine_t *engine, jack_request_t *req)
+{
+ JSList *node;
+ req->status = -1;
+ for (node = engine->clients; node; node = jack_slist_next (node)) {
+ jack_client_internal_t* client = (jack_client_internal_t*) node->data;
+ if( client->control->uid == req->x.client_id ) {
+ snprintf( req->x.port_info.name, sizeof(req->x.port_info.name), "%s", client->control->name );
+ req->status = 0;
+ return;
+ }
+ }
+}
+
+static void jack_do_reserve_name ( jack_engine_t *engine, jack_request_t *req)
+{
+ jack_reserved_name_t *reservation;
+ JSList *node;
+ // check is name is free...
+ for (node = engine->clients; node; node = jack_slist_next (node)) {
+ jack_client_internal_t* client = (jack_client_internal_t*) node->data;
+ if( !strcmp( (char *)client->control->name, req->x.reservename.name )) {
+ req->status = -1;
+ return;
+ }
+ }
+
+ reservation = malloc( sizeof( jack_reserved_name_t ) );
+ if( reservation == NULL ) {
+ req->status = -1;
+ return;
+ }
+
+ snprintf( reservation->name, sizeof( reservation->name ), "%s", req->x.reservename.name );
+ reservation->uuid = req->x.reservename.uuid;
+ engine->reserved_client_names = jack_slist_append( engine->reserved_client_names, reservation );
+
+ req->status = 0;
+}
+
+static int jack_send_session_reply ( jack_engine_t *engine, jack_client_internal_t *client )
+{
+ if (write (engine->session_reply_fd, (const void *) &client->control->uid, sizeof (client->control->uid))
+ < (ssize_t) sizeof (client->control->uid)) {
+ jack_error ("cannot write SessionNotify result "
+ "to client via fd = %d (%s)",
+ engine->session_reply_fd, strerror (errno));
+ return -1;
+ }
+ if (write (engine->session_reply_fd, (const void *) client->control->name, sizeof (client->control->name))
+ < (ssize_t) sizeof (client->control->name)) {
+ jack_error ("cannot write SessionNotify result "
+ "to client via fd = %d (%s)",
+ engine->session_reply_fd, strerror (errno));
+ return -1;
+ }
+ if (write (engine->session_reply_fd, (const void *) client->control->session_command,
+ sizeof (client->control->session_command))
+ < (ssize_t) sizeof (client->control->session_command)) {
+ jack_error ("cannot write SessionNotify result "
+ "to client via fd = %d (%s)",
+ engine->session_reply_fd, strerror (errno));
+ return -1;
+ }
+ if (write (engine->session_reply_fd, (const void *) ( & client->control->session_flags ),
+ sizeof (client->control->session_flags))
+ < (ssize_t) sizeof (client->control->session_flags)) {
+ jack_error ("cannot write SessionNotify result "
+ "to client via fd = %d (%s)",
+ engine->session_reply_fd, strerror (errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+jack_do_session_notify (jack_engine_t *engine, jack_request_t *req, int reply_fd )
+{
+ JSList *node;
+ jack_event_t event;
+
+ int reply;
+ jack_client_id_t finalizer=0;
+
+ if (engine->session_reply_fd != -1) {
+ // we should have a notion of busy or somthing.
+ // just sending empty reply now.
+ goto send_final;
+ }
+
+ engine->session_reply_fd = reply_fd;
+ engine->session_pending_replies = 0;
+
+ event.type = SaveSession;
+ event.y.n = req->x.session.type;
+
+ /* GRAPH MUST BE LOCKED : see callers of jack_send_connection_notification()
+ */
+
+ for (node = engine->clients; node; node = jack_slist_next (node)) {
+ jack_client_internal_t* client = (jack_client_internal_t*) node->data;
+ if (client->control->session_cbset) {
+
+ if( client->control->uid == 0 ) {
+ client->control->uid=jack_engine_get_max_uuid( engine ) + 1;
+ }
+
+ // in case we only want to send to a special client.
+ // uuid assign is still complete. not sure if thats necessary.
+ if( (req->x.session.target[0] != 0) && strcmp(req->x.session.target, (char *)client->control->name) )
+ continue;
+
+ snprintf (event.x.name, sizeof (event.x.name), "%s%s/", req->x.session.path, client->control->name );
+ mkdir (event.x.name, 0777 );
+ reply = jack_deliver_event (engine, client, &event);
+
+ if (reply == 1) {
+ // delayed reply
+ engine->session_pending_replies += 1;
+ client->session_reply_pending = TRUE;
+ } else if (reply == 2) {
+ // immediate reply
+ if (jack_send_session_reply (engine, client))
+ goto error_out;
+ }
+ }
+ }
+
+ if (engine->session_pending_replies != 0)
+ return 0;
+
+send_final:
+ if (write (reply_fd, &finalizer, sizeof (finalizer))
+ < (ssize_t) sizeof (finalizer)) {
+ jack_error ("cannot write SessionNotify result "
+ "to client via fd = %d (%s)",
+ reply_fd, strerror (errno));
+ goto error_out;
+ }
+
+ engine->session_reply_fd = -1;
+ return 0;
+error_out:
+ return -3;
+}
+
+static void jack_do_session_reply (jack_engine_t *engine, jack_request_t *req )
+{
+ jack_client_id_t client_id = req->x.client_id;
+ jack_client_internal_t *client = jack_client_internal_by_id (engine, client_id);
+ jack_client_id_t finalizer=0;
+
+ req->status = 0;
+
+ client->session_reply_pending = 0;
+
+ if (engine->session_reply_fd == -1) {
+ jack_error ("spurious Session Reply");
+ return;
+ }
+
+ engine->session_pending_replies -= 1;
+
+ if (jack_send_session_reply (engine, client)) {
+ // maybe need to fix all client pendings.
+ // but we will just get a set of spurious replies now.
+ engine->session_reply_fd = -1;
+ return;
+ }
+
+ if (engine->session_pending_replies == 0) {
+ if (write (engine->session_reply_fd, &finalizer, sizeof (finalizer))
+ < (ssize_t) sizeof (finalizer)) {
+ jack_error ("cannot write SessionNotify result "
+ "to client via fd = %d (%s)",
+ engine->session_reply_fd, strerror (errno));
+ req->status = -1;
+ }
+ engine->session_reply_fd = -1;
+ }
+}
+
static void
jack_notify_all_port_interested_clients (jack_engine_t *engine, jack_client_id_t src, jack_client_id_t dst, jack_port_id_t a, jack_port_id_t b, int connected)
{
@@ -2522,7 +2751,7 @@ static int
jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client,
jack_event_t *event)
{
- char status;
+ char status=0;
/* caller must hold the graph lock */
@@ -2607,7 +2836,7 @@ jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client,
}
if (client->error) {
- status = 1;
+ status = -1;
} else {
// then we check whether there really is an error.... :)
@@ -2672,7 +2901,7 @@ jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client,
client->event_fd,
pfd[0].revents,
poll_timeout);
- status = 1;
+ status = -2;
#ifdef __linux
}
#endif
@@ -2697,7 +2926,7 @@ jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client,
event->type);
}
- if (status) {
+ if (status<0) {
client->error += JACK_ERROR_WITH_SOCKETS;
jack_engine_signal_problems (engine);
}
@@ -2705,7 +2934,7 @@ jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client,
}
DEBUG ("event delivered");
- return 0;
+ return status;
}
int
@@ -3919,7 +4148,8 @@ next:
}
client->ports = jack_slist_prepend (client->ports, port);
- jack_port_registration_notify (engine, port_id, TRUE);
+ if( client->control->active )
+ jack_port_registration_notify (engine, port_id, TRUE);
jack_unlock_graph (engine);
VERBOSE (engine, "registered port %s, offset = %u",
diff --git a/libjack/client.c b/libjack/client.c
index ef6f881..793a3c8 100644
--- a/libjack/client.c
+++ b/libjack/client.c
@@ -514,6 +514,32 @@ jack_client_handle_port_connection (jack_client_t *client, jack_event_t *event)
return 0;
}
+int
+jack_client_handle_session_callback (jack_client_t *client, jack_event_t *event)
+{
+ char prefix[32];
+ jack_session_event_t *s_event;
+
+ if (! client->control->session_cbset) {
+ return -1;
+ }
+
+ snprintf( prefix, sizeof(prefix), "%d", client->control->uid );
+
+ s_event = malloc( sizeof(jack_session_event_t) );
+ s_event->type = event->y.n;
+ s_event->session_dir = strdup( event->x.name );
+ s_event->client_uuid = strdup( prefix );
+ s_event->command_line = NULL;
+
+ client->session_cb_immediate_reply = 0;
+ client->session_cb ( s_event, client->session_cb_arg);
+
+ if (client->session_cb_immediate_reply) {
+ return 2;
+ }
+ return 1;
+}
#if JACK_USE_MACH_THREADS
static int
@@ -874,6 +900,10 @@ jack_request_client (ClientType type,
/* format connection request */
+ if( va->sess_uuid )
+ req.uuid = atoi( va->sess_uuid );
+ else
+ req.uuid = 0;
req.protocol_v = jack_protocol_version;
req.load = TRUE;
req.type = type;
@@ -1116,6 +1146,11 @@ jack_client_open_aux (const char *client_name,
client->deliver_request = oop_client_deliver_request;
client->deliver_arg = client;
+ if( va.sess_uuid )
+ client->control->uid = atoi( va.sess_uuid );
+ else
+ client->control->uid = 0U;
+
if ((ev_fd = server_event_connect (client, va.server_name)) < 0) {
goto fail;
}
@@ -1302,6 +1337,144 @@ jack_set_freewheel (jack_client_t* client, int onoff)
return jack_client_deliver_request (client, &request);
}
+int
+jack_session_reply (jack_client_t *client, jack_session_event_t *event )
+{
+ int retval = 0;
+
+ if (event->command_line) {
+ snprintf ((char *)client->control->session_command,
+ sizeof(client->control->session_command),
+ "%s", event->command_line);
+ client->control->session_flags = event->flags;
+
+ } else {
+ retval = -1;
+ }
+
+ if (pthread_self() == client->thread_id) {
+ client->session_cb_immediate_reply = 1;
+ } else {
+ jack_request_t request;
+ request.type = SessionReply;
+ request.x.client_id = client->control->id;
+
+ retval = jack_client_deliver_request(client, &request);
+ }
+
+ return retval;
+}
+
+void
+jack_session_event_free (jack_session_event_t *event)
+{
+ if (event->command_line)
+ free (event->command_line);
+
+ free ((char *)event->session_dir);
+ free ((char *)event->client_uuid);
+ free (event);
+}
+
+void
+jack_session_commands_free (jack_session_command_t *cmds)
+{
+ int i=0;
+ while(1) {
+ if (cmds[i].client_name)
+ free ((char *)cmds[i].client_name);
+ if (cmds[i].command)
+ free ((char *)cmds[i].command);
+ if (cmds[i].uuid)
+ free ((char *)cmds[i].uuid);
+ else
+ break;
+
+ i += 1;
+ }
+
+ free(cmds);
+}
+
+jack_session_command_t *
+jack_session_notify (jack_client_t* client, const char *target, jack_session_event_type_t code, const char *path )
+{
+ jack_request_t request;
+
+ jack_session_command_t *retval = NULL;
+ int num_replies = 0;
+ request.type = SessionNotify;
+ if( path )
+ snprintf( request.x.session.path, sizeof( request.x.session.path ), "%s", path );
+ else
+ request.x.session.path[0] = '\0';
+
+ if( target )
+ snprintf( request.x.session.target, sizeof( request.x.session.target ), "%s", target );
+ else
+ request.x.session.target[0] = '\0';
+
+ request.x.session.type = code;
+
+ if( (write (client->request_fd, &request, sizeof (request))
+ != sizeof (request)) ) {
+ jack_error ("cannot send request type %d to server",
+ request.type);
+ goto out;
+ }
+
+ while( 1 ) {
+ jack_client_id_t uid;
+ if (read (client->request_fd, &uid, sizeof (uid)) != sizeof (uid)) {
+ jack_error ("cannot read result for request type %d from"
+ " server (%s)", request.type, strerror (errno));
+ goto out;
+ }
+
+ num_replies += 1;
+ retval = realloc( retval, (num_replies)*sizeof(jack_session_command_t) );
+ retval[num_replies-1].client_name = malloc (JACK_CLIENT_NAME_SIZE);
+ retval[num_replies-1].command = malloc (JACK_PORT_NAME_SIZE);
+ retval[num_replies-1].uuid = malloc (16);
+
+ if ( (retval[num_replies-1].client_name == NULL)
+ ||(retval[num_replies-1].command == NULL)
+ ||(retval[num_replies-1].uuid == NULL) )
+ goto out;
+
+ if( uid == 0 )
+ break;
+
+
+ if (read (client->request_fd, (char *)retval[num_replies-1].client_name, JACK_CLIENT_NAME_SIZE)
+ != JACK_CLIENT_NAME_SIZE) {
+ jack_error ("cannot read result for request type %d from"
+ " server (%s)", request.type, strerror (errno));
+ goto out;
+ }
+ if (read (client->request_fd, (char *)retval[num_replies-1].command, JACK_PORT_NAME_SIZE)
+ != JACK_PORT_NAME_SIZE) {
+ jack_error ("cannot read result for request type %d from"
+ " server (%s)", request.type, strerror (errno));
+ goto out;
+ }
+ if (read (client->request_fd, & retval[num_replies-1].flags, sizeof(retval[num_replies-1].flags) )
+ != sizeof(retval[num_replies-1].flags) ) {
+ jack_error ("cannot read result for request type %d from"
+ " server (%s)", request.type, strerror (errno));
+ goto out;
+ }
+ snprintf( (char *)retval[num_replies-1].uuid, 16, "%d", uid );
+ }
+ free((char *)retval[num_replies-1].uuid);
+ retval[num_replies-1].uuid = NULL;
+ return retval;
+out:
+ if( retval )
+ jack_session_commands_free(retval);
+ return NULL;
+}
+
void
jack_start_freewheel (jack_client_t* client)
{
@@ -1473,6 +1646,9 @@ jack_client_process_events (jack_client_t* client)
case StopFreewheel:
jack_stop_freewheel (client);
break;
+ case SaveSession:
+ status = jack_client_handle_session_callback (client, &event );
+ break;
}
DEBUG ("client has dealt with the event, writing "
@@ -2005,11 +2181,11 @@ jack_activate (jack_client_t *client)
* usage in jack_start_thread())
*/
- char buf[JACK_THREAD_STACK_TOUCH];
+ //char buf[JACK_THREAD_STACK_TOUCH];
int i;
for (i = 0; i < JACK_THREAD_STACK_TOUCH; i++) {
- buf[i] = (char) (i & 0xff);
+ //buf[i] = (char) (i & 0xff);
}
if (client->control->type == ClientInternal ||
@@ -2450,6 +2626,20 @@ jack_set_process_thread(jack_client_t* client, JackThreadCallback callback, void
}
int
+jack_set_session_callback(jack_client_t* client, JackSessionCallback callback, void *arg)
+{
+ if (client->control->active) {
+ jack_error ("You cannot set callbacks on an active client.");
+ return -1;
+ }
+
+ client->session_cb_arg = arg;
+ client->session_cb = callback;
+ client->control->session_cbset = (callback != NULL);
+ return 0;
+}
+
+int
jack_get_process_done_fd (jack_client_t *client)
{
return client->graph_next_fd;
@@ -2469,6 +2659,39 @@ jack_on_info_shutdown (jack_client_t *client, void (*function)(jack_status_t, co
client->on_info_shutdown_arg = arg;
}
+char *
+jack_get_client_name_by_uuid( jack_client_t *client, const char *uuid )
+{
+ jack_request_t request;
+
+ char *end_ptr;
+ jack_client_id_t uuid_int = strtol( uuid, &end_ptr, 10 );
+ if( *end_ptr != '\0' )
+ return NULL;
+ request.type = GetClientByUUID;
+ request.x.client_id = uuid_int;
+ if( jack_client_deliver_request( client, &request ) )
+ return NULL;
+
+ return strdup( request.x.port_info.name );
+}
+
+int
+jack_reserve_client_name( jack_client_t *client, const char *name, const char *uuid )
+{
+ jack_request_t request;
+
+ char *end_ptr;
+ jack_client_id_t uuid_int = strtol( uuid, &end_ptr, 10 );
+ if( *end_ptr != '\0' )
+ return -1;
+ request.type = ReserveName;
+ snprintf( request.x.reservename.name, sizeof( request.x.reservename.name ),
+ "%s", name );
+ request.x.reservename.uuid = uuid_int;
+ return jack_client_deliver_request( client, &request );
+}
+
const char **
jack_get_ports (jack_client_t *client,
const char *port_name_pattern,
diff --git a/libjack/local.h b/libjack/local.h
index ef03235..1168676 100644
--- a/libjack/local.h
+++ b/libjack/local.h
@@ -35,6 +35,7 @@ struct _jack_client {
char first_active : 1;
pthread_t thread_id;
char name[JACK_CLIENT_NAME_SIZE];
+ int session_cb_immediate_reply;
#ifdef JACK_USE_MACH_THREADS
/* specific ressources for server/client real-time thread communication */
@@ -70,8 +71,10 @@ struct _jack_client {
void *freewheel_arg;
JackClientRegistrationCallback client_register;
void *client_register_arg;
- JackThreadCallback thread_cb;
+ JackThreadCallback thread_cb;
void *thread_cb_arg;
+ JackSessionCallback session_cb;
+ void *session_cb_arg;
/* external clients: set by libjack
* internal clients: set by engine */
diff --git a/python/libjack.py b/python/libjack.py
new file mode 100644
index 0000000..e64251f
--- /dev/null
+++ b/python/libjack.py
@@ -0,0 +1,363 @@
+
+from ctypes import *
+import string
+from Queue import Queue
+
+class jack_client_t(Structure):
+ pass
+
+class jack_port_t(Structure):
+ pass
+
+client_p = POINTER(jack_client_t)
+port_p = POINTER(jack_port_t)
+
+libjack = cdll.LoadLibrary( "libjack.so" )
+client_new = libjack.jack_client_new
+client_new.argtypes = [ c_char_p ]
+client_new.restype = client_p
+
+client_open = libjack.jack_client_open
+client_open.argtypes = [ c_char_p, c_uint, POINTER( c_uint ) ]
+client_open.restype = client_p
+
+client_close = libjack.jack_client_close
+client_close.argtypes = [ client_p ]
+client_close.restype = None
+
+activate = libjack.jack_activate
+activate.argtypes = [ client_p ]
+activate.restype = None
+
+deactivate = libjack.jack_deactivate
+deactivate.argtypes = [ client_p ]
+deactivate.restype = None
+
+get_ports = libjack.jack_get_ports
+get_ports.argtypes = [ client_p, c_char_p, c_char_p, c_ulong ]
+get_ports.restype = POINTER( c_char_p )
+
+port_by_name = libjack.jack_port_by_name
+port_by_name.argtypes = [ client_p, c_char_p ]
+port_by_name.restype = port_p
+
+port_get_all_connections = libjack.jack_port_get_all_connections
+port_get_all_connections.argtypes = [ client_p, port_p ]
+port_get_all_connections.restype = POINTER( c_char_p )
+
+jack_free = libjack.jack_free
+jack_free.argtypes = [ c_void_p ]
+jack_free.restype = None
+
+rename_client = libjack.jack_rename_client
+rename_client.argtypes = [ client_p, c_char_p, c_char_p ]
+rename_client.restype = c_int
+
+reserve_client_name = libjack.jack_reserve_client_name
+reserve_client_name.argtypes = [ client_p, c_char_p, c_char_p ]
+reserve_client_name.restype = c_int
+
+class jack_session_command_t( Structure ):
+ _fields_ = [ ("uuid", c_char_p ), ("clientname", c_char_p), ("command", c_char_p ), ("flags", c_int) ]
+
+session_notify = libjack.jack_session_notify
+session_notify.argtypes = [ client_p, c_char_p, c_uint, c_char_p ]
+session_notify.restype = POINTER( jack_session_command_t )
+
+session_commands_free = libjack.jack_session_commands_free
+session_commands_free.argtypes = [ POINTER( jack_session_command_t ) ]
+session_commands_free.restype = None
+
+get_client_name_by_uuid = libjack.jack_get_client_name_by_uuid
+get_client_name_by_uuid.argtypes = [ client_p, c_char_p ]
+get_client_name_by_uuid.restype = c_char_p
+
+connect = libjack.jack_connect
+connect.argtypes = [ client_p, c_char_p, c_char_p ]
+connect.restype = c_int
+
+disconnect = libjack.jack_disconnect
+disconnect.argtypes = [ client_p, c_char_p, c_char_p ]
+disconnect.restype = c_int
+
+PortRegistrationCallback = CFUNCTYPE( None, c_uint, c_int, c_void_p )
+
+set_port_registration_callback = libjack.jack_set_port_registration_callback
+set_port_registration_callback.argtypes = [ client_p, PortRegistrationCallback, c_void_p ]
+set_port_registration_callback.restype = c_int
+
+port_by_id = libjack.jack_port_by_id
+port_by_id.argtypes = [ client_p, c_uint ]
+port_by_id.restype = port_p
+
+port_name = libjack.jack_port_name
+port_name.argtypes = [ port_p ]
+port_name.restype = c_char_p
+
+port_type = libjack.jack_port_type
+port_type.argtypes = [ port_p ]
+port_type.restype = c_char_p
+
+port_flags = libjack.jack_port_flags
+port_flags.argtypes = [ port_p ]
+port_flags.restype = c_int
+
+JACK_DEFAULT_AUDIO_TYPE="32 bit float mono audio"
+JACK_DEFAULT_MIDI_TYPE="8 bit raw midi"
+
+JackPortIsInput = 0x1
+JackPortIsOutput = 0x2
+JackPortIsPhysical = 0x4
+JackPortCanMonitor = 0x8
+JackPortIsTerminal = 0x10
+
+JackSessionSave = 1
+JackSessionQuit = 2
+
+
+
+class Port( object ):
+ def __init__( self, client, name ):
+ self.client = client
+ self.name = name
+ self.portname = name.split(':')[1]
+ self.port_p = port_by_name( self.client, name )
+ self.conns = self.get_live_connections()
+
+ def get_connections( self ):
+ return self.conns
+
+ def get_live_connections( self ):
+ conns = port_get_all_connections( self.client, self.port_p )
+ if not conns:
+ return []
+
+ i=0
+ retval = []
+ while conns[i]:
+ retval.append( conns[i] )
+ i+=1
+ jack_free(conns)
+
+ return retval
+
+ def connect( self, other ):
+ connect( self.client, self.name, other )
+
+ def disconnect( self, other ):
+ disconnect( self.client, self.name, other )
+
+ def is_input( self ):
+ return (port_flags( self.port_p ) & JackPortIsInput) != 0
+
+ def is_output( self ):
+ return (port_flags( self.port_p ) & JackPortIsOutput) != 0
+
+ def is_audio( self ):
+ return (port_type( self.port_p ) == JACK_DEFAULT_AUDIO_TYPE)
+
+ def is_midi( self ):
+ return (port_type( self.port_p ) == JACK_DEFAULT_MIDI_TYPE)
+
+
+class Client( object ):
+ def __init__( self, client, name ):
+ self.client = client
+ self.name = name
+ self.ports = []
+ self.commandline = None
+ self.isinfra = False
+ self.uuid = None
+
+ def get_commandline( self ):
+ if self.commandline:
+ return self.commandline
+ else:
+ return ""
+
+ def set_commandline( self, cmdline ):
+ self.commandline = cmdline
+
+ def get_uuid( self ):
+ return self.uuid
+
+ def set_uuid( self, uuid ):
+ self.uuid = uuid
+
+ def add_port( self, portname ):
+ self.ports.append( Port( self.client, portname ) )
+
+ def rename( self, newname ):
+ rename_client( self.client, self.name, newname )
+ self.ports = []
+ ports = get_ports( self.client, newname+":.*", None, 0 )
+ self.name = newname
+
+ i=0
+ while(ports[i]):
+ self.add_port( ports[i] )
+ i+=1
+
+ jack_free( ports )
+
+ def set_infra( self, cmdline ):
+ self.isinfra = True
+ self.commandline = cmdline
+
+
+
+class JackGraph( object ):
+ def __init__( self, client, ports, uuids=[] ):
+ self.client = client
+ self.clients = {}
+ self.reserved_names = []
+
+ i=0
+ while(ports[i]):
+ port_split = ports[i].split(':')
+ if not self.clients.has_key(port_split[0]):
+ self.clients[port_split[0]] = Client( client, port_split[0] )
+
+ self.clients[port_split[0]].add_port( ports[i] )
+ i+=1
+
+ def get_client( self, name ):
+ return self.clients[name]
+
+ def get_port_list( self ):
+ retval = []
+ for c in self.clients.values():
+ for p in c.ports:
+ retval.append( p.name )
+ return retval
+
+
+
+ def check_client_name( self, client ):
+ if not client.name in self.reserved_names:
+ return
+
+ oldname = client.name
+ newname = self.get_free_name( client.name )
+
+ client.rename( newname )
+ del self.clients[oldname]
+ self.clients[newname] = client
+
+ def get_free_name( self, oldname, other_names=[] ):
+ cname_split = oldname.split('-')
+ if len(cname_split) == 1:
+ cname_prefix = cname_split[0]
+ else:
+ cname_prefix = string.join( cname_split[:-1], '-' )
+
+ num = 1
+ while ("%s-%d"%(cname_prefix,num)) in (self.clients.keys()+self.reserved_names+other_names):
+ num+=1
+
+ return ("%s-%d"%(cname_prefix,num))
+
+
+
+ def remove_client( self, name ):
+ del self.clients[name]
+ for c in self.clients.values():
+ for p in c.ports:
+ for conn in p.get_connections():
+ if conn.startswith(name+":"):
+ p.conns.remove( conn )
+
+ def remove_client_only( self, name ):
+ del self.clients[name]
+
+ def ensure_clientnames( self, names ):
+ self.reserved_names = names
+ for c in self.clients.values():
+ self.check_client_name( c )
+
+ def get_taken_names( self ):
+ return self.clients.keys() + self.reserved_names
+
+ def reserve_name( self, uuid, name ):
+ if reserve_client_name( self.client, name, uuid ):
+ raise Exception( "reservation failure" )
+ self.reserved_names.append( name )
+
+
+class NotifyReply(object):
+ def __init__( self, uuid, clientname, commandline ):
+ self.uuid = uuid
+ self.clientname = clientname
+ self.commandline = commandline
+
+
+class JackClient(object):
+ def __init__( self, name ):
+ self.client = client_open( name, 0, None )
+ if not self.client:
+ raise Exception( "got no client name" )
+ self.reg_cb = PortRegistrationCallback( self.port_registration_cb )
+ set_port_registration_callback( self.client, self.reg_cb, None )
+ self.port_queue = Queue()
+
+ activate( self.client )
+
+ def close( self ):
+ client_close( self.client )
+
+ def get_graph( self ):
+ ports = get_ports( self.client, None, None, 0 )
+ retval = JackGraph( self.client, ports )
+ jack_free( ports )
+ return retval
+
+ def rename_client( self, old, new ):
+ if rename_client( self.client, old, new ):
+ raise Exception
+
+ def port_registration_cb( self, port_id, reg, arg ):
+ port_p = port_by_id( self.client, port_id )
+ self.port_queue.put( (port_p,reg) )
+
+
+ def session_save( self, path ):
+ commands = session_notify( self.client, None, JackSessionSave, path )
+ i=0
+ retval = []
+ while( commands[i].uuid ):
+ retval.append( NotifyReply( commands[i].uuid, commands[i].clientname, commands[i].command ) )
+ i+=1
+
+ session_commands_free( commands )
+
+ return retval
+
+ def session_save_and_quit( self, path ):
+ commands = session_notify( self.client, None, JackSessionQuit, path )
+ i=0
+ retval = []
+ while( commands[i].uuid ):
+ retval.append( NotifyReply( commands[i].uuid, commands[i].clientname, commands[i].command ) )
+ i+=1
+
+ session_commands_free( commands )
+
+ return retval
+
+ def connect( self, a, b ):
+ portA_p = port_by_name( self.client, a )
+
+ if( port_flags( portA_p ) & JackPortIsOutput ):
+ connect( self.client, a, b )
+ else:
+ connect( self.client, b, a )
+
+
+
+
+
+
+
+
+
+
diff --git a/python/sessionmanager.py b/python/sessionmanager.py
new file mode 100755
index 0000000..97fe261
--- /dev/null
+++ b/python/sessionmanager.py
@@ -0,0 +1,277 @@
+#!/usr/bin/env python
+
+import libjack
+import state
+import subprocess
+from optparse import OptionParser
+from ConfigParser import SafeConfigParser
+import os
+
+try:
+ import dbus.service
+ import gobject
+ have_dbus = True
+except:
+ have_dbus = False
+
+
+SESSION_PATH=os.path.join(os.getenv("HOME"), "jackSessions")
+defaults = { "jackclientname": "sessionmanager", "sessiondir": "~/jackSessions" }
+
+class SessionManager( object ):
+ def __init__( self ):
+ self.config = SafeConfigParser( defaults )
+ self.config.read( os.path.expanduser( "~/.jacksessionrc" ) )
+ self.jackname = self.config.get( "DEFAULT", "jackclientname" )
+ self.cl = libjack.JackClient( self.jackname )
+ if self.config.has_section( "infra" ):
+ self.infra_clients = {}
+ for inf in self.config.items( "infra" ):
+ self.infra_clients[inf[0]] = inf[1]
+ else:
+ self.infra_clients = { "a2j": "a2jmidid" }
+ self.implicit_clients = [ "system" ]
+
+ self.sessiondir = os.path.expanduser( self.config.get( "DEFAULT", "sessiondir" ) ) + "/"
+ if not os.path.exists( self.sessiondir ):
+ print "Sessiondir %s does not exist. Creating it..."%self.sessiondir
+ os.mkdir( self.sessiondir )
+
+ def list_projects( self ):
+ files = os.listdir( self.sessiondir )
+ files = filter( lambda x: os.path.isdir( os.path.join( self.sessiondir, x ) ), files )
+ return files
+
+ def load_session( self, name ):
+ if not os.path.exists( self.sessiondir+name+"/session.xml" ):
+ print "Session %s does not exist"%name
+ return -1
+
+ sd = state.SessionDom( self.sessiondir+name+"/session.xml" )
+
+ g=self.cl.get_graph()
+
+ children = []
+
+ for ic in sd.get_infra_clients():
+ if not ic[0] in g.clients.keys():
+ children.append( subprocess.Popen( ic[1], shell=True ) )
+
+
+ print sd.get_client_names()
+
+ if opt.renames:
+ g.ensure_clientnames( sd.get_reg_client_names() )
+ # get graph again... renaming isnt prefect yet.
+ g=self.cl.get_graph()
+
+ # fixup names, doing this unconditionally, because
+ # a client rename might have failed.
+ sd.fixup_client_names( g )
+
+ # now we have mangled all the names, lets reserve them.
+ for (uuid, clientname) in sd.get_uuid_client_pairs():
+ print "reserving name %s"%clientname
+ g.reserve_name( uuid, clientname )
+
+ # build up list of port connections
+ conns = []
+ for p in sd.get_port_names():
+ for c in sd.get_connections_for_port( p ):
+ conns.append( (p,c) )
+ print conns
+
+ # now fire up the processes
+ for cname in sd.get_reg_client_names():
+ cmd = sd.get_commandline_for_client( cname )
+ children.append( subprocess.Popen( cmd, shell=True ) )
+
+ avail_ports = g.get_port_list()
+ # wait for ports to appear, and connect em.
+
+ while len(conns) > 0:
+ p = self.cl.port_queue.get()
+ print p[0],p[1]
+ pname = libjack.port_name(p[0])
+ for c1 in conns:
+ if c1[0]==pname:
+ if c1[1] in avail_ports:
+ self.cl.connect( pname, c1[1] )
+
+ conns.remove( c1 )
+ if (c1[1],c1[0]) in conns:
+ conns.remove( (c1[1],c1[0]) )
+
+ break
+
+ if c1[1]==pname:
+ if c1[0] in avail_ports:
+ self.cl.connect( pname, c1[0] )
+
+ conns.remove( c1 )
+ if (c1[1],c1[0]) in conns:
+ conns.remove( (c1[1],c1[0]) )
+
+ break
+
+ avail_ports.append( pname )
+
+
+ print "session restored..."
+ return 0
+
+ def quit_session( self, name ):
+ self.save_session( name, True )
+
+
+ def save_session( self, name, quit=False ):
+ if os.path.exists( self.sessiondir+name ):
+ print "session %s already exists"%name
+ return -1
+ os.mkdir( self.sessiondir+name )
+ g=self.cl.get_graph()
+ if quit:
+ notify = self.cl.session_save_and_quit( self.sessiondir+name+"/" )
+ else:
+ notify = self.cl.session_save( self.sessiondir+name+"/" )
+
+ for n in notify:
+ c = g.get_client( n.clientname )
+ c.set_commandline( n.commandline )
+ c.set_uuid( n.uuid )
+
+ sd = state.SessionDom()
+
+ for c in g.clients.values():
+ if c.get_commandline() == "":
+ if not c.name in self.infra_clients.keys()+self.implicit_clients:
+ g.remove_client( c.name )
+ elif c.name in self.implicit_clients:
+ g.remove_client_only( c.name )
+ else:
+ c.set_infra( self.infra_clients[c.name] )
+
+
+ for i in g.clients.values():
+ sd.add_client(i)
+
+ f = file( self.sessiondir+name+"/session.xml", "w" )
+ f.write( sd.get_xml() )
+ f.close()
+
+ print sd.get_xml()
+ return 0
+
+ def exit( self ):
+ self.cl.close()
+
+if have_dbus:
+ class DbusSM( dbus.service.Object ):
+ def __init__( self, sm ):
+ self.sm = sm
+ dbus.service.Object.__init__( self, None,
+ "/org/jackaudio/sessionmanager",
+ dbus.service.BusName( "org.jackaudio.sessionmanager", bus=dbus.SessionBus() ) )
+ @dbus.service.method( dbus_interface="org.jackaudio.sessionmanager",
+ in_signature="s", out_signature="i" )
+ def save_as( self, name ):
+ return self.sm.save_session( name )
+
+ @dbus.service.method( dbus_interface="org.jackaudio.sessionmanager",
+ in_signature="s", out_signature="i" )
+ def quit_as( self, name ):
+ return self.sm.quit_session( name )
+
+ @dbus.service.method( dbus_interface="org.jackaudio.sessionmanager",
+ in_signature="s", out_signature="i" )
+ def load( self, name ):
+ return self.sm.load_session( name )
+
+ @dbus.service.method( dbus_interface="org.jackaudio.sessionmanager",
+ in_signature="", out_signature="as" )
+ def list( self ):
+ return self.sm.list_projects()
+
+ @dbus.service.method( dbus_interface="org.jackaudio.sessionmanager",
+ in_signature="", out_signature="" )
+ def daemon_quit( self ):
+ loop.quit()
+
+oparser = OptionParser()
+oparser.add_option( "--nodbus", action="store_false", dest="dbus", default=have_dbus,
+ help="Dont use DBUS to issue commands to a running instance" )
+oparser.add_option( "--daemon", action="store_true", dest="daemon", default=False,
+ help="Start in daemon mode, and listen for dbus commands" )
+#oparser.add_option( "--save", action="store_true", dest="save", default=False,
+# help="Tell SessionManger to save." )
+oparser.add_option( "--saveas", action="store", type="string", dest="saveas",
+ help="Save Session As <name>" )
+
+#oparser.add_option( "--quit", action="store_true", dest="quit", default=False,
+# help="Tell SessionManager to Save And Quit" )
+
+oparser.add_option( "--list", action="store_true", dest="list", default=False,
+ help="List Projects" )
+oparser.add_option( "--quitdaemon", action="store_true", dest="quitdaemon", default=False,
+ help="Tell SessionManager Daemon to Exit" )
+oparser.add_option( "--quitas", action="store", dest="quitas", type="string",
+ help="SaveAs And Quit" )
+oparser.add_option( "--load", action="store", dest="load", type="string",
+ help="Load Session with <name>" )
+oparser.add_option( "--renames", action="store_true", dest="renames", default=False,
+ help="Allow renaming offending clients" )
+
+(opt,args) = oparser.parse_args()
+
+if not opt.dbus:
+ sm = SessionManager()
+ try:
+ if opt.saveas:
+ sm.save_session( opt.saveas )
+
+ if opt.load:
+ sm.load_session( opt.load )
+
+ if opt.quitas:
+ sm.quit_session( opt.quitas )
+ except:
+ sm.exit()
+ raise
+
+ sm.exit()
+else:
+ if opt.daemon:
+ try:
+ sm = SessionManager()
+ from dbus.mainloop.glib import DBusGMainLoop
+ DBusGMainLoop(set_as_default=True)
+ dbsm = DbusSM( sm )
+ loop = gobject.MainLoop()
+ loop.run()
+ except:
+ sm.exit()
+ raise
+
+ sm.exit()
+ else:
+ session_bus = dbus.SessionBus()
+ sm_proxy = session_bus.get_object( "org.jackaudio.sessionmanager", "/org/jackaudio/sessionmanager" )
+ sm_iface = dbus.Interface( sm_proxy, "org.jackaudio.sessionmanager" )
+ if opt.saveas:
+ sm_iface.save_as( opt.saveas )
+ if opt.quitas:
+ sm_iface.quit_as( opt.quitas )
+ if opt.load:
+ sm_iface.load( opt.load )
+ if opt.list:
+ projects = sm_iface.list()
+ for i in projects:
+ print i
+
+ if opt.quitdaemon:
+ sm_iface.quit()
+
+
+
+
+
diff --git a/python/state.py b/python/state.py
new file mode 100644
index 0000000..159f789
--- /dev/null
+++ b/python/state.py
@@ -0,0 +1,136 @@
+
+from xml.dom.minidom import getDOMImplementation, parse, Element
+import string
+
+impl = getDOMImplementation()
+
+class SessionDom( object ):
+ def __init__( self, filename=None ):
+ if filename:
+ self.dom = parse( filename )
+ else:
+ self.dom = impl.createDocument(None,"jacksession",None)
+
+ def add_client( self, client ):
+ cl_elem = Element( "jackclient" )
+ cl_elem.setAttribute( "cmdline", client.get_commandline() )
+ cl_elem.setAttribute( "jackname", client.name )
+ if client.get_uuid():
+ cl_elem.setAttribute( "uuid", client.get_uuid() )
+ if client.isinfra:
+ cl_elem.setAttribute( "infra", "True" )
+ else:
+ cl_elem.setAttribute( "infra", "False" )
+
+ for p in client.ports:
+ po_elem = Element( "port" )
+ po_elem.setAttribute( "name", p.name )
+ po_elem.setAttribute( "shortname", p.portname )
+
+ for c in p.get_connections():
+ c_elem = Element( "conn" )
+ c_elem.setAttribute( "dst", c )
+
+ po_elem.appendChild( c_elem )
+
+ cl_elem.appendChild( po_elem )
+
+ self.dom.documentElement.appendChild( cl_elem )
+
+ def get_xml(self):
+ return self.dom.toprettyxml()
+
+ def get_client_names(self):
+ retval = []
+ doc = self.dom.documentElement
+ for c in doc.getElementsByTagName( "jackclient" ):
+ retval.append( c.getAttribute( "jackname" ) )
+
+ return retval
+
+ def get_reg_client_names(self):
+ retval = []
+ doc = self.dom.documentElement
+ for c in doc.getElementsByTagName( "jackclient" ):
+ if c.getAttribute( "infra" ) != "True":
+ retval.append( c.getAttribute( "jackname" ) )
+
+ return retval
+
+ def get_infra_clients(self):
+ retval = []
+ doc = self.dom.documentElement
+ for c in doc.getElementsByTagName( "jackclient" ):
+ if c.getAttribute( "infra" ) == "True":
+ retval.append( (c.getAttribute( "jackname" ), c.getAttribute( "cmdline" ) ) )
+
+ return retval
+ def get_port_names(self):
+ retval = []
+ doc = self.dom.documentElement
+ for c in doc.getElementsByTagName( "port" ):
+ retval.append( c.getAttribute( "name" ) )
+ return retval
+
+ def get_connections_for_port( self, portname ):
+ retval = []
+ doc = self.dom.documentElement
+ for c in doc.getElementsByTagName( "port" ):
+ if c.getAttribute( "name" ) == portname:
+ for i in c.getElementsByTagName( "conn" ):
+ retval.append( i.getAttribute( "dst" ) )
+ return retval
+
+ def get_commandline_for_client( self, name ):
+ doc = self.dom.documentElement
+ for c in doc.getElementsByTagName( "jackclient" ):
+ if c.getAttribute( "jackname" ) == name:
+ return c.getAttribute( "cmdline" )
+
+ def get_uuid_client_pairs( self ):
+ retval = []
+ doc = self.dom.documentElement
+ for c in doc.getElementsByTagName( "jackclient" ):
+ if c.getAttribute( "infra" ) != "True":
+ retval.append( (c.getAttribute( "uuid" ), c.getAttribute( "jackname" )) )
+
+ return retval
+
+ def renameclient( self, celem, newname ):
+ doc = self.dom.documentElement
+ celem.setAttribute( "jackname", newname )
+ for pelem in celem.getElementsByTagName( "port" ):
+ old_pname = pelem.getAttribute( "name" )
+ pname_split = old_pname.split(":")
+ pname_split[0] = newname
+ new_pname = string.join( pname_split, ":" )
+ pelem.setAttribute( "name", new_pname )
+ for dst in doc.getElementsByTagName( "conn" ):
+ if dst.getAttribute( "dst" ) == old_pname:
+ dst.setAttribute( "dst", new_pname )
+
+
+
+ def fixup_client_names( self, graph ):
+ doc = self.dom.documentElement
+ for c in doc.getElementsByTagName( "jackclient" ):
+ if c.getAttribute( "infra" ) == "True":
+ continue
+ cname = c.getAttribute( "jackname" )
+ if cname in graph.get_taken_names():
+ free_name = graph.get_free_name( cname, self.get_reg_client_names() )
+ print "name taken %s.. reallocate to %s"%(cname, free_name )
+ self.renameclient( c, free_name )
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 9ac94c4..6616f05 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -38,6 +38,7 @@ bin_PROGRAMS = jack_load \
jack_alias \
jack_bufsize \
jack_samplerate \
+ jack_session_notify \
jack_wait \
$(JACK_TRANSPORT) \
$(NETJACK_TOOLS)
@@ -96,6 +97,10 @@ jack_samplerate_SOURCES = samplerate.c
jack_samplerate_LDFLAGS = @OS_LDFLAGS@
jack_samplerate_LDADD = $(top_builddir)/libjack/libjack.la
+jack_session_notify_SOURCES = session_notify.c
+jack_session_notify_LDFLAGS = @OS_LDFLAGS@
+jack_session_notify_LDADD = $(top_builddir)/libjack/libjack.la
+
if HAVE_READLINE
jack_transport_SOURCES = transport.c
jack_transport_LDFLAGS = -lreadline @READLINE_DEPS@ @OS_LDFLAGS@
diff --git a/tools/connect.c b/tools/connect.c
index 23189a3..7ddc7ba 100644
--- a/tools/connect.c
+++ b/tools/connect.c
@@ -27,6 +27,7 @@
#include <config.h>
#include <jack/jack.h>
+#include <jack/session.h>
#define TRUE 1
#define FALSE 0
@@ -49,6 +50,7 @@ show_usage (char *my_name)
fprintf (stderr, "For more information see http://jackaudio.org/\n");
}
+
int
main (int argc, char *argv[])
{
@@ -63,6 +65,9 @@ main (int argc, char *argv[])
jack_port_t *dst_port = 0;
jack_port_t *port1 = 0;
jack_port_t *port2 = 0;
+ char portA[300];
+ char portB[300];
+ int use_uuid=0;
int connecting, disconnecting;
int port1_flags, port2_flags;
int rc = 1;
@@ -71,16 +76,20 @@ main (int argc, char *argv[])
{ "server", 1, 0, 's' },
{ "help", 0, 0, 'h' },
{ "version", 0, 0, 'v' },
+ { "uuid", 0, 0, 'u' },
{ 0, 0, 0, 0 }
};
- while ((c = getopt_long (argc, argv, "s:hv", long_options, &option_index)) >= 0) {
+ while ((c = getopt_long (argc, argv, "s:hvu", long_options, &option_index)) >= 0) {
switch (c) {
case 's':
server_name = (char *) malloc (sizeof (char) * strlen(optarg));
strcpy (server_name, optarg);
options |= JackServerName;
break;
+ case 'u':
+ use_uuid = 1;
+ break;
case 'h':
show_usage (my_name);
return 1;
@@ -123,12 +132,48 @@ main (int argc, char *argv[])
/* find the two ports */
- if ((port1 = jack_port_by_name(client, argv[argc-1])) == 0) {
- fprintf (stderr, "ERROR %s not a valid port\n", argv[argc-1]);
+ if( use_uuid ) {
+ char *tmpname;
+ char *clientname;
+ char *portname;
+ tmpname = strdup( argv[argc-1] );
+ portname = strchr( tmpname, ':' );
+ portname[0] = '\0';
+ portname+=1;
+ clientname = jack_get_client_name_by_uuid( client, tmpname );
+ if( clientname ) {
+
+ snprintf( portA, sizeof(portA), "%s:%s", clientname, portname );
+ jack_free( clientname );
+ } else {
+ snprintf( portA, sizeof(portA), "%s", argv[argc-1] );
+ }
+ free( tmpname );
+
+ tmpname = strdup( argv[argc-2] );
+ portname = strchr( tmpname, ':' );
+ portname[0] = '\0';
+ portname+=1;
+ clientname = jack_get_client_name_by_uuid( client, tmpname );
+ if( clientname ) {
+ snprintf( portB, sizeof(portB), "%s:%s", clientname, portname );
+ jack_free( clientname );
+ } else {
+ snprintf( portB, sizeof(portB), "%s", argv[argc-2] );
+ }
+
+ free( tmpname );
+
+ } else {
+ snprintf( portA, sizeof(portA), "%s", argv[argc-1] );
+ snprintf( portB, sizeof(portB), "%s", argv[argc-2] );
+ }
+ if ((port1 = jack_port_by_name(client, portA)) == 0) {
+ fprintf (stderr, "ERROR %s not a valid port\n", portA);
goto exit;
}
- if ((port2 = jack_port_by_name(client, argv[argc-2])) == 0) {
- fprintf (stderr, "ERROR %s not a valid port\n", argv[argc-2]);
+ if ((port2 = jack_port_by_name(client, portB)) == 0) {
+ fprintf (stderr, "ERROR %s not a valid port\n", portB);
goto exit;
}
diff --git a/tools/session_notify.c b/tools/session_notify.c
new file mode 100644
index 0000000..67170cb
--- /dev/null
+++ b/tools/session_notify.c
@@ -0,0 +1,180 @@
+/*
+ * session_notify.c -- ultra minimal session manager
+ *
+ * Copyright (C) 2010 Torben Hohn.
+ *
+ * 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, or
+ * (at your option) any later version.
+ *
+ * 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 <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <jack/jack.h>
+#include <jack/jslist.h>
+#include <jack/transport.h>
+#include <jack/session.h>
+
+char *package; /* program name */
+jack_client_t *client;
+
+jack_session_event_type_t notify_type;
+char *save_path = NULL;
+
+void jack_shutdown(void *arg)
+{
+ fprintf(stderr, "JACK shut down, exiting ...\n");
+ exit(1);
+}
+
+void signal_handler(int sig)
+{
+ jack_client_close(client);
+ fprintf(stderr, "signal received, exiting ...\n");
+ exit(0);
+}
+
+void parse_arguments(int argc, char *argv[])
+{
+
+ /* basename $0 */
+ package = strrchr(argv[0], '/');
+ if (package == 0)
+ package = argv[0];
+ else
+ package++;
+
+ if (argc==2) {
+ if( !strcmp( argv[1], "quit" ) ) {
+ notify_type = JackSessionSaveAndQuit;
+ return;
+ }
+ }
+ if (argc==3) {
+ if( !strcmp( argv[1], "save" ) ) {
+ notify_type = JackSessionSave;
+ save_path = argv[2];
+ return;
+ }
+
+ }
+ fprintf(stderr, "usage: %s quit|save [path]\n", package);
+ exit(9);
+}
+
+typedef struct {
+ char name[32];
+ char uuid[16];
+} uuid_map_t;
+
+JSList *uuid_map = NULL;
+
+void add_uuid_mapping( const char *uuid ) {
+ char *clientname = jack_get_client_name_by_uuid( client, uuid );
+ if( !clientname ) {
+ printf( "error... cant find client for uuid" );
+ return;
+ }
+
+ uuid_map_t *mapping = malloc( sizeof(uuid_map_t) );
+ snprintf( mapping->uuid, sizeof(mapping->uuid), "%s", uuid );
+ snprintf( mapping->name, sizeof(mapping->name), "%s", clientname );
+ uuid_map = jack_slist_append( uuid_map, mapping );
+}
+
+char *map_port_name_to_uuid_port( const char *port_name )
+{
+ JSList *node;
+ char retval[300];
+ char *port_component = strchr( port_name,':' );
+ char *client_component = strdup( port_name );
+ strchr( client_component, ':' )[0] = '\0';
+
+ sprintf( retval, "%s", port_name );
+
+ for( node=uuid_map; node; node=jack_slist_next(node) ) {
+ uuid_map_t *mapping = node->data;
+ if( !strcmp( mapping->name, client_component ) ) {
+ sprintf( retval, "%s%s", mapping->uuid, port_component );
+ break;
+ }
+ }
+
+ return strdup(retval);
+}
+
+int main(int argc, char *argv[])
+{
+ parse_arguments(argc, argv);
+ jack_session_command_t *retval;
+ int k,i,j;
+
+
+ /* become a JACK client */
+ if ((client = jack_client_open(package, JackNullOption, NULL)) == 0) {
+ fprintf(stderr, "JACK server not running?\n");
+ exit(1);
+ }
+
+ signal(SIGQUIT, signal_handler);
+ signal(SIGTERM, signal_handler);
+ signal(SIGHUP, signal_handler);
+ signal(SIGINT, signal_handler);
+
+ jack_on_shutdown(client, jack_shutdown, 0);
+
+ jack_activate(client);
+
+
+ retval = jack_session_notify( client, NULL, notify_type, save_path );
+ for(i=0; retval[i].uuid; i++ ) {
+ printf( "%s &\n", retval[i].command );
+ add_uuid_mapping(retval[i].uuid);
+ }
+
+ printf( "sleep 10\n" );
+
+ for(k=0; retval[k].uuid; k++ ) {
+
+ char* port_regexp = alloca( jack_client_name_size()+3 );
+ char* client_name = jack_get_client_name_by_uuid( client, retval[k].uuid );
+ snprintf( port_regexp, jack_client_name_size()+3, "%s:.*", client_name );
+ jack_free(client_name);
+ const char **ports = jack_get_ports( client, port_regexp, NULL, 0 );
+ if( !ports ) {
+ continue;
+ }
+ for (i = 0; ports[i]; ++i) {
+ const char **connections;
+ if ((connections = jack_port_get_all_connections (client, jack_port_by_name(client, ports[i]))) != 0) {
+ for (j = 0; connections[j]; j++) {
+ char *src = map_port_name_to_uuid_port( ports[i] );
+ char *dst = map_port_name_to_uuid_port( connections[j] );
+ printf( "jack_connect -u \"%s\" \"%s\"\n", src, dst );
+ }
+ jack_free (connections);
+ }
+ }
+ jack_free(ports);
+
+ }
+ jack_session_commands_free(retval);
+
+ jack_client_close(client);
+
+ return 0;
+}