maybe its possible, that the feedback to this patch can be constructive.
a) i layed out usecases for multiple veto clients.
b) its more complex, to implement a single vetoer.
patch is against jack1.
--
torben Hohn
http://galan.sourceforge.net -- The graphical Audio language
diff --git a/example-clients/Makefile.am b/example-clients/Makefile.am
index 8eddc4c..f16978f 100644
--- a/example-clients/Makefile.am
+++ b/example-clients/Makefile.am
@@ -6,7 +6,8 @@ bin_PROGRAMS = jack_simple_client \
jack_metro \
jack_showtime \
jack_midisine \
- jack_midiseq
+ jack_midiseq \
+ jack_veto_test
if HAVE_SNDFILE
# note! jackrec_CFLAGS syntax not supported by automake-1.4
@@ -44,6 +45,10 @@ jack_midisine_SOURCES = midisine.c
jack_midisine_LDFLAGS = @OS_LDFLAGS@
jack_midisine_LDADD = $(top_builddir)/libjack/libjack.la
+jack_veto_test_SOURCES = veto_test.c
+jack_veto_test_LDFLAGS = @OS_LDFLAGS@
+jack_veto_test_LDADD = $(top_builddir)/libjack/libjack.la
+
#
# sample in-process client(s)
#
diff --git a/example-clients/veto_test.c b/example-clients/veto_test.c
new file mode 100644
index 0000000..ad9e094
--- /dev/null
+++ b/example-clients/veto_test.c
@@ -0,0 +1,106 @@
+/** @file simple_client.c
+ *
+ * @brief This simple client demonstrates the most basic features of JACK
+ * as they would be used by many applications.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <jack/jack.h>
+
+jack_client_t *client;
+
+
+int veto_cb( const char *req_client, jack_port_id_t a, jack_port_id_t b, int connect, void *arg )
+{
+ const char *name_a = jack_port_name( jack_port_by_id( client, a ) );
+ const char *name_b = jack_port_name( jack_port_by_id( client, b ) );
+
+ if( connect )
+ printf( "connection attempt... requestor: %s ( %s -> %s )\n", req_client, name_a, name_b );
+ else
+ printf( "disconnection attempt... requestor: %s ( %s -> %s )\n", req_client, name_a, name_b );
+
+ if( strcmp( req_client, "ardour" ) && strcmp( req_client, "qjackctl" ) ) {
+ // its not ardour, and its not qjack... lets veto :)
+ return 1;
+ } else {
+ // ok... ardour of qjack.
+ return 0;
+ }
+}
+
+/**
+ * JACK calls this shutdown_callback if the server ever shuts down or
+ * decides to disconnect the client.
+ */
+void
+jack_shutdown (void *arg)
+{
+ exit (1);
+}
+
+int
+main (int argc, char *argv[])
+{
+ const char *client_name = "veto";
+ const char *server_name = NULL;
+ jack_options_t options = JackNullOption;
+ jack_status_t status;
+
+ /* open a client connection to the JACK server */
+
+ client = jack_client_open (client_name, options, &status, server_name);
+ if (client == NULL) {
+ fprintf (stderr, "jack_client_open() failed, "
+ "status = 0x%2.0x\n", status);
+ if (status & JackServerFailed) {
+ fprintf (stderr, "Unable to connect to JACK server\n");
+ }
+ exit (1);
+ }
+ if (status & JackServerStarted) {
+ fprintf (stderr, "JACK server started\n");
+ }
+ if (status & JackNameNotUnique) {
+ client_name = jack_get_client_name(client);
+ fprintf (stderr, "unique name `%s' assigned\n", client_name);
+ }
+
+ /* tell the JACK server to call `process()' whenever
+ there is work to be done.
+ */
+
+ jack_set_connection_veto_callback (client, veto_cb, 0);
+
+ /* tell the JACK server to call `jack_shutdown()' if
+ it ever shuts down, either entirely, or if it
+ just decides to stop calling us.
+ */
+
+ jack_on_shutdown (client, jack_shutdown, 0);
+
+ /* display the current sample rate.
+ */
+
+ if (jack_activate (client)) {
+ fprintf (stderr, "cannot activate client");
+ exit (1);
+ }
+
+ /* keep running until stopped by the user */
+
+ sleep (-1);
+
+ /* this is never reached but if the program
+ had some other way to exit besides being killed,
+ they would be important to call.
+ */
+
+ jack_client_close (client);
+ exit (0);
+}
diff --git a/jack/internal.h b/jack/internal.h
index b51ef44..a5fc559 100644
--- a/jack/internal.h
+++ b/jack/internal.h
@@ -217,7 +217,9 @@ typedef enum {
StartFreewheel,
StopFreewheel,
ClientRegistered,
- ClientUnregistered
+ ClientUnregistered,
+ PortConnectVeto,
+ PortDisconnectVeto
} JackEventType;
typedef struct {
@@ -233,6 +235,9 @@ typedef struct {
jack_port_type_id_t ptid;
jack_port_id_t other_id;
} y;
+ union {
+ jack_port_id_t port_id;
+ } z;
} POST_PACKED_STRUCTURE jack_event_t;
typedef enum {
@@ -291,6 +296,7 @@ typedef volatile struct {
volatile uint8_t timebase_cb_cbset;
volatile uint8_t freewheel_cb_cbset;
volatile uint8_t client_register_cbset;
+ volatile uint8_t connect_veto_cbset;
volatile uint8_t thread_cb_cbset;
} POST_PACKED_STRUCTURE jack_client_control_t;
@@ -389,6 +395,7 @@ struct _jack_request {
struct {
char source_port[JACK_PORT_NAME_SIZE];
char destination_port[JACK_PORT_NAME_SIZE];
+ jack_client_id_t client_id;
} POST_PACKED_STRUCTURE connect;
struct {
int32_t nports;
diff --git a/jack/jack.h b/jack/jack.h
index 64ba8a7..2cd66bf 100644
--- a/jack/jack.h
+++ b/jack/jack.h
@@ -365,6 +365,16 @@ int jack_set_graph_order_callback (jack_client_t *,
int jack_set_xrun_callback (jack_client_t *,
JackXRunCallback xrun_callback, void *arg);
+/**
+ * Tell the JACK server to call @a veto_callback whenever there is an
+ * attempt to connect or disconnect a port.
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+
+int jack_set_connection_veto_callback(jack_client_t *client,
+ JackConnectVetoCallback veto_callback,
+ void *arg);
/*@}*/
/**
diff --git a/jack/types.h b/jack/types.h
index 020a51b..9e90a0b 100644
--- a/jack/types.h
+++ b/jack/types.h
@@ -186,6 +186,20 @@ typedef void (*JackPortConnectCallback)(jack_port_id_t a, jack_port_id_t b, int
/**
* Prototype for the client supplied function that is called
+ * whenever a Connection or Disconnection is made.
+ * if it returns non-zero, it means a veto against the process,
+ * and the connection/disconnection, will not be made.
+ *
+ * @param a one of two ports connected or disconnected
+ * @param b one of two ports connected or disconnected
+ * @param connect non-zero if ports were connected
+ * zero if ports were disconnected
+ * @param arg pointer to a client supplied data
+ */
+typedef int (*JackConnectVetoCallback)(const char *req_client, jack_port_id_t a, jack_port_id_t b, int connect, void* arg);
+
+/**
+ * Prototype for the client supplied function that is called
* whenever jackd starts or stops freewheeling.
*
* @param starting non-zero if we start starting to freewheel, zero otherwise
diff --git a/jackd/clientengine.c b/jackd/clientengine.c
index 5de971f..b5db3b5 100644
--- a/jackd/clientengine.c
+++ b/jackd/clientengine.c
@@ -538,6 +538,7 @@ jack_setup_client_control (jack_engine_t *engine, int fd,
client->control->graph_order_cbset = FALSE;
client->control->client_register_cbset = FALSE;
client->control->thread_cb_cbset = FALSE;
+ client->control->connect_veto_cbset = FALSE;
#if 0
if (type != ClientExternal) {
diff --git a/jackd/engine.c b/jackd/engine.c
index d3f7263..b38a6c7 100644
--- a/jackd/engine.c
+++ b/jackd/engine.c
@@ -89,10 +89,10 @@ static int jack_rechain_graph (jack_engine_t *engine);
static void jack_clear_fifos (jack_engine_t *engine);
static int jack_port_do_connect (jack_engine_t *engine,
const char *source_port,
- const char *destination_port);
+ const char *destination_port, jack_client_id_t req_id);
static int jack_port_do_disconnect (jack_engine_t *engine,
const char *source_port,
- const char *destination_port);
+ const char *destination_port, jack_client_id_t req_id);
static int jack_port_do_disconnect_all (jack_engine_t *engine,
jack_port_id_t);
static int jack_port_do_unregister (jack_engine_t *engine, jack_request_t *);
@@ -1229,7 +1229,7 @@ do_request (jack_engine_t *engine, jack_request_t *req, int *reply_fd)
case ConnectPorts:
req->status = jack_port_do_connect
(engine, req->x.connect.source_port,
- req->x.connect.destination_port);
+ req->x.connect.destination_port, req->x.connect.client_id);
break;
case DisconnectPort:
@@ -1240,7 +1240,7 @@ do_request (jack_engine_t *engine, jack_request_t *req, int *reply_fd)
case DisconnectPorts:
req->status = jack_port_do_disconnect
(engine, req->x.connect.source_port,
- req->x.connect.destination_port);
+ req->x.connect.destination_port, req->x.connect.client_id);
break;
case ActivateClient:
@@ -2423,6 +2423,37 @@ jack_deliver_event_to_all (jack_engine_t *engine, jack_event_t *event)
jack_unlock_graph (engine);
}
+static int
+jack_query_veto_clients (jack_engine_t *engine, jack_client_id_t requestor, jack_port_id_t a, jack_port_id_t b, int connected)
+{
+ JSList *node;
+ jack_event_t event;
+
+ int retval = 0;
+ int reply;
+
+ jack_client_internal_t* req_client = jack_client_internal_by_id (engine, requestor);
+
+ event.type = (connected ? PortConnectVeto : PortDisconnectVeto);
+ snprintf (event.x.name, sizeof (event.x.name), "%s", req_client->control->name);
+ event.y.other_id = a;
+ event.z.port_id = b;
+
+ /* GRAPH MUST BE LOCKED : see callers of jack_send_connection_notification()
+ */
+
+ for (node = engine->clients; node; node = jack_slist_next (node)) {
+ jack_client_internal_t* client = (jack_client_internal_t*) node->data;
+ if (client->control->connect_veto_cbset) {
+
+ reply = jack_deliver_event (engine, client, &event);
+ if( reply >= 0 )
+ retval |= reply;
+ }
+ }
+ return retval;
+}
+
static void
jack_notify_all_port_interested_clients (jack_engine_t *engine, jack_client_id_t src, jack_client_id_t dst, jack_port_id_t a, jack_port_id_t b, int connected)
{
@@ -2453,7 +2484,7 @@ static int
jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client,
jack_event_t *event)
{
- char status;
+ char status = -1;
/* caller must hold the graph lock */
@@ -2603,7 +2634,7 @@ jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client,
client->event_fd,
pfd[0].revents,
poll_timeout);
- status = 1;
+ status = -2;
#ifdef __linux
}
#endif
@@ -2628,7 +2659,7 @@ jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client,
event->type);
}
- if (status) {
+ if (status < 0) {
client->error += JACK_ERROR_WITH_SOCKETS;
jack_engine_signal_problems (engine);
}
@@ -2636,7 +2667,7 @@ jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client,
}
DEBUG ("event delivered");
- return 0;
+ return status;
}
int
@@ -3202,7 +3233,8 @@ void jack_dump_configuration(jack_engine_t *engine, int take_lock)
static int
jack_port_do_connect (jack_engine_t *engine,
const char *source_port,
- const char *destination_port)
+ const char *destination_port,
+ jack_client_id_t req_id )
{
jack_connection_internal_t *connection;
jack_port_internal_t *srcport, *dstport;
@@ -3291,6 +3323,15 @@ jack_port_do_connect (jack_engine_t *engine,
jack_lock_graph (engine);
+ // XXX: do i really need to have the graph locked, las ?
+ if( jack_query_veto_clients( engine, req_id, src_id, dst_id, 1 ) ) {
+ // Connection vetoed... go to the UN if you dont like it.
+ free (connection);
+ jack_unlock_graph (engine);
+
+ // we return success here, because most dumb clients exit, when the connection fails
+ return 0;
+ }
if (dstport->connections && !dstport->shared->has_mixdown) {
jack_port_type_info_t *port_type =
jack_port_type_info (engine, dstport);
@@ -3532,7 +3573,7 @@ jack_port_do_disconnect_all (jack_engine_t *engine,
static int
jack_port_do_disconnect (jack_engine_t *engine,
const char *source_port,
- const char *destination_port)
+ const char *destination_port, jack_client_id_t req_id)
{
jack_port_internal_t *srcport, *dstport;
int ret = -1;
@@ -3550,8 +3591,18 @@ jack_port_do_disconnect (jack_engine_t *engine,
return -1;
}
+ jack_port_id_t src_id = srcport->shared->id;
+ jack_port_id_t dst_id = dstport->shared->id;
+
jack_lock_graph (engine);
+ // XXX: do i really need to have the graph locked, las ?
+ if( jack_query_veto_clients( engine, req_id, src_id, dst_id, 0 ) ) {
+ // Connection vetoed... go to the UN if you dont like it.
+ jack_unlock_graph (engine);
+ return -1;
+ }
+
ret = jack_port_disconnect_internal (engine, srcport, dstport);
jack_unlock_graph (engine);
diff --git a/libjack/client.c b/libjack/client.c
index ed0bee8..4b0f6db 100644
--- a/libjack/client.c
+++ b/libjack/client.c
@@ -468,6 +468,19 @@ jack_client_handle_port_connection (jack_client_t *client, jack_event_t *event)
return 0;
}
+int
+jack_client_handle_connect_veto (jack_client_t *client, jack_event_t *event)
+{
+ int retval = 0;
+
+ if (client->control->connect_veto_cbset) {
+ retval = client->connect_veto (event->x.name, event->y.other_id, event->z.port_id,
+ (event->type == PortConnectVeto ? 1 : 0),
+ client->connect_veto_arg);
+ }
+
+ return retval;
+}
#if JACK_USE_MACH_THREADS
static int
@@ -1412,6 +1425,12 @@ jack_client_process_events (jack_client_t* client)
case StopFreewheel:
jack_stop_freewheel (client);
break;
+
+ case PortConnectVeto:
+ case PortDisconnectVeto:
+ status = jack_client_handle_connect_veto
+ (client, &event);
+ break;
}
DEBUG ("client has dealt with the event, writing "
@@ -2173,6 +2192,7 @@ jack_connect (jack_client_t *client, const char *source_port,
snprintf (req.x.connect.destination_port,
sizeof (req.x.connect.destination_port),
"%s", destination_port);
+ req.x.connect.client_id = client->control->id;
return jack_client_deliver_request (client, &req);
}
@@ -2210,6 +2230,7 @@ jack_disconnect (jack_client_t *client, const char *source_port,
snprintf (req.x.connect.destination_port,
sizeof (req.x.connect.destination_port),
"%s", destination_port);
+ req.x.connect.client_id = client->control->id;
return jack_client_deliver_request (client, &req);
}
@@ -2360,6 +2381,22 @@ jack_set_client_registration_callback(jack_client_t *client,
}
int
+jack_set_connection_veto_callback(jack_client_t *client,
+ JackConnectVetoCallback callback,
+ void *arg)
+{
+ if (client->control->active) {
+ jack_error ("You cannot set callbacks on an active client.");
+ return -1;
+ }
+ client->connect_veto_arg = arg;
+ client->connect_veto = callback;
+ client->control->connect_veto_cbset = (callback != NULL);
+ return 0;
+}
+
+
+int
jack_set_process_thread(jack_client_t* client, JackThreadCallback callback, void *arg)
{
if (client->control->active) {
diff --git a/libjack/local.h b/libjack/local.h
index 7ee4bbb..95f714b 100644
--- a/libjack/local.h
+++ b/libjack/local.h
@@ -68,7 +68,9 @@ struct _jack_client {
void *freewheel_arg;
JackClientRegistrationCallback client_register;
void *client_register_arg;
- JackThreadCallback thread_cb;
+ JackConnectVetoCallback connect_veto;
+ void *connect_veto_arg;
+ JackThreadCallback thread_cb;
void *thread_cb_arg;
/* external clients: set by libjack
_______________________________________________
Jack-Devel mailing list
Jack-Devel@...
http://lists.jackaudio.org/listinfo.cgi/jack-devel-jackaudio.org