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 --- | dnl --- | ||||
| JACK_MAJOR_VERSION=0 | JACK_MAJOR_VERSION=0 | ||||
| JACK_MINOR_VERSION=99 | JACK_MINOR_VERSION=99 | ||||
| JACK_MICRO_VERSION=13 | |||||
| JACK_MICRO_VERSION=14 | |||||
| dnl --- | dnl --- | ||||
| dnl HOWTO: updating the jack protocol version | dnl HOWTO: updating the jack protocol version | ||||
| @@ -5,9 +5,15 @@ CLEANFILES=doxygen-build.stamp | |||||
| DOX=reference.doxygen | DOX=reference.doxygen | ||||
| DOXSOURCES=mainpage.dox transport.dox porting.dox fsm.png fsm.eps \ | 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 | 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 | Using JACK within your program is very simple, and typically consists | ||||
| of just: | 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 | - registering "ports" to enable data to be moved to and from | ||||
| your application. | your application. | ||||
| - registering a "process callback" which will be called at the | - 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: | The JACK programming interfaces are described in several header files: | ||||
| - @ref jack.h "<jack/jack.h>" defines most of the JACK interfaces. | - @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 | - @ref ringbuffer.h "<jack/ringbuffer.h>" defines a simple API for | ||||
| using lock-free ringbuffers. These are a good way to pass data | using lock-free ringbuffers. These are a good way to pass data | ||||
| between threads, when streaming realtime data to slower media, like | between threads, when streaming realtime data to slower media, like | ||||
| @@ -365,13 +365,14 @@ WARN_LOGFILE = | |||||
| INPUT = @top_srcdir@/doc/mainpage.dox \ | INPUT = @top_srcdir@/doc/mainpage.dox \ | ||||
| @top_srcdir@/doc/transport.dox \ | @top_srcdir@/doc/transport.dox \ | ||||
| @top_srcdir@/doc/porting.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/jack.h \ | ||||
| @top_srcdir@/jack/types.h \ | |||||
| @top_srcdir@/jack/transport.h \ | |||||
| @top_srcdir@/jack/ringbuffer.h \ | @top_srcdir@/jack/ringbuffer.h \ | ||||
| @top_srcdir@/jack/thread.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 | # 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 | # 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 | * 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 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 | * @return 0 if successful; otherwise jack_finish() will be called and | ||||
| * the client terminated immediately. | * the client terminated immediately. | ||||
| */ | */ | ||||
| int | 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)); | 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 | * This required entry point is called immediately before the client | ||||
| * is unloaded, which could happen due to a call to | * 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(). | * jack_initialize() or inprocess(). | ||||
| * | * | ||||
| * @param arg the same parameter provided to 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 */ | /* after internal client loaded */ | ||||
| int | int | ||||
| jack_initialize (jack_client_t *client, const char *arg) | |||||
| jack_initialize (jack_client_t *client, const char *load_init) | |||||
| { | { | ||||
| JackTimebaseCallback callback = timebbt; | 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); | &time_beat_type, &time_beats_per_minute); | ||||
| if (rc > 0) { | 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_bar, time_beat_type, | ||||
| time_beats_per_minute); | time_beats_per_minute); | ||||
| } else { | } 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; | callback = timecode; | ||||
| } | } | ||||
| @@ -1,34 +1,169 @@ | |||||
| #include <stdio.h> | #include <stdio.h> | ||||
| #include <string.h> | |||||
| #include <signal.h> | |||||
| #include <unistd.h> | |||||
| #include <getopt.h> | |||||
| #include <jack/jack.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 | int | ||||
| main (int argc, char *argv[]) | 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/jack.h> | ||||
| #include <jack/intclient.h> | |||||
| int | int | ||||
| main (int argc, char *argv[]) | 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; | return 0; | ||||
| } | } | ||||
| @@ -37,6 +37,7 @@ int | |||||
| main (int argc, char *argv[]) | main (int argc, char *argv[]) | ||||
| { | { | ||||
| jack_client_t *client; | jack_client_t *client; | ||||
| jack_status_t status; | |||||
| const char **ports, **connections; | const char **ports, **connections; | ||||
| unsigned int i, j; | unsigned int i, j; | ||||
| int show_con = 0; | 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; | return 1; | ||||
| } | } | ||||
| @@ -93,7 +93,7 @@ main (int argc, char *argv[]) | |||||
| client = jack_client_open (client_name, options, &status, server_name); | client = jack_client_open (client_name, options, &status, server_name); | ||||
| if (client == NULL) { | if (client == NULL) { | ||||
| fprintf (stderr, "jack_client_open() failed, status = %d\n", | |||||
| fprintf (stderr, "jack_client_open() failed, status = 0x%x\n", | |||||
| status); | status); | ||||
| if (status & JackServerFailed) { | if (status & JackServerFailed) { | ||||
| fprintf (stderr, "Unable to connect to JACK server\n"); | fprintf (stderr, "Unable to connect to JACK server\n"); | ||||
| @@ -3,6 +3,7 @@ MAINTAINERCLEANFILES = Makefile.in version.h | |||||
| libjackincludedir = $(includedir)/jack | libjackincludedir = $(includedir)/jack | ||||
| libjackinclude_HEADERS = \ | libjackinclude_HEADERS = \ | ||||
| intclient.h \ | |||||
| jack.h \ | jack.h \ | ||||
| ringbuffer.h \ | ringbuffer.h \ | ||||
| thread.h \ | thread.h \ | ||||
| @@ -26,4 +27,5 @@ noinst_HEADERS = \ | |||||
| shm.h \ | shm.h \ | ||||
| start.h \ | start.h \ | ||||
| unlock.h \ | unlock.h \ | ||||
| varargs.h \ | |||||
| version.h | version.h | ||||
| @@ -162,6 +162,12 @@ int jack_engine_load_driver (jack_engine_t *engine, | |||||
| JSList * driver_params); | JSList * driver_params); | ||||
| void jack_dump_configuration(jack_engine_t *engine, int take_lock); | 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 * | extern jack_client_internal_t * | ||||
| jack_client_internal_by_id (jack_engine_t *engine, jack_client_id_t id); | 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)); | 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__ */ | #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 | /* external clients: set by libjack | ||||
| * internal clients: set by engine */ | * 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; | void *deliver_arg; | ||||
| /* for engine use only */ | /* for engine use only */ | ||||
| @@ -239,10 +239,9 @@ typedef struct { | |||||
| typedef struct { | typedef struct { | ||||
| int32_t status; /* messy name overloading */ | |||||
| uint32_t protocol_v; | 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 client_shm; | ||||
| jack_shm_info_t engine_shm; | jack_shm_info_t engine_shm; | ||||
| @@ -253,12 +252,9 @@ typedef struct { | |||||
| char name[JACK_CLIENT_NAME_SIZE]; /* unique name, if assigned */ | 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 | #ifdef JACK_USE_MACH_THREADS | ||||
| /* specific resources for server/client real-time thread communication */ | /* specific resources for server/client real-time thread communication */ | ||||
| @@ -294,6 +290,10 @@ typedef enum { | |||||
| SetBufferSize = 16, | SetBufferSize = 16, | ||||
| FreeWheel = 17, | FreeWheel = 17, | ||||
| StopFreeWheel = 18, | StopFreeWheel = 18, | ||||
| IntClientHandle = 19, | |||||
| IntClientLoad = 20, | |||||
| IntClientName = 21, | |||||
| IntClientUnload = 22 | |||||
| } RequestType; | } RequestType; | ||||
| struct _jack_request { | struct _jack_request { | ||||
| @@ -320,6 +320,13 @@ struct _jack_request { | |||||
| jack_client_id_t client_id; | jack_client_id_t client_id; | ||||
| int32_t conditional; | int32_t conditional; | ||||
| } timebase; | } 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_client_id_t client_id; | ||||
| jack_nframes_t nframes; | jack_nframes_t nframes; | ||||
| jack_time_t timeout; | jack_time_t timeout; | ||||
| @@ -47,22 +47,25 @@ extern "C" { | |||||
| * @ref JackUseExactName option, the server will modify this name to | * @ref JackUseExactName option, the server will modify this name to | ||||
| * create a unique variant, if needed. | * 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 | * @param status (if non-NULL) an address for JACK to return | ||||
| * information from the open operation. This status word is formed by | * 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 | * @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_client_t *jack_client_open (const char *client_name, | ||||
| jack_options_t options, | jack_options_t options, | ||||
| @@ -110,30 +113,37 @@ int jack_client_name_size (void); | |||||
| * JackNameNotUnique status was returned. In that case, the actual | * JackNameNotUnique status was returned. In that case, the actual | ||||
| * name will differ from the @a client_name requested. | * 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 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); | void jack_internal_client_close (const char *client_name); | ||||
| @@ -1,5 +1,6 @@ | |||||
| /* | /* | ||||
| Copyright (C) 2001 Paul Davis | Copyright (C) 2001 Paul Davis | ||||
| Copyright (C) 2004 Jack O'Quin | |||||
| This program is free software; you can redistribute it and/or modify | 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 | 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; | 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 | * jack_port_t is an opaque type. You may only access it using the | ||||
| * API provided. | * 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. | * Null value to use when no option bits are needed. | ||||
| @@ -251,32 +265,50 @@ enum JackOpenOptions { | |||||
| JackUseExactName = 0x02, | 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 | * 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 | * that was used. If the specified @a client_name plus these | ||||
| * extra characters would be too long, the open fails instead. | * 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 | * Otherwise, it was running already. In either case the caller | ||||
| * is now connected to jackd, so there is no race condition. | * is now connected to jackd, so there is no race condition. | ||||
| * When the server shuts down, the client will find out. | * 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__ */ | #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)\" | 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@ | 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 | 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, | if ((engine = jack_engine_new (realtime, realtime_priority, | ||||
| do_mlock, do_unlock, | 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"); | fprintf (stderr, "cannot create engine\n"); | ||||
| return -1; | 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> | 2004-10-14 Jack O'Quin <joq@io.com> | ||||
| * new API function: jack_get_xrun_delayed_usecs() | * new API function: jack_get_xrun_delayed_usecs() | ||||
| @@ -1,16 +1,17 @@ | |||||
| MAINTAINERCLEANFILES = Makefile.in | MAINTAINERCLEANFILES = Makefile.in | ||||
| SOURCE_FILES = \ | SOURCE_FILES = \ | ||||
| shm.c \ | |||||
| client.c \ | client.c \ | ||||
| driver.c \ | driver.c \ | ||||
| unlock.c \ | |||||
| intclient.c \ | |||||
| pool.c \ | pool.c \ | ||||
| port.c \ | port.c \ | ||||
| ringbuffer.c \ | ringbuffer.c \ | ||||
| shm.c \ | |||||
| thread.c \ | thread.c \ | ||||
| timestamps.c \ | timestamps.c \ | ||||
| transclient.c | |||||
| transclient.c \ | |||||
| unlock.c | |||||
| lib_LTLIBRARIES = libjack.la | lib_LTLIBRARIES = libjack.la | ||||
| @@ -45,6 +45,7 @@ | |||||
| #include <jack/shm.h> | #include <jack/shm.h> | ||||
| #include <jack/unlock.h> | #include <jack/unlock.h> | ||||
| #include <jack/thread.h> | #include <jack/thread.h> | ||||
| #include <jack/varargs.h> | |||||
| #include <sysdeps/time.h> | #include <sysdeps/time.h> | ||||
| JACK_TIME_GLOBAL_DECL; /* One instance per process. */ | JACK_TIME_GLOBAL_DECL; /* One instance per process. */ | ||||
| @@ -146,12 +147,13 @@ oop_client_deliver_request (void *ptr, jack_request_t *req) | |||||
| int | int | ||||
| jack_client_deliver_request (const jack_client_t *client, jack_request_t *req) | 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 * | jack_client_t * | ||||
| @@ -344,7 +346,7 @@ server_connect (const char *server_name) | |||||
| } | } | ||||
| //JOQ: temporary debug code | //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 | //JOQ: use server_name as part of socket path | ||||
| addr.sun_family = AF_UNIX; | 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. */ | /* Exec the JACK server in this process. Does not return. */ | ||||
| static void | static void | ||||
| _start_server (const char *server_cmd) | |||||
| _start_server (void) | |||||
| { | { | ||||
| FILE* fp = 0; | FILE* fp = 0; | ||||
| char filename[255]; | char filename[255]; | ||||
| @@ -426,11 +428,6 @@ _start_server (const char *server_cmd) | |||||
| int good = 0; | int good = 0; | ||||
| int ret; | int ret; | ||||
| //JOQ: | |||||
| // if (start_command) { | |||||
| // parse user-supplied command | |||||
| // } else { | |||||
| snprintf(filename, 255, "%s/.jackdrc", getenv("HOME")); | snprintf(filename, 255, "%s/.jackdrc", getenv("HOME")); | ||||
| fp = fopen(filename, "r"); | fp = fopen(filename, "r"); | ||||
| @@ -501,7 +498,7 @@ _start_server (const char *server_cmd) | |||||
| } | } | ||||
| int | int | ||||
| start_server (jack_options_t options, const char *server_cmd) | |||||
| start_server (jack_options_t options) | |||||
| { | { | ||||
| if ((options & JackNoStartServer) | if ((options & JackNoStartServer) | ||||
| || (getenv("JACK_NO_START_SERVER") != NULL)) { | || (getenv("JACK_NO_START_SERVER") != NULL)) { | ||||
| @@ -521,7 +518,7 @@ start_server (jack_options_t options, const char *server_cmd) | |||||
| case 0: /* child process */ | case 0: /* child process */ | ||||
| switch (fork()) { | switch (fork()) { | ||||
| case 0: /* grandchild process */ | case 0: /* grandchild process */ | ||||
| _start_server(server_cmd); | |||||
| _start_server(); | |||||
| _exit (99); /* exec failed */ | _exit (99); /* exec failed */ | ||||
| case -1: | case -1: | ||||
| _exit (98); | _exit (98); | ||||
| @@ -537,18 +534,15 @@ start_server (jack_options_t options, const char *server_cmd) | |||||
| } | } | ||||
| static int | 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; | jack_client_connect_request_t req; | ||||
| *req_fd = -1; | *req_fd = -1; | ||||
| memset (&req, 0, sizeof (req)); | memset (&req, 0, sizeof (req)); | ||||
| req.options = options; | req.options = options; | ||||
| if (strlen (client_name) >= sizeof (req.name)) { | if (strlen (client_name) >= sizeof (req.name)) { | ||||
| @@ -559,48 +553,54 @@ jack_request_client (ClientType type, const char* client_name, | |||||
| return -1; | 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" | jack_error ("\"%s\" is too long to be used as a JACK shared" | ||||
| " object name.\n" | " object name.\n" | ||||
| "Please use %lu characters or less.", | "Please use %lu characters or less.", | ||||
| so_name, sizeof (req.object_path) - 1); | |||||
| va->load_name, sizeof (req.object_path) - 1); | |||||
| return -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" | jack_error ("\"%s\" is too long to be used as a JACK shared" | ||||
| " object data string.\n" | " object data string.\n" | ||||
| "Please use %lu characters or less.", | "Please use %lu characters or less.", | ||||
| so_data, sizeof (req.object_data) - 1); | |||||
| va->load_init, sizeof (req.object_data) - 1); | |||||
| return -1; | return -1; | ||||
| } | } | ||||
| if ((*req_fd = server_connect (server_name)) < 0) { | |||||
| if ((*req_fd = server_connect (va->server_name)) < 0) { | |||||
| int trys; | int trys; | ||||
| if (start_server(options, server_cmd)) { | |||||
| *status |= JackServerFailed; | |||||
| if (start_server(options)) { | |||||
| *status |= (JackFailure|JackServerFailed); | |||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| trys = 5; | trys = 5; | ||||
| do { | do { | ||||
| sleep(1); | sleep(1); | ||||
| if (--trys < 0) { | if (--trys < 0) { | ||||
| *status |= JackServerFailed; | |||||
| *status |= (JackFailure|JackServerFailed); | |||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| } while ((*req_fd = server_connect (server_name)) < 0); | |||||
| } while ((*req_fd = server_connect (va->server_name)) < 0); | |||||
| *status |= JackServerStarted; | *status |= JackServerStarted; | ||||
| } | } | ||||
| req.load = TRUE; | req.load = TRUE; | ||||
| req.type = type; | 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)) { | if (write (*req_fd, &req, sizeof (req)) != sizeof (req)) { | ||||
| jack_error ("cannot send request to jack server (%s)", | jack_error ("cannot send request to jack server (%s)", | ||||
| strerror (errno)); | strerror (errno)); | ||||
| *status |= (JackFailure|JackServerError); | |||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| @@ -608,33 +608,36 @@ jack_request_client (ClientType type, const char* client_name, | |||||
| if (errno == 0) { | if (errno == 0) { | ||||
| /* server shut the socket */ | /* 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; | goto fail; | ||||
| } | } | ||||
| if (errno == ECONNRESET) { | 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; | goto fail; | ||||
| } | } | ||||
| jack_error ("cannot read regsponse from jack server (%s)", | |||||
| jack_error ("cannot read response from jack server (%s)", | |||||
| strerror (errno)); | strerror (errno)); | ||||
| *status |= (JackFailure|JackServerError); | |||||
| goto fail; | 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; | goto fail; | ||||
| } | } | ||||
| if (res->protocol_v != jack_protocol_version){ | if (res->protocol_v != jack_protocol_version){ | ||||
| jack_error ("application linked against incompatible libjack" | jack_error ("application linked against incompatible libjack" | ||||
| " version."); | " version."); | ||||
| *status |= (JackFailure|JackServerError); | |||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| @@ -735,9 +738,8 @@ jack_client_open (const char *client_name, | |||||
| jack_status_t *status, ...) | jack_status_t *status, ...) | ||||
| { | { | ||||
| /* optional arguments: */ | /* optional arguments: */ | ||||
| char *server_name = "default"; /* server name */ | |||||
| char *server_cmd = NULL; /* server command (not implemented) */ | |||||
| va_list ap; /* variable argument pointer */ | va_list ap; /* variable argument pointer */ | ||||
| jack_varargs_t va; /* variable arguments */ | |||||
| int req_fd = -1; | int req_fd = -1; | ||||
| int ev_fd = -1; | int ev_fd = -1; | ||||
| @@ -748,21 +750,17 @@ jack_client_open (const char *client_name, | |||||
| if (status == NULL) /* no status from caller? */ | if (status == NULL) /* no status from caller? */ | ||||
| status = &my_status; /* use local status word */ | status = &my_status; /* use local status word */ | ||||
| *status = 0; | *status = 0; | ||||
| /* validate parameters */ | /* validate parameters */ | ||||
| if ((options & ~JackValidOptions)) { | |||||
| *status |= JackInvalidOption; | |||||
| if ((options & ~JackOpenOptions)) { | |||||
| *status |= (JackFailure|JackInvalidOption); | |||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| /* parse optional arguments */ | |||||
| /* parse variable arguments */ | |||||
| va_start (ap, status); | 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); | va_end (ap); | ||||
| /* External clients need this initialized. It is already set | /* External clients need this initialized. It is already set | ||||
| @@ -775,9 +773,8 @@ jack_client_open (const char *client_name, | |||||
| return NULL; | 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; | return NULL; | ||||
| } | } | ||||
| @@ -893,19 +890,24 @@ jack_get_client_name (jack_client_t *client) | |||||
| } | } | ||||
| int | 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; | jack_client_connect_result_t res; | ||||
| int req_fd; | int req_fd; | ||||
| jack_varargs_t va; | |||||
| jack_status_t status; | jack_status_t status; | ||||
| jack_options_t options = JackUseExactName; | jack_options_t options = JackUseExactName; | ||||
| if (getenv("JACK_START_SERVER") == NULL) | if (getenv("JACK_START_SERVER") == NULL) | ||||
| options |= JackNoStartServer; | 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 | void | ||||
| @@ -913,16 +915,21 @@ jack_internal_client_close (const char *client_name) | |||||
| { | { | ||||
| jack_client_connect_request_t req; | jack_client_connect_request_t req; | ||||
| int fd; | int fd; | ||||
| char *server_name; | |||||
| if ((server_name = getenv("JACK_DEFAULT_SERVER")) == NULL) | |||||
| server_name = "default"; | |||||
| req.load = FALSE; | req.load = FALSE; | ||||
| snprintf (req.name, sizeof (req.name), "%s", client_name); | snprintf (req.name, sizeof (req.name), "%s", client_name); | ||||
| if ((fd = server_connect (NULL)) < 0) { | |||||
| if ((fd = server_connect (server_name)) < 0) { | |||||
| return; | return; | ||||
| } | } | ||||
| if (write (fd, &req, sizeof (req)) != sizeof(req)) { | 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 */ | /* no response to this request */ | ||||
| @@ -931,7 +938,6 @@ jack_internal_client_close (const char *client_name) | |||||
| return; | return; | ||||
| } | } | ||||
| int | int | ||||
| jack_set_freewheel (jack_client_t* client, int onoff) | jack_set_freewheel (jack_client_t* client, int onoff) | ||||
| { | { | ||||
| @@ -1489,7 +1495,7 @@ jack_activate (jack_client_t *client) | |||||
| req.type = SetClientCapabilities; | req.type = SetClientCapabilities; | ||||
| req.x.client_id = client->control->id; | req.x.client_id = client->control->id; | ||||
| jack_client_deliver_request (client, &req); | jack_client_deliver_request (client, &req); | ||||
| if (req.status) { | 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; | extern void *jack_zero_filled_buffer; | ||||