git-svn-id: svn+ssh://jackaudio.org/trunk/jack@806 0c269be4-1314-0410-8aa9-9f06e86f4224tags/0.109.0
| @@ -15,7 +15,7 @@ dnl changes are made | |||
| dnl --- | |||
| JACK_MAJOR_VERSION=0 | |||
| JACK_MINOR_VERSION=99 | |||
| JACK_MICRO_VERSION=13 | |||
| JACK_MICRO_VERSION=14 | |||
| dnl --- | |||
| dnl HOWTO: updating the jack protocol version | |||
| @@ -5,9 +5,15 @@ CLEANFILES=doxygen-build.stamp | |||
| DOX=reference.doxygen | |||
| DOXSOURCES=mainpage.dox transport.dox porting.dox fsm.png fsm.eps \ | |||
| ../jack/jack.h ../jack/types.h ../jack/transport.h \ | |||
| ../jack/ringbuffer.h ../jack/thread.h ../jack/port.h \ | |||
| ../example-clients/simple_client.c ../example-clients/inprocess.c | |||
| ../example-clients/inprocess.c \ | |||
| ../example-clients/simple_client.c \ | |||
| ../jack/intclient.h \ | |||
| ../jack/jack.h \ | |||
| ../jack/port.h \ | |||
| ../jack/ringbuffer.h \ | |||
| ../jack/thread.h \ | |||
| ../jack/transport.h \ | |||
| ../jack/types.h | |||
| EXTRA_DIST=mainpage.dox transport.dox fsm.png fsm.eps porting.dox | |||
| @@ -63,7 +63,7 @@ programs are not all running synchronously. | |||
| Using JACK within your program is very simple, and typically consists | |||
| of just: | |||
| - calling @ref jack_client_open to connect to the JACK server. | |||
| - calling @ref jack_client_open() to connect to the JACK server. | |||
| - registering "ports" to enable data to be moved to and from | |||
| your application. | |||
| - registering a "process callback" which will be called at the | |||
| @@ -83,6 +83,8 @@ internal client "plugin" that runs within the JACK server process. | |||
| The JACK programming interfaces are described in several header files: | |||
| - @ref jack.h "<jack/jack.h>" defines most of the JACK interfaces. | |||
| - @ref intclient.h "<jack/intclient.h>" defines interfaces for | |||
| loading and unloading JACK internal clients. | |||
| - @ref ringbuffer.h "<jack/ringbuffer.h>" defines a simple API for | |||
| using lock-free ringbuffers. These are a good way to pass data | |||
| between threads, when streaming realtime data to slower media, like | |||
| @@ -365,13 +365,14 @@ WARN_LOGFILE = | |||
| INPUT = @top_srcdir@/doc/mainpage.dox \ | |||
| @top_srcdir@/doc/transport.dox \ | |||
| @top_srcdir@/doc/porting.dox \ | |||
| @top_srcdir@/example-clients/inprocess.c \ | |||
| @top_srcdir@/example-clients/simple_client.c \ | |||
| @top_srcdir@/jack/intclient.h \ | |||
| @top_srcdir@/jack/jack.h \ | |||
| @top_srcdir@/jack/types.h \ | |||
| @top_srcdir@/jack/transport.h \ | |||
| @top_srcdir@/jack/ringbuffer.h \ | |||
| @top_srcdir@/jack/thread.h \ | |||
| @top_srcdir@/example-clients/simple_client.c \ | |||
| @top_srcdir@/example-clients/inprocess.c | |||
| @top_srcdir@/jack/transport.h \ | |||
| @top_srcdir@/jack/types.h | |||
| # If the value of the INPUT tag contains directories, you can use the | |||
| # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp | |||
| @@ -48,16 +48,16 @@ inprocess (jack_nframes_t nframes, void *arg) | |||
| /** | |||
| * This required entry point is called after the client is loaded by | |||
| * jack_internal_client_new(). | |||
| * jack_internal_client_load(). | |||
| * | |||
| * @param client pointer to JACK client structure. | |||
| * @param so_data character string passed from jack_internal_client_new(). | |||
| * @param load_init character string passed to the load operation. | |||
| * | |||
| * @return 0 if successful; otherwise jack_finish() will be called and | |||
| * the client terminated immediately. | |||
| */ | |||
| int | |||
| jack_initialize (jack_client_t *client, const char *so_data) | |||
| jack_initialize (jack_client_t *client, const char *load_init) | |||
| { | |||
| port_pair_t *pp = malloc (sizeof (port_pair_t)); | |||
| @@ -96,7 +96,7 @@ jack_initialize (jack_client_t *client, const char *so_data) | |||
| /** | |||
| * This required entry point is called immediately before the client | |||
| * is unloaded, which could happen due to a call to | |||
| * jack_internal_client_close(), or a nonzero return from either | |||
| * jack_internal_client_unload(), or a nonzero return from either | |||
| * jack_initialize() or inprocess(). | |||
| * | |||
| * @param arg the same parameter provided to inprocess(). | |||
| @@ -122,11 +122,11 @@ timecode (jack_transport_state_t state, jack_nframes_t nframes, | |||
| /* after internal client loaded */ | |||
| int | |||
| jack_initialize (jack_client_t *client, const char *arg) | |||
| jack_initialize (jack_client_t *client, const char *load_init) | |||
| { | |||
| JackTimebaseCallback callback = timebbt; | |||
| int rc = sscanf(arg, " %f/%f, %lf bpm ", &time_beats_per_bar, | |||
| int rc = sscanf(load_init, " %f/%f, %lf bpm ", &time_beats_per_bar, | |||
| &time_beat_type, &time_beats_per_minute); | |||
| if (rc > 0) { | |||
| @@ -134,8 +134,8 @@ jack_initialize (jack_client_t *client, const char *arg) | |||
| time_beats_per_bar, time_beat_type, | |||
| time_beats_per_minute); | |||
| } else { | |||
| int len = strlen(arg); | |||
| if ((len > 0) && (strncmp(arg, "timecode", len) == 0)) | |||
| int len = strlen(load_init); | |||
| if ((len > 0) && (strncmp(load_init, "timecode", len) == 0)) | |||
| callback = timecode; | |||
| } | |||
| @@ -1,34 +1,169 @@ | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| #include <signal.h> | |||
| #include <unistd.h> | |||
| #include <getopt.h> | |||
| #include <jack/jack.h> | |||
| #include <jack/intclient.h> | |||
| jack_client_t *client; | |||
| jack_intclient_t intclient; | |||
| char *client_name; | |||
| char *intclient_name; | |||
| char *load_name; | |||
| char *load_init = NULL; | |||
| char *server_name = NULL; | |||
| int wait_opt = 0; | |||
| void | |||
| signal_handler (int sig) | |||
| { | |||
| jack_status_t status; | |||
| fprintf (stderr, "signal received, unloading..."); | |||
| status = jack_internal_client_unload (client, intclient); | |||
| if (status & JackFailure) | |||
| fprintf (stderr, "(failed), status = 0x%x\n", status); | |||
| else | |||
| fprintf (stderr, "(succeeded)\n"); | |||
| jack_client_close (client); | |||
| exit (0); | |||
| } | |||
| void | |||
| show_usage () | |||
| { | |||
| fprintf (stderr, "usage: %s [ options ] client-name [ load-name " | |||
| "[ init-string]]\n\noptions:\n", client_name); | |||
| fprintf (stderr, | |||
| "\t-h, --help \t\t print help message\n" | |||
| "\t-i, --init string\t initialize string\n" | |||
| "\t-s, --server name\t select JACK server\n" | |||
| "\t-w, --wait \t\t wait for signal, then unload\n" | |||
| "\n" | |||
| ); | |||
| } | |||
| int | |||
| parse_args (int argc, char *argv[]) | |||
| { | |||
| int c; | |||
| int option_index = 0; | |||
| char *short_options = "hi:s:w"; | |||
| struct option long_options[] = { | |||
| { "help", 0, 0, 'h' }, | |||
| { "init", required_argument, 0, 'i' }, | |||
| { "server", required_argument, 0, 's' }, | |||
| { "wait", 0, 0, 'w' }, | |||
| { 0, 0, 0, 0 } | |||
| }; | |||
| client_name = strrchr(argv[0], '/'); | |||
| if (client_name == NULL) { | |||
| client_name = argv[0]; | |||
| } else { | |||
| client_name++; | |||
| } | |||
| while ((c = getopt_long (argc, argv, short_options, long_options, | |||
| &option_index)) >= 0) { | |||
| switch (c) { | |||
| case 'i': | |||
| load_init = optarg; | |||
| break; | |||
| case 's': | |||
| server_name = optarg; | |||
| break; | |||
| case 'w': | |||
| wait_opt = 1; | |||
| break; | |||
| case 'h': | |||
| default: | |||
| show_usage (); | |||
| return 1; | |||
| } | |||
| } | |||
| if (optind == argc) { /* no positional args? */ | |||
| show_usage (); | |||
| return 1; | |||
| } | |||
| if (optind < argc) | |||
| load_name = intclient_name = argv[optind++]; | |||
| if (optind < argc) | |||
| load_name = argv[optind++]; | |||
| if (optind < argc) | |||
| load_init = argv[optind++]; | |||
| //fprintf (stderr, "client-name = `%s', load-name = `%s', " | |||
| // "load-init = `%s', wait = %d\n", | |||
| // intclient_name, load_name, load_init, wait_opt); | |||
| return 0; /* args OK */ | |||
| } | |||
| int | |||
| main (int argc, char *argv[]) | |||
| { | |||
| char *name; | |||
| char *so_name; | |||
| char *so_data; | |||
| jack_status_t status; | |||
| if (argc < 3) { | |||
| fprintf (stderr, "usage: %s client-name so-name [ so-data ]\n", argv[0]); | |||
| return -1; | |||
| /* parse and validate command arguments */ | |||
| if (parse_args (argc, argv)) | |||
| exit (1); /* invalid command line */ | |||
| /* first, become a JACK client */ | |||
| client = jack_client_open (client_name, JackServerName, | |||
| &status, server_name); | |||
| if (client == NULL) { | |||
| fprintf (stderr, "jack_client_open() failed, status = 0x%x\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); | |||
| } | |||
| name = argv[1]; | |||
| so_name = argv[2]; | |||
| if (argc < 4) { | |||
| so_data = ""; | |||
| } else { | |||
| so_data = argv[3]; | |||
| /* then, load the internal client */ | |||
| intclient = jack_internal_client_load (client, intclient_name, | |||
| (JackLoadName|JackLoadInit), | |||
| &status, load_name, load_init); | |||
| if (status & JackFailure) { | |||
| fprintf (stderr, "could not load %s, status = %d\n", | |||
| load_name, status); | |||
| return 2; | |||
| } | |||
| if (jack_internal_client_new (name, so_name, so_data) != 0) { | |||
| fprintf (stderr, "could not load %s\n", so_name); | |||
| return -1; | |||
| } else { | |||
| fprintf (stdout, "%s is running.\n", name); | |||
| return 0; | |||
| if (status & JackNameNotUnique) { | |||
| intclient_name = | |||
| jack_get_internal_client_name (client, intclient); | |||
| fprintf (stderr, "unique internal client name `%s' assigned\n", | |||
| intclient_name); | |||
| } | |||
| fprintf (stdout, "%s is running.\n", load_name); | |||
| if (wait_opt) { | |||
| /* define a signal handler to unload the client, then | |||
| * wait for it to exit */ | |||
| signal (SIGQUIT, signal_handler); | |||
| signal (SIGTERM, signal_handler); | |||
| signal (SIGHUP, signal_handler); | |||
| signal (SIGINT, signal_handler); | |||
| while (1) { | |||
| sleep (1); | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| @@ -1,9 +1,74 @@ | |||
| #include <string.h> | |||
| #include <stdio.h> | |||
| #include <jack/jack.h> | |||
| #include <jack/intclient.h> | |||
| int | |||
| main (int argc, char *argv[]) | |||
| { | |||
| jack_internal_client_close (argv[1]); | |||
| char *my_name; | |||
| char *client_name; | |||
| jack_client_t *client; | |||
| jack_status_t status; | |||
| jack_intclient_t intclient; | |||
| /* validate args */ | |||
| if ((argc < 2) || (argc > 3)) { | |||
| fprintf (stderr, "usage: %s client-name [ server-name ]]\n", | |||
| argv[0]); | |||
| return 1; | |||
| } | |||
| /* use `basename $0` for my own client name */ | |||
| my_name = strrchr(argv[0], '/'); | |||
| if (my_name == 0) { | |||
| my_name = argv[0]; | |||
| } else { | |||
| my_name++; | |||
| } | |||
| /* first, become a JACK client */ | |||
| if (argc > 2) { | |||
| client = jack_client_open (my_name, | |||
| (JackServerName|JackNoStartServer), | |||
| &status, argv[2]); | |||
| } else { | |||
| client = jack_client_open (my_name, JackNoStartServer, &status); | |||
| } | |||
| if (client == NULL) { | |||
| if (status & JackServerFailed) { | |||
| fprintf (stderr, "JACK server not running.\n"); | |||
| } else { | |||
| fprintf (stderr, "JACK open failed, status = 0x%0x\n", | |||
| status); | |||
| } | |||
| exit (1); | |||
| } | |||
| /* then, get the internal client handle */ | |||
| client_name = argv[1]; | |||
| intclient = jack_internal_client_handle (client, client_name, &status); | |||
| if (status & JackFailure) { | |||
| fprintf (stderr, "client %s not found.\n", client_name); | |||
| exit (2); | |||
| } | |||
| /* now, unload the internal client */ | |||
| status = jack_internal_client_unload (client, intclient); | |||
| if (status & JackFailure) { | |||
| if (status & JackNoSuchClient) { | |||
| fprintf (stderr, "client %s is gone.\n", | |||
| client_name); | |||
| } else { | |||
| fprintf (stderr, "could not unload %s, returns 0x%0x\n", | |||
| client_name, status); | |||
| } | |||
| exit (3); | |||
| } else { | |||
| fprintf (stdout, "%s unloaded.\n", client_name); | |||
| } | |||
| return 0; | |||
| } | |||
| @@ -37,6 +37,7 @@ int | |||
| main (int argc, char *argv[]) | |||
| { | |||
| jack_client_t *client; | |||
| jack_status_t status; | |||
| const char **ports, **connections; | |||
| unsigned int i, j; | |||
| int show_con = 0; | |||
| @@ -87,10 +88,19 @@ main (int argc, char *argv[]) | |||
| } | |||
| } | |||
| /* try to become a client of the JACK server */ | |||
| /* Open a client connection to the JACK server. Starting a | |||
| * new server only to list its ports seems pointless, so we | |||
| * specify JackNoStartServer. */ | |||
| //JOQ: need a new server name option | |||
| if ((client = jack_client_new ("lsp")) == 0) { | |||
| fprintf (stderr, "jack server not running?\n"); | |||
| client = jack_client_open ("lsp", JackNoStartServer, &status); | |||
| if (client == NULL) { | |||
| if (status & JackServerFailed) { | |||
| fprintf (stderr, "JACK server not running\n"); | |||
| } else { | |||
| fprintf (stderr, "jack_client_open() failed, " | |||
| "status = 0x%x\n", status); | |||
| } | |||
| return 1; | |||
| } | |||
| @@ -93,7 +93,7 @@ main (int argc, char *argv[]) | |||
| client = jack_client_open (client_name, options, &status, server_name); | |||
| if (client == NULL) { | |||
| fprintf (stderr, "jack_client_open() failed, status = %d\n", | |||
| fprintf (stderr, "jack_client_open() failed, status = 0x%x\n", | |||
| status); | |||
| if (status & JackServerFailed) { | |||
| fprintf (stderr, "Unable to connect to JACK server\n"); | |||
| @@ -3,6 +3,7 @@ MAINTAINERCLEANFILES = Makefile.in version.h | |||
| libjackincludedir = $(includedir)/jack | |||
| libjackinclude_HEADERS = \ | |||
| intclient.h \ | |||
| jack.h \ | |||
| ringbuffer.h \ | |||
| thread.h \ | |||
| @@ -26,4 +27,5 @@ noinst_HEADERS = \ | |||
| shm.h \ | |||
| start.h \ | |||
| unlock.h \ | |||
| varargs.h \ | |||
| version.h | |||
| @@ -162,6 +162,12 @@ int jack_engine_load_driver (jack_engine_t *engine, | |||
| JSList * driver_params); | |||
| void jack_dump_configuration(jack_engine_t *engine, int take_lock); | |||
| /* private engine functions */ | |||
| void jack_engine_reset_rolling_usecs (jack_engine_t *engine); | |||
| int internal_client_request (void* ptr, jack_request_t *request); | |||
| int jack_get_fifo_fd (jack_engine_t *engine, | |||
| unsigned int which_fifo); | |||
| extern jack_client_internal_t * | |||
| jack_client_internal_by_id (jack_engine_t *engine, jack_client_id_t id); | |||
| @@ -187,4 +193,11 @@ static inline unsigned int jack_power_of_two (unsigned int n) | |||
| return !(n & (n - 1)); | |||
| } | |||
| /* Internal port handling interfaces for JACK engine. */ | |||
| void jack_port_clear_connections (jack_engine_t *engine, | |||
| jack_port_internal_t *port); | |||
| 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); | |||
| #endif /* __jack_engine_h__ */ | |||
| @@ -0,0 +1,130 @@ | |||
| /* | |||
| * Copyright (C) 2004 Jack O'Quin | |||
| * | |||
| * 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. | |||
| * | |||
| * $Id$ | |||
| */ | |||
| #ifndef __jack_intclient_h__ | |||
| #define __jack_intclient_h__ | |||
| #ifdef __cplusplus | |||
| extern "C" { | |||
| #endif | |||
| #include <jack/types.h> | |||
| /** | |||
| * Get an internal client's name. This is useful when @ref | |||
| * JackUseExactName was not specified on jack_internal_client_load() | |||
| * and @ref JackNameNotUnique status was returned. In that case, the | |||
| * actual name will differ from the @a client_name requested. | |||
| * | |||
| * @param client requesting JACK client's handle. | |||
| * | |||
| * @param intclient handle returned from jack_internal_client_load() | |||
| * or jack_internal_client_handle(). | |||
| * | |||
| * @return NULL if unsuccessful, otherwise pointer to the internal | |||
| * client name obtained from the heap via malloc(). The caller should | |||
| * free() this storage when no longer needed. | |||
| */ | |||
| char *jack_get_internal_client_name (jack_client_t *client, | |||
| jack_intclient_t intclient); | |||
| /** | |||
| * Return the @ref jack_intclient_t handle for an internal client | |||
| * running in the JACK server. | |||
| * | |||
| * @param client requesting JACK client's handle. | |||
| * | |||
| * @param client_name for the internal client of no more than | |||
| * jack_client_name_size() characters. The name scope is local to the | |||
| * current server. | |||
| * | |||
| * @param status (if non-NULL) an address for JACK to return | |||
| * information from this operation. This status word is formed by | |||
| * OR-ing together the relevant @ref JackStatus bits. | |||
| * | |||
| * @return Opaque internal client handle if successful. If 0, the | |||
| * internal client was not found, and @a *status includes the @ref | |||
| * JackNoSuchClient and @ref JackFailure bits. | |||
| */ | |||
| jack_intclient_t jack_internal_client_handle (jack_client_t *client, | |||
| const char *client_name, | |||
| jack_status_t *status); | |||
| /** | |||
| * Load an internal client into the JACK server. | |||
| * | |||
| * Internal clients run inside the JACK server process. They can use | |||
| * most of the same functions as external clients. Each internal | |||
| * client is built as a shared object module, which must declare | |||
| * jack_initialize() and jack_finish() entry points called at load and | |||
| * unload times. See @ref inprocess.c for an example. | |||
| * | |||
| * @param client loading JACK client's handle. | |||
| * | |||
| * @param client_name of at most jack_client_name_size() characters | |||
| * for the internal client to load. The name scope is local to the | |||
| * current server. | |||
| * | |||
| * @param options formed by OR-ing together @ref JackOptions bits. | |||
| * Only the @ref JackLoadOptions bits are valid. | |||
| * | |||
| * @param status (if non-NULL) an address for JACK to return | |||
| * information from the load operation. This status word is formed by | |||
| * OR-ing together the relevant @ref JackStatus bits. | |||
| * | |||
| * <b>Optional parameters:</b> depending on corresponding [@a options | |||
| * bits] additional parameters may follow @a status (in this order). | |||
| * | |||
| * @arg [@ref JackLoadName] <em>(char *) load_name</em> is the shared | |||
| * object file from which to load the new internal client (otherwise | |||
| * use the @a client_name). | |||
| * | |||
| * @arg [@ref JackLoadInit] <em>(char *) load_init</em> an arbitary | |||
| * string passed to the internal client's jack_initialize() routine | |||
| * (otherwise NULL), of no more than @ref JACK_LOAD_INIT_LIMIT bytes. | |||
| * | |||
| * @return Opaque internal client handle if successful. If this is 0, | |||
| * the load operation failed, the internal client was not loaded, and | |||
| * @a *status includes the @ref JackFailure bit. | |||
| */ | |||
| jack_intclient_t jack_internal_client_load (jack_client_t *client, | |||
| const char *client_name, | |||
| jack_options_t options, | |||
| jack_status_t *status, ...); | |||
| /** | |||
| * Unload an internal client from a JACK server. This calls the | |||
| * intclient's jack_finish() entry point then removes it. See @ref | |||
| * inprocess.c for an example. | |||
| * | |||
| * @param client unloading JACK client's handle. | |||
| * | |||
| * @param intclient handle returned from jack_internal_client_load() or | |||
| * jack_internal_client_attach(). | |||
| * | |||
| * @return 0 if successful, otherwise @ref JackStatus bits. | |||
| */ | |||
| jack_status_t jack_internal_client_unload (jack_client_t *client, | |||
| jack_intclient_t intclient); | |||
| #ifdef __cplusplus | |||
| } | |||
| #endif | |||
| #endif /* __jack_intclient_h__ */ | |||
| @@ -217,7 +217,7 @@ typedef volatile struct { | |||
| /* external clients: set by libjack | |||
| * internal clients: set by engine */ | |||
| int (*deliver_request)(void*, jack_request_t*); | |||
| int (*deliver_request)(void*, jack_request_t*); /* JOQ: 64/32 bug! */ | |||
| void *deliver_arg; | |||
| /* for engine use only */ | |||
| @@ -239,10 +239,9 @@ typedef struct { | |||
| typedef struct { | |||
| int32_t status; /* messy name overloading */ | |||
| uint32_t protocol_v; | |||
| jack_status_t status; | |||
| jack_status_t open_status; /* used for open() */ | |||
| jack_shm_info_t client_shm; | |||
| jack_shm_info_t engine_shm; | |||
| @@ -253,12 +252,9 @@ typedef struct { | |||
| char name[JACK_CLIENT_NAME_SIZE]; /* unique name, if assigned */ | |||
| /* these two are valid only if the connect request | |||
| was for type == ClientDriver. | |||
| */ | |||
| jack_client_control_t *client_control; /* JOQ: 64/32 problem */ | |||
| jack_control_t *engine_control; /* JOQ: 64/32 problem */ | |||
| /* these two are valid only for internal clients */ | |||
| jack_client_control_t *client_control; | |||
| jack_control_t *engine_control; | |||
| #ifdef JACK_USE_MACH_THREADS | |||
| /* specific resources for server/client real-time thread communication */ | |||
| @@ -294,6 +290,10 @@ typedef enum { | |||
| SetBufferSize = 16, | |||
| FreeWheel = 17, | |||
| StopFreeWheel = 18, | |||
| IntClientHandle = 19, | |||
| IntClientLoad = 20, | |||
| IntClientName = 21, | |||
| IntClientUnload = 22 | |||
| } RequestType; | |||
| struct _jack_request { | |||
| @@ -320,6 +320,13 @@ struct _jack_request { | |||
| jack_client_id_t client_id; | |||
| int32_t conditional; | |||
| } timebase; | |||
| struct { | |||
| jack_options_t options; | |||
| jack_client_id_t id; | |||
| char name[JACK_CLIENT_NAME_SIZE]; | |||
| char path[PATH_MAX+1]; | |||
| char init[JACK_LOAD_INIT_LIMIT]; | |||
| } intclient; | |||
| jack_client_id_t client_id; | |||
| jack_nframes_t nframes; | |||
| jack_time_t timeout; | |||
| @@ -47,22 +47,25 @@ extern "C" { | |||
| * @ref JackUseExactName option, the server will modify this name to | |||
| * create a unique variant, if needed. | |||
| * | |||
| * @param options formed by OR-ing together @ref JackOpenOptions bits. | |||
| * @param options formed by OR-ing together @ref JackOptions bits. | |||
| * Only the @ref JackOpenOptions bits are allowed. | |||
| * | |||
| * @param status (if non-NULL) an address for JACK to return | |||
| * information from the open operation. This status word is formed by | |||
| * OR-ing together the relevant @ref JackOpenStatus bits. | |||
| * OR-ing together the relevant @ref JackStatus bits. | |||
| * | |||
| * <b>Optional parameters:</b> | |||
| * | |||
| * @arg @a server_name <tt>(char *)</tt> selects from among several | |||
| * possible concurrent server instances. This parameter must follow | |||
| * @a status if the @ref JackServerName option is specified. | |||
| * Otherwise, "default" is assumed. Server names are unique to each | |||
| * user. | |||
| * <b>Optional parameters:</b> depending on corresponding [@a options | |||
| * bits] additional parameters may follow @a status (in this order). | |||
| * | |||
| * @arg [@ref JackServerName] <em>(char *) server_name</em> selects | |||
| * from among several possible concurrent server instances. Server | |||
| * names are unique to each user. If unspecified, use "default" | |||
| * unless \$JACK_DEFAULT_SERVER is defined in the process environment. | |||
| * | |||
| * @return Opaque client handle if successful. If this is NULL, the | |||
| * open operation failed, and the caller is not a JACK client. | |||
| * open operation failed, @a *status includes @ref JackFailure and the | |||
| * caller is not a JACK client. | |||
| */ | |||
| jack_client_t *jack_client_open (const char *client_name, | |||
| jack_options_t options, | |||
| @@ -110,30 +113,37 @@ int jack_client_name_size (void); | |||
| * JackNameNotUnique status was returned. In that case, the actual | |||
| * name will differ from the @a client_name requested. | |||
| */ | |||
| char *jack_get_client_name (jack_client_t* client); | |||
| char *jack_get_client_name (jack_client_t *client); | |||
| /** | |||
| * Attempt to load an internal client into the Jack server. | |||
| * Load an internal client into the Jack server. | |||
| * | |||
| * Internal clients run inside the JACK server process. They can use | |||
| * most of the same functions as external clients. Each internal | |||
| * client must declare jack_initialize() and jack_finish() entry | |||
| * points, called at load and unload times. See inprocess.c for an | |||
| * example of how to write an internal client. | |||
| * | |||
| * Internal clients run within the JACK server process. They can use | |||
| * of the same functions as external clients. Each internal client | |||
| * must declare jack_initialize() and jack_finish() entry points, | |||
| * called at load and unload times. See inprocess.c for an example of | |||
| * how to write an internal client. | |||
| * @deprecated Please use jack_internal_client_load(). | |||
| * | |||
| * @param client_name of at most jack_client_name_size() characters. | |||
| * @param so_name A path to a shared object file containing the code | |||
| * for the new client. | |||
| * @param so_data An arbitary string containing information to be | |||
| * passed to the jack_initialize() routine of the new client. | |||
| * | |||
| * @param load_name of a shared object file containing the code for | |||
| * the new client. | |||
| * | |||
| * @param load_init an arbitary string passed to the jack_initialize() | |||
| * routine of the new client (may be NULL). | |||
| * | |||
| * @return 0 if successful. | |||
| */ | |||
| int jack_internal_client_new (const char *client_name, const char *so_name, | |||
| const char *so_data); | |||
| int jack_internal_client_new (const char *client_name, | |||
| const char *load_name, | |||
| const char *load_init); | |||
| /** | |||
| * Removes an internal client from a JACK server. | |||
| * Remove an internal client from a JACK server. | |||
| * | |||
| * @return 0 on success, otherwise a non-zero error code | |||
| * @deprecated Please use jack_internal_client_load(). | |||
| */ | |||
| void jack_internal_client_close (const char *client_name); | |||
| @@ -1,5 +1,6 @@ | |||
| /* | |||
| Copyright (C) 2001 Paul Davis | |||
| Copyright (C) 2004 Jack O'Quin | |||
| 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 | |||
| @@ -43,6 +44,19 @@ typedef uint32_t jack_nframes_t; | |||
| */ | |||
| typedef uint64_t jack_time_t; | |||
| /** | |||
| * Maximum size of @a load_init string passed to an internal client | |||
| * jack_initialize() function via jack_internal_client_load(). | |||
| */ | |||
| #define JACK_LOAD_INIT_LIMIT 1024 | |||
| /** | |||
| * jack_intclient_t is an opaque type representing a loaded internal | |||
| * client. You may only access it using the API provided in @ref | |||
| * intclient.h "<jack/intclient.h>". | |||
| */ | |||
| typedef uint64_t jack_intclient_t; | |||
| /** | |||
| * jack_port_t is an opaque type. You may only access it using the | |||
| * API provided. | |||
| @@ -227,9 +241,9 @@ enum JackPortFlags { | |||
| }; | |||
| /** | |||
| * jack_client_open() option bits | |||
| * @ref jack_options_t bits | |||
| */ | |||
| enum JackOpenOptions { | |||
| enum JackOptions { | |||
| /** | |||
| * Null value to use when no option bits are needed. | |||
| @@ -251,32 +265,50 @@ enum JackOpenOptions { | |||
| JackUseExactName = 0x02, | |||
| /** | |||
| * Open with optional @a char @a *server_name parameter. | |||
| * | |||
| * @warning This option has not yet been implemented. | |||
| * Open with optional <em>(char *) server_name</em> parameter. | |||
| * <b>Warning: this option is not yet implemented.</b> | |||
| */ | |||
| JackServerName = 0x04, | |||
| /** | |||
| * Load internal client from optional <em>(char *) | |||
| * load_name</em>. Otherwise use the @a client_name. | |||
| */ | |||
| JackServerName = 0x04 | |||
| JackLoadName = 0x08, | |||
| /** | |||
| * Pass optional <em>(char *) load_init</em> string to the | |||
| * jack_initialize() entry point of an internal client. | |||
| */ | |||
| JackLoadInit = 0x10 | |||
| }; | |||
| /* options mask does not include unimplemented features */ | |||
| #define JackValidOptions (JackNoStartServer|JackUseExactName) | |||
| /** Valid options for opening an external client. */ | |||
| #define JackOpenOptions (JackServerName|JackNoStartServer|JackUseExactName) | |||
| /** Valid options for loading an internal client. */ | |||
| #define JackLoadOptions (JackLoadInit|JackLoadName|JackUseExactName) | |||
| /** | |||
| * jack_client_open() request options are formed by AND-ing together | |||
| * @ref JackOpenOptions bits. | |||
| * Options for several JACK operations, formed by OR-ing together the | |||
| * relevant @ref JackOptions bits. | |||
| */ | |||
| typedef enum JackOpenOptions jack_options_t; | |||
| typedef enum JackOptions jack_options_t; | |||
| /** | |||
| * jack_client_open() status bits | |||
| * @ref jack_status_t bits | |||
| */ | |||
| enum JackOpenStatus { | |||
| enum JackStatus { | |||
| /** | |||
| * Overall operation failed. | |||
| */ | |||
| JackFailure = 0x01, | |||
| /** | |||
| * The open request failed because it contained an invalid or | |||
| * unsupported option. | |||
| * The operation contained an invalid or unsupported option. | |||
| */ | |||
| JackInvalidOption = 0x01, | |||
| JackInvalidOption = 0x02, | |||
| /** | |||
| * The desired client name was not unique. With the @ref | |||
| @@ -287,26 +319,36 @@ enum JackOpenStatus { | |||
| * that was used. If the specified @a client_name plus these | |||
| * extra characters would be too long, the open fails instead. | |||
| */ | |||
| JackNameNotUnique = 0x02, | |||
| JackNameNotUnique = 0x04, | |||
| /** | |||
| * The JACK server was started as a result of this open. | |||
| * The JACK server was started as a result of this operation. | |||
| * Otherwise, it was running already. In either case the caller | |||
| * is now connected to jackd, so there is no race condition. | |||
| * When the server shuts down, the client will find out. | |||
| */ | |||
| JackServerStarted = 0x04, | |||
| JackServerStarted = 0x08, | |||
| /** | |||
| * Unable to connect to the JACK server. | |||
| */ | |||
| JackServerFailed = 0x10, | |||
| /** | |||
| * Communication error with the JACK server. | |||
| */ | |||
| JackServerError = 0x20, | |||
| /** | |||
| * Unable to connect to the JACK server, open failed. | |||
| * Requested client does not exist. | |||
| */ | |||
| JackServerFailed = 0x08 | |||
| JackNoSuchClient = 0x40 | |||
| }; | |||
| /** | |||
| * The status word returned from jack_client_open() is formed by | |||
| * AND-ing together the relevant @ref JackOpenStatus bits. | |||
| * Status word returned from several JACK operations, formed by | |||
| * OR-ing together the relevant @ref JackStatus bits. | |||
| */ | |||
| typedef enum JackOpenStatus jack_status_t; | |||
| typedef enum JackStatus jack_status_t; | |||
| #endif /* __jack_types_h__ */ | |||
| @@ -0,0 +1,64 @@ | |||
| /* | |||
| * Copyright (C) 2004 Jack O'Quin | |||
| * | |||
| * 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. | |||
| * | |||
| * $Id$ | |||
| */ | |||
| #ifndef __jack_varargs_h__ | |||
| #define __jack_varargs_h__ | |||
| #ifdef __cplusplus | |||
| extern "C" { | |||
| #endif | |||
| /* variable argument structure */ | |||
| typedef struct { | |||
| char *server_name; /* server name */ | |||
| char *load_name; /* load module name */ | |||
| char *load_init; /* initialization string */ | |||
| } jack_varargs_t; | |||
| static inline void | |||
| jack_varargs_init (jack_varargs_t *va) | |||
| { | |||
| memset (va, 0, sizeof(jack_varargs_t)); | |||
| if ((va->server_name = getenv("JACK_DEFAULT_SERVER")) == NULL) | |||
| va->server_name = "default"; | |||
| } | |||
| static inline void | |||
| jack_varargs_parse (jack_options_t options, va_list ap, jack_varargs_t *va) | |||
| { | |||
| /* initialize default settings */ | |||
| jack_varargs_init (va); | |||
| if ((options & JackServerName)) { | |||
| char *sn = va_arg(ap, char *); | |||
| if (sn) | |||
| va->server_name = sn; | |||
| } | |||
| if ((options & JackLoadName)) | |||
| va->load_name = va_arg(ap, char *); | |||
| if ((options & JackLoadInit)) | |||
| va->load_init = va_arg(ap, char *); | |||
| } | |||
| #ifdef __cplusplus | |||
| } | |||
| #endif | |||
| #endif /* __jack_varargs_h__ */ | |||
| @@ -23,9 +23,10 @@ bin_PROGRAMS = jackd $(CAP_PROGS) | |||
| AM_CFLAGS = $(JACK_CFLAGS) -DJACK_LOCATION=\"$(bindir)\" | |||
| jackd_SOURCES = jackd.c engine.c transengine.c | |||
| jackd_SOURCES = jackd.c engine.c clientengine.c transengine.c | |||
| jackd_LDADD = ../libjack/libjack.la $(CAP_LIBS) @OS_LDFLAGS@ | |||
| noinst_HEADERS = jack_md5.h md5.h md5_loc.h transengine.h | |||
| noinst_HEADERS = jack_md5.h md5.h md5_loc.h \ | |||
| clientengine.h transengine.h | |||
| BUILT_SOURCES = jack_md5.h | |||
| @@ -0,0 +1,933 @@ | |||
| /* -*- mode: c; c-file-style: "bsd"; -*- */ | |||
| /* | |||
| * Internal client handling interfaces for JACK engine. | |||
| * | |||
| * Copyright (C) 2001-2003 Paul Davis | |||
| * Copyright (C) 2004 Jack O'Quin | |||
| * | |||
| * 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. | |||
| * | |||
| * $Id$ | |||
| */ | |||
| #include <config.h> | |||
| #include <errno.h> | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| #include <jack/internal.h> | |||
| #include <jack/engine.h> | |||
| #include <jack/version.h> | |||
| #include <sysdeps/poll.h> | |||
| #include "clientengine.h" | |||
| #include "transengine.h" | |||
| #define JACK_ERROR_WITH_SOCKETS 10000000 | |||
| static void | |||
| jack_client_disconnect (jack_engine_t *engine, jack_client_internal_t *client) | |||
| { | |||
| JSList *node; | |||
| jack_port_internal_t *port; | |||
| /* call tree **** MUST HOLD *** engine->client_lock */ | |||
| for (node = client->ports; node; node = jack_slist_next (node)) { | |||
| port = (jack_port_internal_t *) node->data; | |||
| jack_port_clear_connections (engine, port); | |||
| jack_port_registration_notify (engine, port->shared->id, FALSE); | |||
| jack_port_release (engine, port); | |||
| } | |||
| jack_slist_free (client->ports); | |||
| jack_slist_free (client->fed_by); | |||
| client->fed_by = 0; | |||
| client->ports = 0; | |||
| } | |||
| int | |||
| jack_client_do_deactivate (jack_engine_t *engine, | |||
| jack_client_internal_t *client, int sort_graph) | |||
| { | |||
| /* caller must hold engine->client_lock and must have checked for and/or | |||
| * cleared all connections held by client. */ | |||
| client->control->active = FALSE; | |||
| jack_transport_client_exit (engine, client); | |||
| if (!jack_client_is_internal (client) && | |||
| engine->external_client_cnt > 0) { | |||
| engine->external_client_cnt--; | |||
| } | |||
| if (sort_graph) { | |||
| jack_sort_graph (engine); | |||
| } | |||
| return 0; | |||
| } | |||
| static void | |||
| jack_zombify_client (jack_engine_t *engine, jack_client_internal_t *client) | |||
| { | |||
| VERBOSE (engine, "removing client \"%s\" from the processing chain\n", | |||
| client->control->name); | |||
| /* caller must hold the client_lock */ | |||
| /* this stops jack_deliver_event() from doing anything */ | |||
| client->control->dead = TRUE; | |||
| jack_client_disconnect (engine, client); | |||
| jack_client_do_deactivate (engine, client, FALSE); | |||
| } | |||
| static void | |||
| jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client) | |||
| { | |||
| /* called *without* the request_lock */ | |||
| unsigned int i; | |||
| JSList *node; | |||
| /* caller must hold the client_lock */ | |||
| VERBOSE (engine, "removing client \"%s\"\n", client->control->name); | |||
| /* if its not already a zombie, make it so */ | |||
| if (!client->control->dead) { | |||
| jack_zombify_client (engine, client); | |||
| } | |||
| if (client->control->type == ClientExternal) { | |||
| /* try to force the server thread to return from poll */ | |||
| close (client->event_fd); | |||
| close (client->request_fd); | |||
| /* rearrange the pollfd array so that things work right the | |||
| next time we go into poll(2). | |||
| */ | |||
| for (i = 0; i < engine->pfd_max; i++) { | |||
| if (engine->pfd[i].fd == client->request_fd) { | |||
| if (i+1 < engine->pfd_max) { | |||
| memmove (&engine->pfd[i], | |||
| &engine->pfd[i+1], | |||
| sizeof (struct pollfd) | |||
| * (engine->pfd_max - i)); | |||
| } | |||
| engine->pfd_max--; | |||
| } | |||
| } | |||
| } | |||
| for (node = engine->clients; node; node = jack_slist_next (node)) { | |||
| if (((jack_client_internal_t *) node->data)->control->id | |||
| == client->control->id) { | |||
| engine->clients = | |||
| jack_slist_remove_link (engine->clients, node); | |||
| jack_slist_free_1 (node); | |||
| break; | |||
| } | |||
| } | |||
| jack_client_delete (engine, client); | |||
| /* ignore the driver, which counts as a client. */ | |||
| if (engine->temporary && (jack_slist_length(engine->clients) <= 1)) { | |||
| exit(0); | |||
| } | |||
| } | |||
| void | |||
| jack_remove_clients (jack_engine_t* engine) | |||
| { | |||
| JSList *tmp, *node; | |||
| int need_sort = FALSE; | |||
| jack_client_internal_t *client; | |||
| /* remove all dead clients */ | |||
| for (node = engine->clients; node; ) { | |||
| tmp = jack_slist_next (node); | |||
| client = (jack_client_internal_t *) node->data; | |||
| if (client->error) { | |||
| /* if we have a communication problem with the | |||
| client, remove it. otherwise, turn it into | |||
| a zombie. the client will/should realize | |||
| this and will close its sockets. then | |||
| we'll end up back here again and will | |||
| finally remove the client. | |||
| */ | |||
| if (client->error >= JACK_ERROR_WITH_SOCKETS) { | |||
| VERBOSE (engine, "removing failed " | |||
| "client %s state = %s errors" | |||
| " = %d\n", | |||
| client->control->name, | |||
| client_state_names[ | |||
| client->control->state | |||
| ], | |||
| client->error); | |||
| jack_remove_client (engine, | |||
| (jack_client_internal_t *) | |||
| node->data); | |||
| } else { | |||
| VERBOSE (engine, "client failure: " | |||
| "client %s state = %s errors" | |||
| " = %d\n", | |||
| client->control->name, | |||
| client_state_names[ | |||
| client->control->state | |||
| ], | |||
| client->error); | |||
| jack_zombify_client (engine, | |||
| (jack_client_internal_t *) | |||
| node->data); | |||
| client->error = 0; | |||
| } | |||
| need_sort = TRUE; | |||
| } | |||
| node = tmp; | |||
| } | |||
| if (need_sort) { | |||
| jack_sort_graph (engine); | |||
| } | |||
| jack_engine_reset_rolling_usecs (engine); | |||
| } | |||
| static int | |||
| jack_load_client (jack_engine_t *engine, jack_client_internal_t *client, | |||
| const char *so_name) | |||
| { | |||
| const char *errstr; | |||
| char path_to_so[PATH_MAX+1]; | |||
| snprintf (path_to_so, sizeof (path_to_so), ADDON_DIR "/%s.so", so_name); | |||
| client->handle = dlopen (path_to_so, RTLD_NOW|RTLD_GLOBAL); | |||
| if (client->handle == 0) { | |||
| if ((errstr = dlerror ()) != 0) { | |||
| jack_error ("%s", errstr); | |||
| } else { | |||
| jack_error ("bizarre error loading %s", so_name); | |||
| } | |||
| return -1; | |||
| } | |||
| client->initialize = dlsym (client->handle, "jack_initialize"); | |||
| if ((errstr = dlerror ()) != 0) { | |||
| jack_error ("%s has no initialize() function\n", so_name); | |||
| dlclose (client->handle); | |||
| client->handle = 0; | |||
| return -1; | |||
| } | |||
| client->finish = (void (*)(void *)) dlsym (client->handle, | |||
| "jack_finish"); | |||
| if ((errstr = dlerror ()) != 0) { | |||
| jack_error ("%s has no finish() function", so_name); | |||
| dlclose (client->handle); | |||
| client->handle = 0; | |||
| return -1; | |||
| } | |||
| return 0; | |||
| } | |||
| static void | |||
| jack_client_unload (jack_client_internal_t *client) | |||
| { | |||
| if (client->handle) { | |||
| if (client->finish) { | |||
| client->finish (client->control->process_arg); | |||
| } | |||
| dlclose (client->handle); | |||
| } | |||
| } | |||
| static jack_client_internal_t * | |||
| jack_client_by_name (jack_engine_t *engine, const char *name) | |||
| { | |||
| jack_client_internal_t *client = NULL; | |||
| JSList *node; | |||
| jack_lock_graph (engine); | |||
| for (node = engine->clients; node; node = jack_slist_next (node)) { | |||
| if (strcmp ((const char *) ((jack_client_internal_t *) | |||
| node->data)->control->name, | |||
| name) == 0) { | |||
| client = (jack_client_internal_t *) node->data; | |||
| break; | |||
| } | |||
| } | |||
| jack_unlock_graph (engine); | |||
| return client; | |||
| } | |||
| static jack_client_id_t | |||
| jack_client_id_by_name (jack_engine_t *engine, const char *name) | |||
| { | |||
| jack_client_id_t id = 0; /* NULL client ID */ | |||
| JSList *node; | |||
| jack_lock_graph (engine); | |||
| for (node = engine->clients; node; node = jack_slist_next (node)) { | |||
| if (strcmp ((const char *) ((jack_client_internal_t *) | |||
| node->data)->control->name, | |||
| name) == 0) { | |||
| jack_client_internal_t *client = | |||
| (jack_client_internal_t *) node->data; | |||
| id = client->control->id; | |||
| break; | |||
| } | |||
| } | |||
| jack_unlock_graph (engine); | |||
| return id; | |||
| } | |||
| jack_client_internal_t * | |||
| jack_client_internal_by_id (jack_engine_t *engine, jack_client_id_t id) | |||
| { | |||
| jack_client_internal_t *client = NULL; | |||
| JSList *node; | |||
| /* call tree ***MUST HOLD*** the graph lock */ | |||
| for (node = engine->clients; node; node = jack_slist_next (node)) { | |||
| if (((jack_client_internal_t *) node->data)->control->id | |||
| == id) { | |||
| client = (jack_client_internal_t *) node->data; | |||
| break; | |||
| } | |||
| } | |||
| return client; | |||
| } | |||
| /* generate a unique client name | |||
| * | |||
| * returns 0 if successful, updates name in place | |||
| */ | |||
| static inline int | |||
| jack_generate_unique_name (jack_engine_t *engine, char *name) | |||
| { | |||
| int tens, ones; | |||
| int length = strlen (name); | |||
| if (length > JACK_CLIENT_NAME_SIZE - 4) { | |||
| jack_error ("%s exists and is too long to make unique", name); | |||
| return 1; /* failure */ | |||
| } | |||
| /* generate a unique name by appending "-01".."-99" */ | |||
| name[length++] = '-'; | |||
| tens = length++; | |||
| ones = length++; | |||
| name[tens] = '0'; | |||
| name[ones] = '1'; | |||
| name[length] = '\0'; | |||
| while (jack_client_by_name (engine, name)) { | |||
| if (name[ones] == '9') { | |||
| if (name[tens] == '9') { | |||
| jack_error ("client %s has 99 extra" | |||
| " instances already", name); | |||
| return 1; /* give up */ | |||
| } | |||
| name[tens]++; | |||
| name[ones] = '0'; | |||
| } else { | |||
| name[ones]++; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| static int | |||
| jack_client_name_invalid (jack_engine_t *engine, char *name, | |||
| jack_options_t options, jack_status_t *status) | |||
| { | |||
| /* Since this is always called from the server thread, no | |||
| * other new client will be created at the same time. So, | |||
| * testing a name for uniqueness is valid here. When called | |||
| * from jack_engine_load_driver() this is not strictly true, | |||
| * but that seems to be adequately serialized due to engine | |||
| * startup. There are no other clients at that point, anyway. | |||
| */ | |||
| if (jack_client_by_name (engine, name)) { | |||
| *status |= JackNameNotUnique; | |||
| if (options & JackUseExactName) { | |||
| jack_error ("cannot create new client; %s already" | |||
| " exists", name); | |||
| *status |= JackFailure; | |||
| return TRUE; | |||
| } | |||
| if (jack_generate_unique_name(engine, name)) { | |||
| *status |= JackFailure; | |||
| return TRUE; | |||
| } | |||
| } | |||
| return FALSE; | |||
| } | |||
| /* Set up the engine's client internal and control structures for both | |||
| * internal and external clients. */ | |||
| static jack_client_internal_t * | |||
| jack_setup_client_control (jack_engine_t *engine, int fd, | |||
| ClientType type, const char *name) | |||
| { | |||
| jack_client_internal_t *client; | |||
| client = (jack_client_internal_t *) | |||
| malloc (sizeof (jack_client_internal_t)); | |||
| client->request_fd = fd; | |||
| client->event_fd = -1; | |||
| client->ports = 0; | |||
| client->fed_by = 0; | |||
| client->execution_order = UINT_MAX; | |||
| client->next_client = NULL; | |||
| client->handle = NULL; | |||
| client->finish = NULL; | |||
| client->error = 0; | |||
| if (type != ClientExternal) { | |||
| client->control = (jack_client_control_t *) | |||
| malloc (sizeof (jack_client_control_t)); | |||
| } else { | |||
| char shm_name[PATH_MAX+1]; | |||
| snprintf (shm_name, sizeof (shm_name), "/jack-c-%s", name); | |||
| if (jack_shmalloc (shm_name, | |||
| sizeof (jack_client_control_t), | |||
| &client->control_shm)) { | |||
| jack_error ("cannot create client control block for %s", | |||
| name); | |||
| free (client); | |||
| return 0; | |||
| } | |||
| if (jack_attach_shm (&client->control_shm)) { | |||
| jack_error ("cannot attach to client control block " | |||
| "for %s (%s)", name, strerror (errno)); | |||
| jack_destroy_shm (&client->control_shm); | |||
| free (client); | |||
| return 0; | |||
| } | |||
| client->control = (jack_client_control_t *) | |||
| jack_shm_addr (&client->control_shm); | |||
| } | |||
| client->control->type = type; | |||
| client->control->active = 0; | |||
| client->control->dead = FALSE; | |||
| client->control->timed_out = 0; | |||
| client->control->id = engine->next_client_id++; | |||
| strcpy ((char *) client->control->name, name); | |||
| client->subgraph_start_fd = -1; | |||
| client->subgraph_wait_fd = -1; | |||
| client->control->process = NULL; | |||
| client->control->process_arg = NULL; | |||
| client->control->bufsize = NULL; | |||
| client->control->bufsize_arg = NULL; | |||
| client->control->srate = NULL; | |||
| client->control->srate_arg = NULL; | |||
| client->control->xrun = NULL; | |||
| client->control->xrun_arg = NULL; | |||
| client->control->port_register = NULL; | |||
| client->control->port_register_arg = NULL; | |||
| client->control->graph_order = NULL; | |||
| client->control->graph_order_arg = NULL; | |||
| jack_transport_client_new (client); | |||
| #ifdef JACK_USE_MACH_THREADS | |||
| /* specific resources for server/client real-time thread | |||
| * communication */ | |||
| allocate_mach_serverport(engine, client); | |||
| client->running = FALSE; | |||
| #endif | |||
| return client; | |||
| } | |||
| /* set up all types of clients */ | |||
| static jack_client_internal_t * | |||
| setup_client (jack_engine_t *engine, ClientType type, char *name, | |||
| jack_options_t options, jack_status_t *status, int client_fd, | |||
| const char *object_path, const char *object_data) | |||
| { | |||
| /* called with the request_lock */ | |||
| jack_client_internal_t *client; | |||
| /* validate client name, generate a unique one if appropriate */ | |||
| if (jack_client_name_invalid (engine, name, options, status)) | |||
| return NULL; | |||
| /* create a client struct for this name */ | |||
| if ((client = jack_setup_client_control (engine, client_fd, | |||
| type, name)) == NULL) { | |||
| jack_error ("cannot create new client object"); | |||
| return NULL; | |||
| } | |||
| /* only for internal clients, driver is already loaded */ | |||
| if (type == ClientInternal) { | |||
| if (jack_load_client (engine, client, object_path)) { | |||
| jack_error ("cannot dynamically load client from" | |||
| " \"%s\"", object_path); | |||
| jack_client_delete (engine, client); | |||
| return NULL; | |||
| } | |||
| } | |||
| VERBOSE (engine, "new client: %s, id = %" PRIu32 | |||
| " type %d @ %p fd = %d\n", | |||
| client->control->name, client->control->id, | |||
| type, client->control, client_fd); | |||
| if (jack_client_is_internal(client)) { | |||
| /* Set up the pointers necessary for the request | |||
| * system to work. The client is in the same address | |||
| * space */ | |||
| client->control->deliver_request = internal_client_request; | |||
| client->control->deliver_arg = engine; | |||
| } | |||
| /* add new client to the clients list */ | |||
| jack_lock_graph (engine); | |||
| engine->clients = jack_slist_prepend (engine->clients, client); | |||
| jack_engine_reset_rolling_usecs (engine); | |||
| if (jack_client_is_internal(client)) { | |||
| /* Internal clients need to make regular JACK API | |||
| * calls, which need a jack_client_t structure. | |||
| * Create one here. | |||
| */ | |||
| client->control->private_client = | |||
| jack_client_alloc_internal (client->control, engine); | |||
| jack_unlock_graph (engine); | |||
| /* Call its initialization function. This function | |||
| * may make requests of its own, so we temporarily | |||
| * release and then reacquire the request_lock. */ | |||
| if (client->control->type == ClientInternal) { | |||
| pthread_mutex_unlock (&engine->request_lock); | |||
| if (client->initialize (client->control->private_client, | |||
| object_data)) { | |||
| /* failed: clean up client data */ | |||
| VERBOSE (engine, | |||
| "%s jack_initialize() failed!\n", | |||
| client->control->name); | |||
| jack_lock_graph (engine); | |||
| jack_remove_client (engine, client); | |||
| jack_unlock_graph (engine); | |||
| client = NULL; | |||
| //JOQ: not clear that all allocated | |||
| //storage has been cleaned up properly. | |||
| } | |||
| pthread_mutex_lock (&engine->request_lock); | |||
| } | |||
| } else { /* external client */ | |||
| if (engine->pfd_max >= engine->pfd_size) { | |||
| engine->pfd = (struct pollfd *) | |||
| realloc (engine->pfd, sizeof (struct pollfd) | |||
| * (engine->pfd_size + 16)); | |||
| engine->pfd_size += 16; | |||
| } | |||
| engine->pfd[engine->pfd_max].fd = client->request_fd; | |||
| engine->pfd[engine->pfd_max].events = | |||
| POLLIN|POLLPRI|POLLERR|POLLHUP|POLLNVAL; | |||
| engine->pfd_max++; | |||
| jack_unlock_graph (engine); | |||
| } | |||
| return client; | |||
| } | |||
| jack_client_internal_t * | |||
| jack_setup_driver_client (jack_engine_t *engine, char *name) | |||
| { | |||
| jack_client_connect_request_t req; | |||
| jack_status_t status; | |||
| jack_client_internal_t *client; | |||
| snprintf (req.name, sizeof (req.name), "%s", name); | |||
| pthread_mutex_lock (&engine->request_lock); | |||
| client = setup_client (engine, ClientDriver, name, JackUseExactName, | |||
| &status, -1, NULL, NULL); | |||
| pthread_mutex_unlock (&engine->request_lock); | |||
| return client; | |||
| } | |||
| static jack_status_t | |||
| handle_unload_client (jack_engine_t *engine, jack_client_id_t id) | |||
| { | |||
| /* called *without* the request_lock */ | |||
| jack_client_internal_t *client; | |||
| jack_status_t status = (JackNoSuchClient|JackFailure); | |||
| jack_lock_graph (engine); | |||
| if ((client = jack_client_internal_by_id (engine, id))) { | |||
| VERBOSE (engine, "unloading client \"%s\"\n", | |||
| client->control->name); | |||
| jack_remove_client (engine, client); | |||
| status = 0; | |||
| } | |||
| jack_unlock_graph (engine); | |||
| return status; | |||
| } | |||
| int | |||
| jack_new_client_request (jack_engine_t *engine, int client_fd) | |||
| { | |||
| /* called *without* the request_lock */ | |||
| jack_client_internal_t *client; | |||
| jack_client_connect_request_t req; | |||
| jack_client_connect_result_t res; | |||
| res.status = 0; | |||
| if (read (client_fd, &req, sizeof (req)) != sizeof (req)) { | |||
| jack_error ("cannot read connection request from client"); | |||
| return -1; | |||
| } | |||
| if (!req.load) { /* internal client close? */ | |||
| int rc = -1; | |||
| jack_client_id_t id; | |||
| if ((id = jack_client_id_by_name(engine, req.name))) { | |||
| rc = handle_unload_client (engine, id); | |||
| } | |||
| /* close does not send a reply */ | |||
| return rc; | |||
| } | |||
| pthread_mutex_lock (&engine->request_lock); | |||
| client = setup_client (engine, req.type, req.name, | |||
| req.options, &res.status, client_fd, | |||
| req.object_path, req.object_data); | |||
| pthread_mutex_unlock (&engine->request_lock); | |||
| if (client == NULL) { | |||
| return -1; | |||
| } | |||
| res.protocol_v = jack_protocol_version; | |||
| res.client_shm = client->control_shm; | |||
| res.engine_shm = engine->control_shm; | |||
| res.realtime = engine->control->real_time; | |||
| res.realtime_priority = engine->rtpriority - 1; | |||
| strncpy (res.name, req.name, sizeof(res.name)); | |||
| #ifdef JACK_USE_MACH_THREADS | |||
| /* Mach port number for server/client communication */ | |||
| res.portnum = client->portnum; | |||
| #endif | |||
| if (jack_client_is_internal(client)) { | |||
| res.client_control = client->control; | |||
| res.engine_control = engine->control; | |||
| } else { | |||
| strcpy (res.fifo_prefix, engine->fifo_prefix); | |||
| } | |||
| if (write (client->request_fd, &res, sizeof (res)) != sizeof (res)) { | |||
| jack_error ("cannot write connection response to client"); | |||
| jack_client_delete (engine, client); | |||
| return -1; | |||
| } | |||
| if (jack_client_is_internal (client)) { | |||
| close (client_fd); | |||
| } | |||
| return 0; | |||
| } | |||
| int | |||
| jack_client_activate (jack_engine_t *engine, jack_client_id_t id) | |||
| { | |||
| jack_client_internal_t *client; | |||
| JSList *node; | |||
| int ret = -1; | |||
| jack_lock_graph (engine); | |||
| for (node = engine->clients; node; node = jack_slist_next (node)) { | |||
| if (((jack_client_internal_t *) node->data)->control->id | |||
| == id) { | |||
| client = (jack_client_internal_t *) node->data; | |||
| client->control->active = TRUE; | |||
| jack_transport_activate(engine, client); | |||
| /* we call this to make sure the FIFO is | |||
| * built+ready by the time the client needs | |||
| * it. we don't care about the return value at | |||
| * this point. | |||
| */ | |||
| jack_get_fifo_fd (engine, | |||
| ++engine->external_client_cnt); | |||
| jack_sort_graph (engine); | |||
| ret = 0; | |||
| break; | |||
| } | |||
| } | |||
| jack_unlock_graph (engine); | |||
| return ret; | |||
| } | |||
| int | |||
| jack_client_deactivate (jack_engine_t *engine, jack_client_id_t id) | |||
| { | |||
| JSList *node; | |||
| int ret = -1; | |||
| 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->id == id) { | |||
| JSList *portnode; | |||
| jack_port_internal_t *port; | |||
| for (portnode = client->ports; portnode; | |||
| portnode = jack_slist_next (portnode)) { | |||
| port = (jack_port_internal_t *) portnode->data; | |||
| jack_port_clear_connections (engine, port); | |||
| } | |||
| ret = jack_client_do_deactivate (engine, client, TRUE); | |||
| break; | |||
| } | |||
| } | |||
| jack_unlock_graph (engine); | |||
| return ret; | |||
| } | |||
| int | |||
| jack_client_socket_error (jack_engine_t *engine, int fd) | |||
| { | |||
| jack_client_internal_t *client = 0; | |||
| JSList *node; | |||
| #ifndef DEFER_CLIENT_REMOVE_TO_AUDIO_THREAD | |||
| jack_lock_graph (engine); | |||
| for (node = engine->clients; node; node = jack_slist_next (node)) { | |||
| if (jack_client_is_internal((jack_client_internal_t *) | |||
| node->data)) { | |||
| continue; | |||
| } | |||
| if (((jack_client_internal_t *) node->data)->request_fd == fd) { | |||
| client = (jack_client_internal_t *) node->data; | |||
| break; | |||
| } | |||
| } | |||
| if (client) { | |||
| VERBOSE (engine, "removing disconnected client %s state = " | |||
| "%s errors = %d\n", client->control->name, | |||
| client_state_names[client->control->state], | |||
| client->error); | |||
| jack_remove_client(engine, client); | |||
| jack_sort_graph (engine); | |||
| } | |||
| jack_unlock_graph (engine); | |||
| #else /* DEFER_CLIENT_REMOVE_TO_AUDIO_THREAD */ | |||
| jack_lock_graph (engine); | |||
| for (node = engine->clients; node; node = jack_slist_next (node)) { | |||
| if (jack_client_is_internal((jack_client_internal_t *) | |||
| node->data)) { | |||
| continue; | |||
| } | |||
| if (((jack_client_internal_t *) node->data)->request_fd == fd) { | |||
| client = (jack_client_internal_t *) node->data; | |||
| if (client->error < JACK_ERROR_WITH_SOCKETS) { | |||
| client->error += JACK_ERROR_WITH_SOCKETS; | |||
| } | |||
| break; | |||
| } | |||
| } | |||
| jack_unlock_graph (engine); | |||
| #endif /* DEFER_CLIENT_REMOVE_TO_AUDIO_THREAD */ | |||
| return 0; | |||
| } | |||
| void | |||
| jack_client_delete (jack_engine_t *engine, jack_client_internal_t *client) | |||
| { | |||
| if (jack_client_is_internal (client)) { | |||
| jack_client_unload (client); | |||
| free (client->control->private_client); | |||
| free ((void *) client->control); | |||
| } else { | |||
| /* release the client segment, mark it for | |||
| destruction, and free up the shm registry | |||
| information so that it can be reused. | |||
| */ | |||
| jack_release_shm (&client->control_shm); | |||
| jack_destroy_shm (&client->control_shm); | |||
| } | |||
| free (client); | |||
| } | |||
| void | |||
| jack_intclient_handle_request (jack_engine_t *engine, jack_request_t *req) | |||
| { | |||
| jack_client_internal_t *client; | |||
| req->status = 0; | |||
| if ((client = jack_client_by_name (engine, req->x.intclient.name))) { | |||
| req->x.intclient.id = client->control->id; | |||
| } else { | |||
| req->status |= (JackNoSuchClient|JackFailure); | |||
| } | |||
| } | |||
| void | |||
| jack_intclient_load_request (jack_engine_t *engine, jack_request_t *req) | |||
| { | |||
| /* called with the request_lock */ | |||
| jack_client_internal_t *client; | |||
| jack_status_t status = 0; | |||
| VERBOSE (engine, "load internal client %s from %s, init `%s', " | |||
| "options: 0x%x\n", req->x.intclient.name, | |||
| req->x.intclient.path, req->x.intclient.init, | |||
| req->x.intclient.options); | |||
| client = setup_client (engine, ClientInternal, req->x.intclient.name, | |||
| req->x.intclient.options, &status, -1, | |||
| req->x.intclient.path, req->x.intclient.init); | |||
| if (status & JackFailure) { | |||
| req->x.intclient.id = 0; | |||
| VERBOSE (engine, "load failed, status = 0x%x\n", status); | |||
| } else { | |||
| req->x.intclient.id = client->control->id; | |||
| } | |||
| req->status = status; | |||
| } | |||
| void | |||
| jack_intclient_name_request (jack_engine_t *engine, jack_request_t *req) | |||
| { | |||
| jack_client_internal_t *client; | |||
| jack_lock_graph (engine); | |||
| if ((client = jack_client_internal_by_id (engine, | |||
| req->x.intclient.id))) { | |||
| strncpy ((char *) req->x.intclient.name, | |||
| (char *) client->control->name, | |||
| sizeof (req->x.intclient.name)); | |||
| req->status = 0; | |||
| } else { | |||
| req->status = (JackNoSuchClient|JackFailure); | |||
| } | |||
| jack_unlock_graph (engine); | |||
| } | |||
| void | |||
| jack_intclient_unload_request (jack_engine_t *engine, jack_request_t *req) | |||
| { | |||
| /* Called with the request_lock, but we need to call | |||
| * handle_unload_client() *without* it. */ | |||
| if (req->x.intclient.id) { | |||
| pthread_mutex_unlock (&engine->request_lock); | |||
| req->status = | |||
| handle_unload_client (engine, req->x.intclient.id); | |||
| pthread_mutex_lock (&engine->request_lock); | |||
| } else { | |||
| VERBOSE (engine, "invalid unload request\n"); | |||
| req->status = JackFailure; | |||
| } | |||
| } | |||
| @@ -0,0 +1,51 @@ | |||
| /* | |||
| * Internal client handling interfaces for JACK engine. | |||
| * | |||
| * Copyright (C) 2004 Jack O'Quin | |||
| * | |||
| * 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. | |||
| */ | |||
| static char *client_state_names[] = { | |||
| "Not triggered", | |||
| "Triggered", | |||
| "Running", | |||
| "Finished" | |||
| }; | |||
| static inline int | |||
| jack_client_is_internal (jack_client_internal_t *client) | |||
| { | |||
| return (client->control->type == ClientInternal) || | |||
| (client->control->type == ClientDriver); | |||
| } | |||
| int jack_client_activate (jack_engine_t *engine, jack_client_id_t id); | |||
| int jack_client_deactivate (jack_engine_t *engine, jack_client_id_t id); | |||
| void jack_client_delete (jack_engine_t *engine, | |||
| jack_client_internal_t *client); | |||
| int jack_client_socket_error (jack_engine_t *engine, int fd); | |||
| void jack_intclient_handle_request (jack_engine_t *engine, | |||
| jack_request_t *req); | |||
| void jack_intclient_load_request (jack_engine_t *engine, | |||
| jack_request_t *req); | |||
| void jack_intclient_name_request (jack_engine_t *engine, | |||
| jack_request_t *req); | |||
| void jack_intclient_unload_request (jack_engine_t *engine, | |||
| jack_request_t *req); | |||
| int jack_new_client_request (jack_engine_t *engine, int client_fd); | |||
| void jack_remove_clients (jack_engine_t* engine); | |||
| jack_client_internal_t *jack_setup_driver_client (jack_engine_t *engine, | |||
| char *name); | |||
| @@ -138,8 +138,8 @@ jack_main (jack_driver_desc_t * driver_desc, JSList * driver_params) | |||
| if ((engine = jack_engine_new (realtime, realtime_priority, | |||
| do_mlock, do_unlock, | |||
| temporary, verbose, client_timeout, port_max, | |||
| getpid(), drivers)) == 0) { | |||
| temporary, verbose, client_timeout, | |||
| port_max, getpid(), drivers)) == 0) { | |||
| fprintf (stderr, "cannot create engine\n"); | |||
| return -1; | |||
| } | |||
| @@ -1,3 +1,9 @@ | |||
| 2004-11-27 Jack O'Quin <joq@io.com> | |||
| * new API functions: jack_internal_client_handle(), | |||
| jack_internal_client_load(), jack_get_internal_client_name(), | |||
| jack_internal_client_unload(). See: <jack/intclient.h>. | |||
| 2004-10-14 Jack O'Quin <joq@io.com> | |||
| * new API function: jack_get_xrun_delayed_usecs() | |||
| @@ -1,16 +1,17 @@ | |||
| MAINTAINERCLEANFILES = Makefile.in | |||
| SOURCE_FILES = \ | |||
| shm.c \ | |||
| client.c \ | |||
| driver.c \ | |||
| unlock.c \ | |||
| intclient.c \ | |||
| pool.c \ | |||
| port.c \ | |||
| ringbuffer.c \ | |||
| shm.c \ | |||
| thread.c \ | |||
| timestamps.c \ | |||
| transclient.c | |||
| transclient.c \ | |||
| unlock.c | |||
| lib_LTLIBRARIES = libjack.la | |||
| @@ -45,6 +45,7 @@ | |||
| #include <jack/shm.h> | |||
| #include <jack/unlock.h> | |||
| #include <jack/thread.h> | |||
| #include <jack/varargs.h> | |||
| #include <sysdeps/time.h> | |||
| JACK_TIME_GLOBAL_DECL; /* One instance per process. */ | |||
| @@ -146,12 +147,13 @@ oop_client_deliver_request (void *ptr, jack_request_t *req) | |||
| int | |||
| jack_client_deliver_request (const jack_client_t *client, jack_request_t *req) | |||
| { | |||
| /* indirect through the function pointer that was set | |||
| either by jack_client_new() (external) or handle_new_client() | |||
| in the server. | |||
| */ | |||
| /* indirect through the function pointer that was set either | |||
| * by jack_client_open() or by jack_new_client_request() in | |||
| * the server. | |||
| */ | |||
| return client->control->deliver_request (client->control->deliver_arg, req); | |||
| return client->control->deliver_request (client->control->deliver_arg, | |||
| req); | |||
| } | |||
| jack_client_t * | |||
| @@ -344,7 +346,7 @@ server_connect (const char *server_name) | |||
| } | |||
| //JOQ: temporary debug code | |||
| // fprintf (stderr, "connecting to %s server\n", server_name); | |||
| //fprintf (stderr, "DEBUG: connecting to `%s' server\n", server_name); | |||
| //JOQ: use server_name as part of socket path | |||
| addr.sun_family = AF_UNIX; | |||
| @@ -412,7 +414,7 @@ server_event_connect (jack_client_t *client) | |||
| /* Exec the JACK server in this process. Does not return. */ | |||
| static void | |||
| _start_server (const char *server_cmd) | |||
| _start_server (void) | |||
| { | |||
| FILE* fp = 0; | |||
| char filename[255]; | |||
| @@ -426,11 +428,6 @@ _start_server (const char *server_cmd) | |||
| int good = 0; | |||
| int ret; | |||
| //JOQ: | |||
| // if (start_command) { | |||
| // parse user-supplied command | |||
| // } else { | |||
| snprintf(filename, 255, "%s/.jackdrc", getenv("HOME")); | |||
| fp = fopen(filename, "r"); | |||
| @@ -501,7 +498,7 @@ _start_server (const char *server_cmd) | |||
| } | |||
| int | |||
| start_server (jack_options_t options, const char *server_cmd) | |||
| start_server (jack_options_t options) | |||
| { | |||
| if ((options & JackNoStartServer) | |||
| || (getenv("JACK_NO_START_SERVER") != NULL)) { | |||
| @@ -521,7 +518,7 @@ start_server (jack_options_t options, const char *server_cmd) | |||
| case 0: /* child process */ | |||
| switch (fork()) { | |||
| case 0: /* grandchild process */ | |||
| _start_server(server_cmd); | |||
| _start_server(); | |||
| _exit (99); /* exec failed */ | |||
| case -1: | |||
| _exit (98); | |||
| @@ -537,18 +534,15 @@ start_server (jack_options_t options, const char *server_cmd) | |||
| } | |||
| static int | |||
| jack_request_client (ClientType type, const char* client_name, | |||
| const char* so_name, const char* so_data, | |||
| jack_client_connect_result_t *res, int *req_fd, | |||
| jack_options_t options, jack_status_t *status, | |||
| const char *server_name, const char *server_cmd) | |||
| jack_request_client (ClientType type, | |||
| const char* client_name, jack_options_t options, | |||
| jack_status_t *status, jack_varargs_t *va, | |||
| jack_client_connect_result_t *res, int *req_fd) | |||
| { | |||
| jack_client_connect_request_t req; | |||
| *req_fd = -1; | |||
| memset (&req, 0, sizeof (req)); | |||
| req.options = options; | |||
| if (strlen (client_name) >= sizeof (req.name)) { | |||
| @@ -559,48 +553,54 @@ jack_request_client (ClientType type, const char* client_name, | |||
| return -1; | |||
| } | |||
| if (strlen (so_name) > sizeof (req.object_path) - 1) { | |||
| if (va->load_name | |||
| && (strlen (va->load_name) > sizeof (req.object_path) - 1)) { | |||
| jack_error ("\"%s\" is too long to be used as a JACK shared" | |||
| " object name.\n" | |||
| "Please use %lu characters or less.", | |||
| so_name, sizeof (req.object_path) - 1); | |||
| va->load_name, sizeof (req.object_path) - 1); | |||
| return -1; | |||
| } | |||
| if (strlen (so_data) > sizeof (req.object_data) - 1) { | |||
| if (va->load_init | |||
| && (strlen (va->load_init) > sizeof (req.object_data) - 1)) { | |||
| jack_error ("\"%s\" is too long to be used as a JACK shared" | |||
| " object data string.\n" | |||
| "Please use %lu characters or less.", | |||
| so_data, sizeof (req.object_data) - 1); | |||
| va->load_init, sizeof (req.object_data) - 1); | |||
| return -1; | |||
| } | |||
| if ((*req_fd = server_connect (server_name)) < 0) { | |||
| if ((*req_fd = server_connect (va->server_name)) < 0) { | |||
| int trys; | |||
| if (start_server(options, server_cmd)) { | |||
| *status |= JackServerFailed; | |||
| if (start_server(options)) { | |||
| *status |= (JackFailure|JackServerFailed); | |||
| goto fail; | |||
| } | |||
| trys = 5; | |||
| do { | |||
| sleep(1); | |||
| if (--trys < 0) { | |||
| *status |= JackServerFailed; | |||
| *status |= (JackFailure|JackServerFailed); | |||
| goto fail; | |||
| } | |||
| } while ((*req_fd = server_connect (server_name)) < 0); | |||
| } while ((*req_fd = server_connect (va->server_name)) < 0); | |||
| *status |= JackServerStarted; | |||
| } | |||
| req.load = TRUE; | |||
| req.type = type; | |||
| snprintf (req.name, sizeof (req.name), "%s", client_name); | |||
| snprintf (req.object_path, sizeof (req.object_path), "%s", so_name); | |||
| snprintf (req.object_data, sizeof (req.object_data), "%s", so_data); | |||
| snprintf (req.name, sizeof (req.name), | |||
| "%s", client_name); | |||
| snprintf (req.object_path, sizeof (req.object_path), | |||
| "%s", va->load_name); | |||
| snprintf (req.object_data, sizeof (req.object_data), | |||
| "%s", va->load_init); | |||
| if (write (*req_fd, &req, sizeof (req)) != sizeof (req)) { | |||
| jack_error ("cannot send request to jack server (%s)", | |||
| strerror (errno)); | |||
| *status |= (JackFailure|JackServerError); | |||
| goto fail; | |||
| } | |||
| @@ -608,33 +608,36 @@ jack_request_client (ClientType type, const char* client_name, | |||
| if (errno == 0) { | |||
| /* server shut the socket */ | |||
| jack_error ("could not attach as client " | |||
| "(duplicate client name?)"); | |||
| jack_error ("could not attach as client"); | |||
| *status |= (JackFailure|JackServerError); | |||
| goto fail; | |||
| } | |||
| if (errno == ECONNRESET) { | |||
| jack_error ("could not attach as jack client (server has exited)"); | |||
| jack_error ("could not attach as JACK client " | |||
| "(server has exited)"); | |||
| *status |= (JackFailure|JackServerError); | |||
| goto fail; | |||
| } | |||
| jack_error ("cannot read regsponse from jack server (%s)", | |||
| jack_error ("cannot read response from jack server (%s)", | |||
| strerror (errno)); | |||
| *status |= (JackFailure|JackServerError); | |||
| goto fail; | |||
| } | |||
| *status |= res->open_status; /* return server status bits */ | |||
| *status |= res->status; /* return server status bits */ | |||
| //JOQ: fixme overloading confusion | |||
| if (res->status) { | |||
| jack_error ("could not attach as client " | |||
| "(duplicate client name?)"); | |||
| if (*status & JackFailure) { | |||
| jack_error ("could not attach as client"); | |||
| *status |= JackServerError; | |||
| goto fail; | |||
| } | |||
| if (res->protocol_v != jack_protocol_version){ | |||
| jack_error ("application linked against incompatible libjack" | |||
| " version."); | |||
| *status |= (JackFailure|JackServerError); | |||
| goto fail; | |||
| } | |||
| @@ -735,9 +738,8 @@ jack_client_open (const char *client_name, | |||
| jack_status_t *status, ...) | |||
| { | |||
| /* optional arguments: */ | |||
| char *server_name = "default"; /* server name */ | |||
| char *server_cmd = NULL; /* server command (not implemented) */ | |||
| va_list ap; /* variable argument pointer */ | |||
| jack_varargs_t va; /* variable arguments */ | |||
| int req_fd = -1; | |||
| int ev_fd = -1; | |||
| @@ -748,21 +750,17 @@ jack_client_open (const char *client_name, | |||
| if (status == NULL) /* no status from caller? */ | |||
| status = &my_status; /* use local status word */ | |||
| *status = 0; | |||
| /* validate parameters */ | |||
| if ((options & ~JackValidOptions)) { | |||
| *status |= JackInvalidOption; | |||
| if ((options & ~JackOpenOptions)) { | |||
| *status |= (JackFailure|JackInvalidOption); | |||
| return NULL; | |||
| } | |||
| /* parse optional arguments */ | |||
| /* parse variable arguments */ | |||
| va_start (ap, status); | |||
| if ((options & JackServerName)) | |||
| server_name = va_arg(ap, char *); | |||
| //if ((options & JackServerCmd)) /* not implemented */ | |||
| // server_cmd = va_arg(ap, char *); | |||
| jack_varargs_parse (options, ap, &va); | |||
| va_end (ap); | |||
| /* External clients need this initialized. It is already set | |||
| @@ -775,9 +773,8 @@ jack_client_open (const char *client_name, | |||
| return NULL; | |||
| } | |||
| if (jack_request_client (ClientExternal, client_name, "", "", | |||
| &res, &req_fd, options, status, | |||
| server_name, server_cmd)) { | |||
| if (jack_request_client (ClientExternal, client_name, options, status, | |||
| &va, &res, &req_fd)) { | |||
| return NULL; | |||
| } | |||
| @@ -893,19 +890,24 @@ jack_get_client_name (jack_client_t *client) | |||
| } | |||
| int | |||
| jack_internal_client_new (const char *client_name, const char *so_name, const char *so_data) | |||
| jack_internal_client_new (const char *client_name, | |||
| const char *so_name, const char *so_data) | |||
| { | |||
| jack_client_connect_result_t res; | |||
| int req_fd; | |||
| jack_varargs_t va; | |||
| jack_status_t status; | |||
| jack_options_t options = JackUseExactName; | |||
| if (getenv("JACK_START_SERVER") == NULL) | |||
| options |= JackNoStartServer; | |||
| return jack_request_client (ClientInternal, client_name, so_name, | |||
| so_data, &res, &req_fd, | |||
| options, &status, NULL, NULL); | |||
| jack_varargs_init (&va); | |||
| va.load_name = (char *) so_name; | |||
| va.load_init = (char *) so_data; | |||
| return jack_request_client (ClientInternal, client_name, | |||
| options, &status, &va, &res, &req_fd); | |||
| } | |||
| void | |||
| @@ -913,16 +915,21 @@ jack_internal_client_close (const char *client_name) | |||
| { | |||
| jack_client_connect_request_t req; | |||
| int fd; | |||
| char *server_name; | |||
| if ((server_name = getenv("JACK_DEFAULT_SERVER")) == NULL) | |||
| server_name = "default"; | |||
| req.load = FALSE; | |||
| snprintf (req.name, sizeof (req.name), "%s", client_name); | |||
| if ((fd = server_connect (NULL)) < 0) { | |||
| if ((fd = server_connect (server_name)) < 0) { | |||
| return; | |||
| } | |||
| if (write (fd, &req, sizeof (req)) != sizeof(req)) { | |||
| jack_error ("cannot deliver ClientUnload request to JACK server."); | |||
| jack_error ("cannot deliver ClientUnload request to JACK " | |||
| "server."); | |||
| } | |||
| /* no response to this request */ | |||
| @@ -931,7 +938,6 @@ jack_internal_client_close (const char *client_name) | |||
| return; | |||
| } | |||
| int | |||
| jack_set_freewheel (jack_client_t* client, int onoff) | |||
| { | |||
| @@ -1489,7 +1495,7 @@ jack_activate (jack_client_t *client) | |||
| req.type = SetClientCapabilities; | |||
| req.x.client_id = client->control->id; | |||
| jack_client_deliver_request (client, &req); | |||
| if (req.status) { | |||
| @@ -0,0 +1,187 @@ | |||
| /* -*- mode: c; c-file-style: "bsd"; -*- */ | |||
| /* | |||
| * Copyright (C) 2004 Jack O'Quin | |||
| * | |||
| * 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. | |||
| * | |||
| * $Id$ | |||
| */ | |||
| #include <config.h> | |||
| #include <errno.h> | |||
| #include <stdarg.h> | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| #include <jack/internal.h> | |||
| #include <jack/intclient.h> | |||
| #include <jack/varargs.h> | |||
| #include "local.h" | |||
| static jack_intclient_t | |||
| jack_intclient_request(RequestType type, jack_client_t *client, | |||
| const char* client_name, jack_options_t options, | |||
| jack_status_t *status, jack_varargs_t *va) | |||
| { | |||
| jack_request_t req; | |||
| memset (&req, 0, sizeof (req)); | |||
| if (strlen (client_name) >= sizeof (req.x.intclient.name)) { | |||
| jack_error ("\"%s\" is too long for a JACK client name.\n" | |||
| "Please use %lu characters or less.", | |||
| client_name, sizeof (req.x.intclient.name)); | |||
| return 0; | |||
| } | |||
| if (va->load_name | |||
| && (strlen (va->load_name) > sizeof (req.x.intclient.path) - 1)) { | |||
| jack_error ("\"%s\" is too long for a shared object name.\n" | |||
| "Please use %lu characters or less.", | |||
| va->load_name, sizeof (req.x.intclient.path) - 1); | |||
| *status |= (JackFailure|JackInvalidOption); | |||
| return 0; | |||
| } | |||
| if (va->load_init | |||
| && (strlen (va->load_init) > sizeof (req.x.intclient.init) - 1)) { | |||
| jack_error ("\"%s\" is too long for internal client init " | |||
| "string.\nPlease use %lu characters or less.", | |||
| va->load_init, sizeof (req.x.intclient.init) - 1); | |||
| *status |= (JackFailure|JackInvalidOption); | |||
| return 0; | |||
| } | |||
| req.type = type; | |||
| req.x.intclient.options = options; | |||
| strncpy (req.x.intclient.name, client_name, | |||
| sizeof (req.x.intclient.name)); | |||
| if (va->load_name) | |||
| strncpy (req.x.intclient.path, va->load_name, | |||
| sizeof (req.x.intclient.path)); | |||
| if (va->load_init) | |||
| strncpy (req.x.intclient.init, va->load_init, | |||
| sizeof (req.x.intclient.init)); | |||
| jack_client_deliver_request (client, &req); | |||
| *status |= req.status; | |||
| if (*status & JackFailure) | |||
| return 0; | |||
| return req.x.intclient.id; | |||
| } | |||
| char * | |||
| jack_get_internal_client_name (jack_client_t *client, | |||
| jack_intclient_t intclient) | |||
| { | |||
| jack_request_t req; | |||
| char *name; | |||
| memset (&req, 0, sizeof (req)); | |||
| req.type = IntClientName; | |||
| req.x.intclient.options = JackNullOption; | |||
| req.x.intclient.id = intclient; | |||
| jack_client_deliver_request (client, &req); | |||
| if (req.status & JackFailure) | |||
| return NULL; | |||
| /* allocate storage for returning the name */ | |||
| name = malloc (strlen (req.x.intclient.name)); | |||
| strcpy (name, req.x.intclient.name); | |||
| return name; | |||
| } | |||
| jack_intclient_t | |||
| jack_internal_client_handle (jack_client_t *client, | |||
| const char *client_name, | |||
| jack_status_t *status) | |||
| { | |||
| jack_request_t req; | |||
| jack_status_t my_status; | |||
| if (status == NULL) /* no status from caller? */ | |||
| status = &my_status; /* use local status word */ | |||
| *status = 0; | |||
| memset (&req, 0, sizeof (req)); | |||
| req.type = IntClientHandle; | |||
| req.x.intclient.options = JackNullOption; | |||
| strncpy (req.x.intclient.name, client_name, | |||
| sizeof (req.x.intclient.name)); | |||
| *status = jack_client_deliver_request (client, &req); | |||
| return req.x.intclient.id; | |||
| } | |||
| jack_intclient_t | |||
| jack_internal_client_load (jack_client_t *client, | |||
| const char *client_name, | |||
| jack_options_t options, | |||
| jack_status_t *status, ...) | |||
| { | |||
| va_list ap; | |||
| jack_varargs_t va; | |||
| jack_status_t my_status; | |||
| if (status == NULL) /* no status from caller? */ | |||
| status = &my_status; /* use local status word */ | |||
| *status = 0; | |||
| /* validate parameters */ | |||
| if ((options & ~JackLoadOptions)) { | |||
| *status |= (JackFailure|JackInvalidOption); | |||
| return 0; | |||
| } | |||
| /* parse variable arguments */ | |||
| va_start (ap, status); | |||
| jack_varargs_parse (options, ap, &va); | |||
| va_end (ap); | |||
| return jack_intclient_request (IntClientLoad, client, client_name, | |||
| options, status, &va); | |||
| } | |||
| jack_status_t | |||
| jack_internal_client_unload (jack_client_t *client, | |||
| jack_intclient_t intclient) | |||
| { | |||
| jack_request_t req; | |||
| jack_status_t status; | |||
| if (intclient) { | |||
| memset (&req, 0, sizeof (req)); | |||
| req.type = IntClientUnload; | |||
| req.x.intclient.options = JackNullOption; | |||
| req.x.intclient.id = intclient; | |||
| jack_client_deliver_request (client, &req); | |||
| status = req.status; | |||
| } else { /* intclient is null */ | |||
| status = (JackNoSuchClient|JackFailure); | |||
| } | |||
| return status; | |||
| } | |||
| @@ -43,8 +43,11 @@ struct _jack_client { | |||
| }; | |||
| extern int jack_client_deliver_request (const jack_client_t *client, jack_request_t *req); | |||
| extern jack_port_t *jack_port_new (const jack_client_t *client, jack_port_id_t port_id, jack_control_t *control); | |||
| extern int jack_client_deliver_request (const jack_client_t *client, | |||
| jack_request_t *req); | |||
| extern jack_port_t *jack_port_new (const jack_client_t *client, | |||
| jack_port_id_t port_id, | |||
| jack_control_t *control); | |||
| extern void *jack_zero_filled_buffer; | |||