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