]> git.alsa-project.org Git - alsa-utils.git/blob - seq/aconnect/aconnect.c
edd028f0391dfe1982dca13495b26e8a23440744
[alsa-utils.git] / seq / aconnect / aconnect.c
1 /*
2  * connect / disconnect two subscriber ports
3  *   ver.0.1.3
4  *
5  * Copyright (C) 1999 Takashi Iwai
6  * 
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  * 
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  */
17
18 #include "aconfig.h"
19 #include <stdio.h>
20 #include <ctype.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <getopt.h>
26 #include <stdarg.h>
27 #include <locale.h>
28 #include <sys/ioctl.h>
29 #include <alsa/asoundlib.h>
30 #include "gettext.h"
31
32 static int show_all;
33
34 static void error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...)
35 {
36         va_list arg;
37
38         if (err == ENOENT)      /* Ignore those misleading "warnings" */
39                 return;
40         va_start(arg, fmt);
41         fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function);
42         vfprintf(stderr, fmt, arg);
43         if (err)
44                 fprintf(stderr, ": %s", snd_strerror(err));
45         putc('\n', stderr);
46         va_end(arg);
47 }
48
49 static void usage(void)
50 {
51         printf(_("aconnect - ALSA sequencer connection manager\n"));
52         printf(_("Copyright (C) 1999-2000 Takashi Iwai\n"));
53         printf(_("Usage:\n"));
54         printf(_(" * Connection/disconnection between two ports\n"));
55         printf(_("   aconnect [-options] sender receiver\n"));
56         printf(_("     sender, receiver = client:port pair\n"));
57         printf(_("     -d,--disconnect     disconnect\n"));
58         printf(_("     -e,--exclusive      exclusive connection\n"));
59         printf(_("     -r,--real #         convert real-time-stamp on queue\n"));
60         printf(_("     -t,--tick #         convert tick-time-stamp on queue\n"));
61         printf(_(" * List connected ports (no subscription action)\n"));
62         printf(_("   aconnect -i|-o [-options]\n"));
63         printf(_("     -i,--input          list input (readable) ports\n"));
64         printf(_("     -o,--output         list output (writable) ports\n"));
65         printf(_("     -a,--all            show inactive ports, too\n"));
66         printf(_("     -l,--list           list current connections of each port\n"));
67         printf(_(" * Remove all exported connections\n"));
68         printf(_("     -x, --removeall\n"));
69 }
70
71 /*
72  * check permission (capability) of specified port
73  */
74
75 #define LIST_INPUT      1
76 #define LIST_OUTPUT     2
77
78 #define perm_ok(cap,bits) (((cap) & (bits)) == (bits))
79
80 static int check_direction(snd_seq_port_info_t *pinfo, int bit)
81 {
82         int dir = snd_seq_port_info_get_direction(pinfo);
83         return !dir || (dir & bit);
84 }
85
86 static int check_permission(snd_seq_port_info_t *pinfo, int perm)
87 {
88         int cap = snd_seq_port_info_get_capability(pinfo);
89
90         if (cap & SND_SEQ_PORT_CAP_NO_EXPORT)
91                 return 0;
92
93         if (!perm)
94                 return 1;
95         if (perm & LIST_INPUT) {
96                 if (perm_ok(cap, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ) &&
97                     check_direction(pinfo, SND_SEQ_PORT_DIR_INPUT))
98                         return 1;
99         }
100         if (perm & LIST_OUTPUT) {
101                 if (perm_ok(cap, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE) &&
102                     check_direction(pinfo, SND_SEQ_PORT_DIR_OUTPUT))
103                         return 1;
104         }
105         return 0;
106 }
107
108 /*
109  * list subscribers of specified type
110  */
111 static void list_each_subs(snd_seq_t *seq, snd_seq_query_subscribe_t *subs, int type, const char *msg)
112 {
113         int count = 0;
114         snd_seq_query_subscribe_set_type(subs, type);
115         snd_seq_query_subscribe_set_index(subs, 0);
116         while (snd_seq_query_port_subscribers(seq, subs) >= 0) {
117                 const snd_seq_addr_t *addr;
118                 if (count++ == 0)
119                         printf("\t%s: ", msg);
120                 else
121                         printf(", ");
122                 addr = snd_seq_query_subscribe_get_addr(subs);
123                 printf("%d:%d", addr->client, addr->port);
124                 if (snd_seq_query_subscribe_get_exclusive(subs))
125                         printf("[ex]");
126                 if (snd_seq_query_subscribe_get_time_update(subs))
127                         printf("[%s:%d]",
128                                (snd_seq_query_subscribe_get_time_real(subs) ? "real" : "tick"),
129                                snd_seq_query_subscribe_get_queue(subs));
130                 snd_seq_query_subscribe_set_index(subs, snd_seq_query_subscribe_get_index(subs) + 1);
131         }
132         if (count > 0)
133                 printf("\n");
134 }
135
136 /*
137  * list subscribers
138  */
139 static void list_subscribers(snd_seq_t *seq, const snd_seq_addr_t *addr)
140 {
141         snd_seq_query_subscribe_t *subs;
142         snd_seq_query_subscribe_alloca(&subs);
143         snd_seq_query_subscribe_set_root(subs, addr);
144         list_each_subs(seq, subs, SND_SEQ_QUERY_SUBS_READ, _("Connecting To"));
145         list_each_subs(seq, subs, SND_SEQ_QUERY_SUBS_WRITE, _("Connected From"));
146 }
147
148 /*
149  * search all ports
150  */
151 typedef void (*action_func_t)(snd_seq_t *seq, snd_seq_client_info_t *cinfo, snd_seq_port_info_t *pinfo, int count);
152
153 static void do_search_port(snd_seq_t *seq, int perm, action_func_t do_action)
154 {
155         snd_seq_client_info_t *cinfo;
156         snd_seq_port_info_t *pinfo;
157         int count;
158
159         snd_seq_client_info_alloca(&cinfo);
160         snd_seq_port_info_alloca(&pinfo);
161         snd_seq_client_info_set_client(cinfo, -1);
162         while (snd_seq_query_next_client(seq, cinfo) >= 0) {
163                 /* reset query info */
164                 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
165                 snd_seq_port_info_set_port(pinfo, -1);
166                 if (show_all)
167                         snd_seq_port_info_set_capability(pinfo, SND_SEQ_PORT_CAP_INACTIVE);
168                 count = 0;
169                 while (snd_seq_query_next_port(seq, pinfo) >= 0) {
170                         if (check_permission(pinfo, perm)) {
171                                 do_action(seq, cinfo, pinfo, count);
172                                 count++;
173                         }
174                         if (show_all)
175                                 snd_seq_port_info_set_capability(pinfo, SND_SEQ_PORT_CAP_INACTIVE);
176                 }
177         }
178 }
179
180
181 static void print_port(snd_seq_t *seq ATTRIBUTE_UNUSED,
182                        snd_seq_client_info_t *cinfo,
183                        snd_seq_port_info_t *pinfo, int count)
184 {
185         if (! count) {
186                 int card = -1, pid = -1;
187
188                 printf(_("client %d: '%s' [type=%s"),
189                        snd_seq_client_info_get_client(cinfo),
190                        snd_seq_client_info_get_name(cinfo),
191                        (snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ?
192                         _("user") : _("kernel")));
193                 switch (snd_seq_client_info_get_midi_version(cinfo)) {
194                 case SND_SEQ_CLIENT_UMP_MIDI_1_0:
195                         printf(",UMP-MIDI1");
196                         break;
197                 case SND_SEQ_CLIENT_UMP_MIDI_2_0:
198                         printf(",UMP-MIDI2");
199                         break;
200                 }
201                 card = snd_seq_client_info_get_card(cinfo);
202                 if (card != -1)
203                         printf(",card=%d", card);
204
205                 pid = snd_seq_client_info_get_pid(cinfo);
206                 if (pid != -1)
207                         printf(",pid=%d", pid);
208                 printf("]\n");
209         }
210         printf("  %3d '%-16s'",
211                snd_seq_port_info_get_port(pinfo),
212                snd_seq_port_info_get_name(pinfo));
213         if (snd_seq_port_info_get_capability(pinfo) & SND_SEQ_PORT_CAP_INACTIVE)
214                 printf(" [INACTIVE]");
215         printf("\n");
216 }
217
218 static void print_port_and_subs(snd_seq_t *seq, snd_seq_client_info_t *cinfo,
219                                 snd_seq_port_info_t *pinfo, int count)
220 {
221         print_port(seq, cinfo, pinfo, count);
222         list_subscribers(seq, snd_seq_port_info_get_addr(pinfo));
223 }
224
225
226 /*
227  * remove all (exported) connections
228  */
229 static void remove_connection(snd_seq_t *seq,
230                               snd_seq_client_info_t *info ATTRIBUTE_UNUSED,
231                               snd_seq_port_info_t *pinfo,
232                               int count ATTRIBUTE_UNUSED)
233 {
234         snd_seq_query_subscribe_t *query;
235         snd_seq_port_info_t *port;
236         snd_seq_port_subscribe_t *subs;
237
238         snd_seq_query_subscribe_alloca(&query);
239         snd_seq_query_subscribe_set_root(query, snd_seq_port_info_get_addr(pinfo));
240         snd_seq_query_subscribe_set_type(query, SND_SEQ_QUERY_SUBS_READ);
241         snd_seq_query_subscribe_set_index(query, 0);
242
243         snd_seq_port_info_alloca(&port);
244         snd_seq_port_subscribe_alloca(&subs);
245
246         while (snd_seq_query_port_subscribers(seq, query) >= 0) {
247                 const snd_seq_addr_t *sender = snd_seq_query_subscribe_get_root(query);
248                 const snd_seq_addr_t *dest = snd_seq_query_subscribe_get_addr(query);
249
250                 if (snd_seq_get_any_port_info(seq, dest->client, dest->port, port) < 0 ||
251                     !(snd_seq_port_info_get_capability(port) & SND_SEQ_PORT_CAP_SUBS_WRITE) ||
252                     (snd_seq_port_info_get_capability(port) & SND_SEQ_PORT_CAP_NO_EXPORT)) {
253                         snd_seq_query_subscribe_set_index(query, snd_seq_query_subscribe_get_index(query) + 1);
254                         continue;
255                 }
256                 snd_seq_port_subscribe_set_queue(subs, snd_seq_query_subscribe_get_queue(query));
257                 snd_seq_port_subscribe_set_sender(subs, sender);
258                 snd_seq_port_subscribe_set_dest(subs, dest);
259                 if (snd_seq_unsubscribe_port(seq, subs) < 0) {
260                         snd_seq_query_subscribe_set_index(query, snd_seq_query_subscribe_get_index(query) + 1);
261                 }
262         }
263 }
264
265 static void remove_all_connections(snd_seq_t *seq)
266 {
267         do_search_port(seq, 0, remove_connection);
268 }
269
270
271 /*
272  * main..
273  */
274
275 enum {
276         SUBSCRIBE, UNSUBSCRIBE, LIST, REMOVE_ALL
277 };
278
279 #define ACONNECT_OPTS "dior:t:elxa"
280
281 static const struct option long_option[] = {
282         {"disconnect", 0, NULL, 'd'},
283         {"input", 0, NULL, 'i'},
284         {"output", 0, NULL, 'o'},
285         {"real", 1, NULL, 'r'},
286         {"tick", 1, NULL, 't'},
287         {"exclusive", 0, NULL, 'e'},
288         {"list", 0, NULL, 'l'},
289         {"removeall", 0, NULL, 'x'},
290         {"all", 0, NULL, 'a'},
291         {NULL, 0, NULL, 0},
292 };
293
294 int main(int argc, char **argv)
295 {
296         int c;
297         snd_seq_t *seq;
298         int queue = 0, convert_time = 0, convert_real = 0, exclusive = 0;
299         int command = SUBSCRIBE;
300         int list_perm = 0;
301         int client;
302         int list_subs = 0;
303         snd_seq_port_subscribe_t *subs;
304         snd_seq_addr_t sender, dest;
305
306 #ifdef ENABLE_NLS
307         setlocale(LC_ALL, "");
308         textdomain(PACKAGE);
309 #endif
310
311         while ((c = getopt_long(argc, argv, ACONNECT_OPTS, long_option, NULL)) != -1) {
312                 switch (c) {
313                 case 'd':
314                         command = UNSUBSCRIBE;
315                         break;
316                 case 'i':
317                         command = LIST;
318                         list_perm |= LIST_INPUT;
319                         break;
320                 case 'o':
321                         command = LIST;
322                         list_perm |= LIST_OUTPUT;
323                         break;
324                 case 'e':
325                         exclusive = 1;
326                         break;
327                 case 'r':
328                         queue = atoi(optarg);
329                         convert_time = 1;
330                         convert_real = 1;
331                         break;
332                 case 't':
333                         queue = atoi(optarg);
334                         convert_time = 1;
335                         convert_real = 0;
336                         break;
337                 case 'l':
338                         command = LIST;
339                         list_subs = 1;
340                         break;
341                 case 'x':
342                         command = REMOVE_ALL;
343                         break;
344                 case 'a':
345                         command = LIST;
346                         show_all = 1;
347                         break;
348                 default:
349                         usage();
350                         exit(1);
351                 }
352         }
353
354         if (snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
355                 fprintf(stderr, _("can't open sequencer\n"));
356                 return 1;
357         }
358         
359         snd_lib_error_set_handler(error_handler);
360
361         switch (command) {
362         case LIST:
363                 do_search_port(seq, list_perm,
364                                list_subs ? print_port_and_subs : print_port);
365                 snd_seq_close(seq);
366                 return 0;
367         case REMOVE_ALL:
368                 remove_all_connections(seq);
369                 snd_seq_close(seq);
370                 return 0;
371         }
372
373         /* connection or disconnection */
374
375         if (optind + 2 > argc) {
376                 snd_seq_close(seq);
377                 usage();
378                 exit(1);
379         }
380
381         if ((client = snd_seq_client_id(seq)) < 0) {
382                 snd_seq_close(seq);
383                 fprintf(stderr, _("can't get client id\n"));
384                 return 1;
385         }
386
387         /* set client info */
388         if (snd_seq_set_client_name(seq, "ALSA Connector") < 0) {
389                 snd_seq_close(seq);
390                 fprintf(stderr, _("can't set client info\n"));
391                 return 1;
392         }
393
394         /* set subscription */
395         if (snd_seq_parse_address(seq, &sender, argv[optind]) < 0) {
396                 snd_seq_close(seq);
397                 fprintf(stderr, _("invalid sender address %s\n"), argv[optind]);
398                 return 1;
399         }
400         if (snd_seq_parse_address(seq, &dest, argv[optind + 1]) < 0) {
401                 snd_seq_close(seq);
402                 fprintf(stderr, _("invalid destination address %s\n"), argv[optind + 1]);
403                 return 1;
404         }
405         snd_seq_port_subscribe_alloca(&subs);
406         snd_seq_port_subscribe_set_sender(subs, &sender);
407         snd_seq_port_subscribe_set_dest(subs, &dest);
408         snd_seq_port_subscribe_set_queue(subs, queue);
409         snd_seq_port_subscribe_set_exclusive(subs, exclusive);
410         snd_seq_port_subscribe_set_time_update(subs, convert_time);
411         snd_seq_port_subscribe_set_time_real(subs, convert_real);
412
413         if (command == UNSUBSCRIBE) {
414                 if (snd_seq_get_port_subscription(seq, subs) < 0) {
415                         snd_seq_close(seq);
416                         fprintf(stderr, _("No subscription is found\n"));
417                         return 1;
418                 }
419                 if (snd_seq_unsubscribe_port(seq, subs) < 0) {
420                         snd_seq_close(seq);
421                         fprintf(stderr, _("Disconnection failed (%s)\n"), snd_strerror(errno));
422                         return 1;
423                 }
424         } else {
425                 if (snd_seq_get_port_subscription(seq, subs) == 0) {
426                         snd_seq_close(seq);
427                         fprintf(stderr, _("Connection is already subscribed\n"));
428                         return 1;
429                 }
430                 if (snd_seq_subscribe_port(seq, subs) < 0) {
431                         snd_seq_close(seq);
432                         fprintf(stderr, _("Connection failed (%s)\n"), snd_strerror(errno));
433                         return 1;
434                 }
435         }
436
437         snd_seq_close(seq);
438
439         return 0;
440 }