diff options
Diffstat (limited to 'seq/aconnect/aconnect.c')
-rw-r--r-- | seq/aconnect/aconnect.c | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/seq/aconnect/aconnect.c b/seq/aconnect/aconnect.c new file mode 100644 index 0000000..1a50666 --- /dev/null +++ b/seq/aconnect/aconnect.c @@ -0,0 +1,411 @@ +/* + * connect / disconnect two subscriber ports + * ver.0.1.3 + * + * Copyright (C) 1999 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + */ + +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <stdarg.h> +#include <locale.h> +#include <sys/ioctl.h> +#include <alsa/asoundlib.h> +#include "aconfig.h" +#include "gettext.h" + +static void error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...) +{ + va_list arg; + + if (err == ENOENT) /* Ignore those misleading "warnings" */ + return; + va_start(arg, fmt); + fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function); + vfprintf(stderr, fmt, arg); + if (err) + fprintf(stderr, ": %s", snd_strerror(err)); + putc('\n', stderr); + va_end(arg); +} + +static void usage(void) +{ + printf(_("aconnect - ALSA sequencer connection manager\n")); + printf(_("Copyright (C) 1999-2000 Takashi Iwai\n")); + printf(_("Usage:\n")); + printf(_(" * Connection/disconnection between two ports\n")); + printf(_(" aconnect [-options] sender receiver\n")); + printf(_(" sender, receiver = client:port pair\n")); + printf(_(" -d,--disconnect disconnect\n")); + printf(_(" -e,--exclusive exclusive connection\n")); + printf(_(" -r,--real # convert real-time-stamp on queue\n")); + printf(_(" -t,--tick # convert tick-time-stamp on queue\n")); + printf(_(" * List connected ports (no subscription action)\n")); + printf(_(" aconnect -i|-o [-options]\n")); + printf(_(" -i,--input list input (readable) ports\n")); + printf(_(" -o,--output list output (writable) ports\n")); + printf(_(" -l,--list list current connections of each port\n")); + printf(_(" * Remove all exported connections\n")); + printf(_(" -x, --removeall\n")); +} + +/* + * check permission (capability) of specified port + */ + +#define LIST_INPUT 1 +#define LIST_OUTPUT 2 + +#define perm_ok(pinfo,bits) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits)) + +static int check_permission(snd_seq_port_info_t *pinfo, int perm) +{ + if (perm) { + if (perm & LIST_INPUT) { + if (perm_ok(pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ)) + goto __ok; + } + if (perm & LIST_OUTPUT) { + if (perm_ok(pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE)) + goto __ok; + } + return 0; + } + __ok: + if (snd_seq_port_info_get_capability(pinfo) & SND_SEQ_PORT_CAP_NO_EXPORT) + return 0; + return 1; +} + +/* + * list subscribers of specified type + */ +static void list_each_subs(snd_seq_t *seq, snd_seq_query_subscribe_t *subs, int type, const char *msg) +{ + int count = 0; + snd_seq_query_subscribe_set_type(subs, type); + snd_seq_query_subscribe_set_index(subs, 0); + while (snd_seq_query_port_subscribers(seq, subs) >= 0) { + const snd_seq_addr_t *addr; + if (count++ == 0) + printf("\t%s: ", msg); + else + printf(", "); + addr = snd_seq_query_subscribe_get_addr(subs); + printf("%d:%d", addr->client, addr->port); + if (snd_seq_query_subscribe_get_exclusive(subs)) + printf("[ex]"); + if (snd_seq_query_subscribe_get_time_update(subs)) + printf("[%s:%d]", + (snd_seq_query_subscribe_get_time_real(subs) ? "real" : "tick"), + snd_seq_query_subscribe_get_queue(subs)); + snd_seq_query_subscribe_set_index(subs, snd_seq_query_subscribe_get_index(subs) + 1); + } + if (count > 0) + printf("\n"); +} + +/* + * list subscribers + */ +static void list_subscribers(snd_seq_t *seq, const snd_seq_addr_t *addr) +{ + snd_seq_query_subscribe_t *subs; + snd_seq_query_subscribe_alloca(&subs); + snd_seq_query_subscribe_set_root(subs, addr); + list_each_subs(seq, subs, SND_SEQ_QUERY_SUBS_READ, _("Connecting To")); + list_each_subs(seq, subs, SND_SEQ_QUERY_SUBS_WRITE, _("Connected From")); +} + +/* + * search all ports + */ +typedef void (*action_func_t)(snd_seq_t *seq, snd_seq_client_info_t *cinfo, snd_seq_port_info_t *pinfo, int count); + +static void do_search_port(snd_seq_t *seq, int perm, action_func_t do_action) +{ + snd_seq_client_info_t *cinfo; + snd_seq_port_info_t *pinfo; + int count; + + snd_seq_client_info_alloca(&cinfo); + snd_seq_port_info_alloca(&pinfo); + snd_seq_client_info_set_client(cinfo, -1); + while (snd_seq_query_next_client(seq, cinfo) >= 0) { + /* reset query info */ + snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); + snd_seq_port_info_set_port(pinfo, -1); + count = 0; + while (snd_seq_query_next_port(seq, pinfo) >= 0) { + if (check_permission(pinfo, perm)) { + do_action(seq, cinfo, pinfo, count); + count++; + } + } + } +} + + +static void print_port(snd_seq_t *seq, snd_seq_client_info_t *cinfo, + snd_seq_port_info_t *pinfo, int count) +{ + if (! count) { + printf(_("client %d: '%s' [type=%s]\n"), + snd_seq_client_info_get_client(cinfo), + snd_seq_client_info_get_name(cinfo), + (snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? + _("user") : _("kernel"))); + } + printf(" %3d '%-16s'\n", + snd_seq_port_info_get_port(pinfo), + snd_seq_port_info_get_name(pinfo)); +} + +static void print_port_and_subs(snd_seq_t *seq, snd_seq_client_info_t *cinfo, + snd_seq_port_info_t *pinfo, int count) +{ + print_port(seq, cinfo, pinfo, count); + list_subscribers(seq, snd_seq_port_info_get_addr(pinfo)); +} + + +/* + * remove all (exported) connections + */ +static void remove_connection(snd_seq_t *seq, snd_seq_client_info_t *cinfo, + snd_seq_port_info_t *pinfo, int count) +{ + snd_seq_query_subscribe_t *query; + + snd_seq_query_subscribe_alloca(&query); + snd_seq_query_subscribe_set_root(query, snd_seq_port_info_get_addr(pinfo)); + + snd_seq_query_subscribe_set_type(query, SND_SEQ_QUERY_SUBS_READ); + snd_seq_query_subscribe_set_index(query, 0); + for (; snd_seq_query_port_subscribers(seq, query) >= 0; + snd_seq_query_subscribe_set_index(query, snd_seq_query_subscribe_get_index(query) + 1)) { + snd_seq_port_info_t *port; + snd_seq_port_subscribe_t *subs; + const snd_seq_addr_t *sender = snd_seq_query_subscribe_get_root(query); + const snd_seq_addr_t *dest = snd_seq_query_subscribe_get_addr(query); + snd_seq_port_info_alloca(&port); + if (snd_seq_get_any_port_info(seq, dest->client, dest->port, port) < 0) + continue; + if (!(snd_seq_port_info_get_capability(port) & SND_SEQ_PORT_CAP_SUBS_WRITE)) + continue; + if (snd_seq_port_info_get_capability(port) & SND_SEQ_PORT_CAP_NO_EXPORT) + continue; + snd_seq_port_subscribe_alloca(&subs); + snd_seq_port_subscribe_set_queue(subs, snd_seq_query_subscribe_get_queue(query)); + snd_seq_port_subscribe_set_sender(subs, sender); + snd_seq_port_subscribe_set_dest(subs, dest); + snd_seq_unsubscribe_port(seq, subs); + } + + snd_seq_query_subscribe_set_type(query, SND_SEQ_QUERY_SUBS_WRITE); + snd_seq_query_subscribe_set_index(query, 0); + for (; snd_seq_query_port_subscribers(seq, query) >= 0; + snd_seq_query_subscribe_set_index(query, snd_seq_query_subscribe_get_index(query) + 1)) { + snd_seq_port_info_t *port; + snd_seq_port_subscribe_t *subs; + const snd_seq_addr_t *dest = snd_seq_query_subscribe_get_root(query); + const snd_seq_addr_t *sender = snd_seq_query_subscribe_get_addr(query); + snd_seq_port_info_alloca(&port); + if (snd_seq_get_any_port_info(seq, sender->client, sender->port, port) < 0) + continue; + if (!(snd_seq_port_info_get_capability(port) & SND_SEQ_PORT_CAP_SUBS_READ)) + continue; + if (snd_seq_port_info_get_capability(port) & SND_SEQ_PORT_CAP_NO_EXPORT) + continue; + snd_seq_port_subscribe_alloca(&subs); + snd_seq_port_subscribe_set_queue(subs, snd_seq_query_subscribe_get_queue(query)); + snd_seq_port_subscribe_set_sender(subs, sender); + snd_seq_port_subscribe_set_dest(subs, dest); + snd_seq_unsubscribe_port(seq, subs); + } +} + +static void remove_all_connections(snd_seq_t *seq) +{ + do_search_port(seq, 0, remove_connection); +} + + +/* + * main.. + */ + +enum { + SUBSCRIBE, UNSUBSCRIBE, LIST, REMOVE_ALL +}; + +static const struct option long_option[] = { + {"disconnect", 0, NULL, 'd'}, + {"input", 0, NULL, 'i'}, + {"output", 0, NULL, 'o'}, + {"real", 1, NULL, 'r'}, + {"tick", 1, NULL, 't'}, + {"exclusive", 0, NULL, 'e'}, + {"list", 0, NULL, 'l'}, + {"removeall", 0, NULL, 'x'}, + {NULL, 0, NULL, 0}, +}; + +int main(int argc, char **argv) +{ + int c; + snd_seq_t *seq; + int queue = 0, convert_time = 0, convert_real = 0, exclusive = 0; + int command = SUBSCRIBE; + int list_perm = 0; + int client; + int list_subs = 0; + snd_seq_port_subscribe_t *subs; + snd_seq_addr_t sender, dest; + +#ifdef ENABLE_NLS + setlocale(LC_ALL, ""); + textdomain(PACKAGE); +#endif + + while ((c = getopt_long(argc, argv, "dior:t:elx", long_option, NULL)) != -1) { + switch (c) { + case 'd': + command = UNSUBSCRIBE; + break; + case 'i': + command = LIST; + list_perm |= LIST_INPUT; + break; + case 'o': + command = LIST; + list_perm |= LIST_OUTPUT; + break; + case 'e': + exclusive = 1; + break; + case 'r': + queue = atoi(optarg); + convert_time = 1; + convert_real = 1; + break; + case 't': + queue = atoi(optarg); + convert_time = 1; + convert_real = 0; + break; + case 'l': + list_subs = 1; + break; + case 'x': + command = REMOVE_ALL; + break; + default: + usage(); + exit(1); + } + } + + if (snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) { + fprintf(stderr, _("can't open sequencer\n")); + return 1; + } + + snd_lib_error_set_handler(error_handler); + + switch (command) { + case LIST: + do_search_port(seq, list_perm, + list_subs ? print_port_and_subs : print_port); + snd_seq_close(seq); + return 0; + case REMOVE_ALL: + remove_all_connections(seq); + snd_seq_close(seq); + return 0; + } + + /* connection or disconnection */ + + if (optind + 2 > argc) { + snd_seq_close(seq); + usage(); + exit(1); + } + + if ((client = snd_seq_client_id(seq)) < 0) { + snd_seq_close(seq); + fprintf(stderr, _("can't get client id\n")); + return 1; + } + + /* set client info */ + if (snd_seq_set_client_name(seq, "ALSA Connector") < 0) { + snd_seq_close(seq); + fprintf(stderr, _("can't set client info\n")); + return 1; + } + + /* set subscription */ + if (snd_seq_parse_address(seq, &sender, argv[optind]) < 0) { + snd_seq_close(seq); + fprintf(stderr, _("invalid sender address %s\n"), argv[optind]); + return 1; + } + if (snd_seq_parse_address(seq, &dest, argv[optind + 1]) < 0) { + snd_seq_close(seq); + fprintf(stderr, _("invalid destination address %s\n"), argv[optind + 1]); + return 1; + } + snd_seq_port_subscribe_alloca(&subs); + snd_seq_port_subscribe_set_sender(subs, &sender); + snd_seq_port_subscribe_set_dest(subs, &dest); + snd_seq_port_subscribe_set_queue(subs, queue); + snd_seq_port_subscribe_set_exclusive(subs, exclusive); + snd_seq_port_subscribe_set_time_update(subs, convert_time); + snd_seq_port_subscribe_set_time_real(subs, convert_real); + + if (command == UNSUBSCRIBE) { + if (snd_seq_get_port_subscription(seq, subs) < 0) { + snd_seq_close(seq); + fprintf(stderr, _("No subscription is found\n")); + return 1; + } + if (snd_seq_unsubscribe_port(seq, subs) < 0) { + snd_seq_close(seq); + fprintf(stderr, _("Disconnection failed (%s)\n"), snd_strerror(errno)); + return 1; + } + } else { + if (snd_seq_get_port_subscription(seq, subs) == 0) { + snd_seq_close(seq); + fprintf(stderr, _("Connection is already subscribed\n")); + return 1; + } + if (snd_seq_subscribe_port(seq, subs) < 0) { + snd_seq_close(seq); + fprintf(stderr, _("Connection failed (%s)\n"), snd_strerror(errno)); + return 1; + } + } + + snd_seq_close(seq); + + return 0; +} |