1 /*
2 * $Id: umix.c,v 1.9 2003/08/23 12:54:09 sakari Exp $
3 *
4 * The main function and command-line UI for Umix.
5 * Main parses command-line options, initializes the mixer
6 * and then calls the UI. The command-line UI does not
7 * support multiple devices in one session.
8 *
9 * Copyright (C) 2002 Sakari Lehtonen <sakari@ionstream.fi>
10 *
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the Free
13 * Software Foundation; either version 2 of the License, or (at your option)
14 * any later version.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 * for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 *
25 */
26
27 #include "umix_common.h"
28 #include "ui.h"
29 #ifdef HAVE_GETOPT_LONG
30 # include <getopt.h>
31 #else
32 # include <unistd.h>
33 #endif
34
35 /* global options */
36 umix_cfg globalopts =
37 {
38 DEF_DEVICE_PATH,
39 DEF_CONFIG_PATH,
40 DEF_DRIVER
41 };
42
43 /* function prototypes */
44 static void check_homeconf(void);
45 static int cmdline(int argc, char *argv[], int optind);
46 static void cmdline_loadsettings(void);
47 static void parse_cmdlineopts(int argc, char *argv[]);
48 static void parse_cmdlinevols(int argc, char *argv[], int optind);
49 static void query(void);
50 static void printlevels(void);
51 static void usage(int status);
52 static void version(void);
53 static void showglobals(void);
54 static int setall(const char *optarg);
55
56 #ifdef UMIX_DEBUG
57 static int test(int mode);
58 #endif
59
60 /* the name umix is called with */
61 const char *program_name;
62
63 /* command line options */
64 typedef struct umix_opts umix_opts;
65 struct umix_opts
66 {
67 int query;
68 int load;
69 int save;
70 int force_interactive;
71 int interactive;
72 } static opts = { 0, 0, 0, 0, 0 };
73
74 int main(int argc, char *argv[])
75 {
76 #ifdef ENABLE_NLS
77 /* internationalization */
78 setlocale(LC_ALL, "");
79 bindtextdomain(PACKAGE, LOCALEDIR);
80 textdomain(PACKAGE);
81 #endif
82 program_name = argv[0];
83
84 /* check if .umixrc exists in the users home dir, and set to the
85 * global config path if exists */
86 check_homeconf();
87 /* try to load global settings if not loading or saving to stdin */
88 if (globalopts.cfgpath[0] != '-' && globalopts.cfgpath[1] != '\0')
89 settings_load(globalopts.cfgpath, UMIX_SET_GLOBAL);
90
91 /* parse command-line options */
92 parse_cmdlineopts(argc, argv);
93
94 /* check if umix was called with only one argument eg `umix foo` */
95 if (argc == 2 && optind == 1)
96 {
97 err_msg(_("Nothing to do!"));
98 usage(EXIT_FAILURE);
99 }
100 /* if any command line options given, parse them */
101 if (argc >= 2)
102 {
103 if (cmdline(argc, argv, optind) == -1)
104 return -1;
105 }
106 /* if no command line options were given, or -i was given, then start
107 * interactive UI, mixers are initialized in the UI */
108 if (argc < 2 || opts.interactive == 1)
109 {
110 /* no sense in saving or loading settings
111 * from stdin/stdout in interactive UI's, replace
112 * config path with the default one */
113 if (globalopts.cfgpath[0]=='-' &&
114 globalopts.cfgpath[1]=='\0')
115 {
116 strncpy(globalopts.cfgpath, DEF_CONFIG_PATH,
117 sizeof(globalopts.cfgpath));
118 }
119 if (ui_init("ncurses", argc, argv) == -1)
120 {
121 err_msg(_("Ncurses UI initialization failed"
122 ", try `%s -q'"), program_name);
123 }
124 }
125 /* finally, close all mixers */
126 close_mixers();
127
128 return 0;
129 }
130
131 static void check_homeconf(void)
132 {
133 char *str;
134 struct stat sbuf;
135 char buf[BUFLEN];
136
137 /* check if $HOME/.umixrc exists, and use as the default
138 * config if found */
139 if ((str = getenv("HOME")) != NULL)
140 {
141 snprintf(buf, sizeof(buf), "%s/.umixrc", str);
ncurses_get_ui_func(void)142 if (stat(buf, &sbuf) == 0)
143 {
144 strncpy(globalopts.cfgpath, buf,
145 sizeof(globalopts.cfgpath));
146 }
ui_ncurs_main(int argc,char * argv[])147 }
148 }
149
150 /* the commandline UI */
151 static int cmdline(int argc, char *argv[], int optind)
152 {
153 #ifdef UMIX_DEBUG
154 err_msg("cmdline: argc=%d optind=%d", argc, optind);
155 #endif
156 if (init_mixers() == -1)
157 {
158 err_msg(_("Failed initializing mixer devices"));
159 return -1;
160 }
161
162 /* process all command-line options conserning channels */
163 parse_cmdlinevols(argc, argv, optind);
164 /* if any channel options are given, do not start interactive ui */
165 if (optind != argc)
166 opts.interactive = 0;
167
168 /* query settings */
169 if (opts.query == 1)
170 {
171 query();
172 /* no point in both querying levels and starting
173 * the interactive ui */
174 opts.interactive = 0;
175 }
176
177 /* load settings */
178 if (opts.load == 1)
179 cmdline_loadsettings();
180
181 /* save settings */
182 if (opts.save == 1)
183 {
184 if (settings_save(globalopts.cfgpath) == -1)
185 err_str(_("Failed saving settings to \'%s\'"),
186 globalopts.cfgpath);
187 else
188 printf(_("Saved mixer settings to \'%s\'\n"),
189 globalopts.cfgpath);
190 opts.interactive = 0;
191 }
192 /* -i forces to start the interactive ui after all other
193 * command line options are parsed */
194 if (opts.force_interactive == 1)
195 opts.interactive = 1;
196
197 return 0;
198 }
199
200 static void cmdline_loadsettings(void)
201 {
202 int retval;
203
204 retval = settings_load(globalopts.cfgpath, UMIX_SET_MIXER);
205 if (retval == 0)
206 {
207 printf(_("Loaded mixer settings from \'%s\'\n"),
208 globalopts.cfgpath);
209 }
210 else
211 {
212 err_msg("Loading settings failed");
213 switch (retval)
214 {
215 case UMIX_ESETINVALID:
216 err_msg("%s: %s",
217 globalopts.cfgpath,
218 UMIX_UI_EINVALID_STR);
219 break;
220 case UMIX_ESETNOTFOUND:
221 err_msg("%s: %s",
222 globalopts.cfgpath,
223 UMIX_UI_ENOTFOUND_STR);
224 break;
225 case UMIX_ESETBINARY:
226 err_msg("%s: %s",
227 globalopts.cfgpath,
228 UMIX_UI_ESETBINARY_STR);
229 break;
230 default:
231 err_str("%s", globalopts.cfgpath);
232 break;
233 }
234 }
235 opts.interactive = 0;
236 }
237
238 static void parse_cmdlineopts(int argc, char *argv[])
239 {
240 int optch;
241 const char *optstr = "d:r:f:ghlLqsSvi";
242
243 #ifdef HAVE_GETOPT_LONG
244 const struct option longopts[] =
245 {
246 { "device", 1, 0, 'd' },
247 { "driver", 1, 0, 'r' },
248 { "file", 1, 0, 'f' },
249 { "globals", 0, 0, 'g' },
250 { "help", 0, 0, 'h' },
251 { "load", 0, 0, 'l' },
252 { "query", 0, 0, 'q' },
253 { "save", 0, 0, 's' },
254 { "version", 0, 0, 'v' },
255 { "interactive",0, 0, 'i' },
256 { NULL, 0, 0, 0 }
257 };
258 while ((optch = getopt_long(argc, argv, optstr, longopts, NULL)) != -1)
259 {
260 #else
261 while ((optch = getopt(argc, argv, optstr)) != -1)
262 {
263 #endif /* HAVE_GETOPT_LONG */
264 switch(optch)
265 {
266 case 'd': strncpy(globalopts.devpath, optarg,
267 sizeof(globalopts.devpath));
268 opts.interactive = 1;
269 break;
270 case 'f': strncpy(globalopts.cfgpath, optarg,
271 sizeof(globalopts.cfgpath));
272 opts.interactive = 1;
273 break;
274 /* showglobals() is called here because we want to be
275 * able to call it even if something fails */
276 case 'g': showglobals();
277 opts.interactive = 0;
278 break;
279 case 'h': usage(EXIT_SUCCESS);
280 break;
281 case 'i': opts.force_interactive = 1;
282 break;
283 case 'L': /* compatibility */
284 case 'l': opts.load = 1;
285 break;
286 case 'q': opts.query = 1;
287 break;
288 case 'r': strncpy(globalopts.driver, optarg,
289 sizeof(globalopts.driver));
290 opts.interactive = 1;
291 break;
292 case 'S': /* compatibility */
293 case 's': opts.save = 1;
294 break;
295 case 'v': version();
296 exit(EXIT_SUCCESS);
297 break;
298 default:
299 usage(EXIT_FAILURE);
300 break;
301 }
302 };
303 }
304
305 static void parse_cmdlinevols(int argc, char *argv[], int optind)
306 {
307 int chnum;
308 int retval;
309 int i;
310
311 for (i=optind; i<argc; i++)
312 {
313 /* the special channel name 'all' sets all channels */
314 if ((strcasecmp(argv[i], "all")) == 0)
315 {
316 i++;
317 setall(argv[i]);
318 }
319 /* get the right channel number for the string argv[i] eg.
320 * "pcm" would return 1 with my system */
321 else if ((chnum = mixer_opt_to_chan_num(argv[i])) != -1)
322 {
323 i++;
324 chan_set_curr(chnum);
325 /* check if no value is given after the channel
calc_label_widths(void)326 * name */
327 if (argv[i] == NULL)
328 {
329 err_msg(_("Value missing for channel %s"),
330 chan_get_name());
331 break;
332 }
333 /* parse the actual volume settings for current chan */
334 retval = parsechanopt(argv[i]);
335 if (retval != 0)
336 err_msg(UMIX_EOPTGENERAL_STRFMT,
337 chan_get_name(), argv[i]);
338 switch (retval)
339 {
340 case UMIX_EOPTLIMIT:
341 err_msg(UMIX_EOPTLIMIT_STR);
342 break;
343 case UMIX_EOPTINVALID:
344 err_msg(UMIX_EOPTINVALID_STR);
345 break;
346 case UMIX_EOPTNOTREC:
347 err_msg(UMIX_EOPTNOTREC_STR);
348 break;
349 default:
350 break;
351 }
352 }
353 else
354 err_msg(_("Invalid channel name: \'%s\'"), argv[i]);
355 }
window_size_changed(void)356 }
357
358 static void query(void)
359 {
360 int i;
361
362 for (i=0; i<mixer_get_num_mix(); i++)
363 {
364 mixer_set_curr(i);
365 printlevels();
366 }
367 }
368
369 /* parse optarg for all channels */
370 static int setall(const char *optarg)
update_stat(void)371 {
372 int i;
373
374 for (i=0; i<mixer_get_num_chan(); i++)
375 {
376 chan_set_curr(i);
377 if (parsechanopt(optarg) != 0)
378 return -1;
379 }
380
381 return 0;
382 }
383
384 /* prints the levels and status for all channels on current mixer */
385 static void printlevels(void)
cycle_mixer(void)386 {
387 int i;
388
389 printf("%s::%s::%s\n", mixer_get_name(),
390 mixer_get_path(),
391 mixer_get_driver());
392 for (i=0; i<mixer_get_num_chan(); i++)
393 {
394 chan_set_curr(i);
395 printf("%-10s %3d:%3d ", chan_get_name(),
396 chan_get_lr(CHAN_LEFT), chan_get_lr(CHAN_RIGHT));
397
398 if (chan_is_stereo())
399 printf(_("Stereo"));
400 else
401 printf(_("Mono "));
402 putchar(' ');
403
404 if (chan_is_recsrc())
405 printf(_("Record Source"));
406 else if (chan_is_record())
407 printf(_("Recordable"));
408 putchar('\n');
409 }
410 }
411
412 static void showglobals(void)
413 {
414 printf(_("Current global settings:\n"
415 "Config path\t %s\n"
416 "Device path\t %s\n"
417 "Mixer driver\t %s\n"),
418 globalopts.cfgpath, globalopts.devpath,
419 globalopts.driver);
420 }
421
422 static void usage(int status)
423 {
424 if (status != 0)
425 fprintf(stderr, _("try `%s -h' for more information\n"),
426 program_name);
427 else
428 {
429 printf(_("Usage: %s [channame volume|left:right][R|P][+|-] [options]\n"
430 "\nYou can get all available channel names with the '-q' option"
431 "\n\nAdditional options:\n"
432 "-q, --query\t\tPrint channel settings and available\n"
433 "\t\t\tchannel names\n"
434 "-s, --save\t\tSave mixer settings to a file\n"
draw_pos(int menu,int status)435 "-l, --load\t\tLoad mixer settings from a file\n"
436 "-f, --file=PATH\t\tSpecify file used to read and save settings\n"
437 "\t\t\t(defaults to %s)\n"
438 "-d, --dev=PATH\t\tSpecify device used for mixing\n"
439 "\t\t\t(defaults to %s)\n"
440 "-r, --driver=DRIVER\tSpecify the mixer driver used "
441 "(defaults to %s)\n"
442 "-i, --interactive"
443 "\tStart interactive user interface after processing\n"
444 "\t\t\tcommand-line options (if available)\n"
445 "-g, --globals\t\tPrint current global options\n"
446 "-h, --help\t\tDisplay this help and exit\n"
447 "-v, --version\t\tDisplay version information and exit\n"),
448 program_name, DEF_CONFIG_PATH, DEF_DEVICE_PATH, DEF_DRIVER);
449 }
450 exit(status);
451 }
452
453 static void version(void)
454 {
455 printf(_("%s\n"
456 "Copyright (C) 2002 Sakari Lehtonen <sakari@ionstream.fi>\n"
457 "\nThis is free software; see the source for copying conditions. "
458 "There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A"
459 " PARTICULAR PURPOSE.\n"), UMIX_VERSION_STR);
460 }
461
462 #ifdef UMIX_DEBUG
463 /* tests most of the functions available, and shows how to do things */
464 static int test(int mode)
465 {
466 int i;
467 int j;
468 volume orig_vol;
469
470 if (init_mixers() == -1)
471 {
472 err_msg(_("Failed initializing mixer devices"));
473 return -1;
474 }
475
476 /* update check test */
477 if (mode == 1)
478 {
draw_status(void)479 while (1)
480 {
481 usleep(250000);
482 if (mixer_get_status() == 1)
483 {
484 err_msg("mixer has been updated!");
485 printlevels();
486 chan_set_curr(0);
487 }
488 }
489
490 mixer_close();
491 return 0;
492 }
493
494 printf("Total number of mixers = %d\n", mixer_get_num_mix());
495 for (j=0; j<mixer_get_num_mix(); j++)
496 {
497 printf("Mixer #%d information\n", mixer_get_curr());
498 printf("numchan = %d\n", mixer_get_num_chan());
499 printf("name = %s\n", mixer_get_name());
draw_slider(void)500 printf("path = %s\n", mixer_get_path());
501 printf("driver = %s\n", mixer_get_driver());
502
503 printf("num name label lt rt s r a m\n");
504 for (i=0; i<mixer_get_num_chan(); i++)
505 {
506 chan_set_curr(i);
507 printf("%3d %-10s %-10s ", chan_get_curr(),
508 chan_get_name(), chan_get_label());
509 printf("%3d %3d %d %d %d %d\n",
510 chan_get_lr(CHAN_LEFT), chan_get_lr(CHAN_RIGHT),
511 chan_is_stereo(), chan_is_recsrc(),
512 chan_is_record(), chan_is_mute());
513 }
514
515 chan_set_curr(1);
516 orig_vol = chan_get_vb();
517
518 printf("Setting the channel %s to 100\n", chan_get_name());
519 chan_set_lr(100, 100);
520 usleep(200000);
521
522 printf("Setting balance to right\n");
523 chan_set_lr(0, 100);
524 usleep(200000);
525
526 printf("Setting balance to left\n");
527 chan_set_lr(100, 0);
528 usleep(200000);
529
530 printf("Setting balance back to center\n");
531 chan_set_lr(100, 100);
532
533 printf("Decreasing volume with 2\n");
534 chan_inc_vol(-2);
draw_balance(void)535 err_msg("left=%d right=%d", chan_get_lr(CHAN_LEFT),
536 chan_get_lr(CHAN_RIGHT));
537 usleep(200000);
538
539 printf("Increasing volume with 2\n");
540 chan_inc_vol(2);
541 err_msg("left=%d right=%d", chan_get_lr(CHAN_LEFT),
542 chan_get_lr(CHAN_RIGHT));
543 usleep(200000);
544
545 printf("Restoring old values: vol=%3f bal=%3f\n",
546 orig_vol.volume, orig_vol.balance);
547 chan_set_vb(orig_vol);
548
549 for (i=mixer_get_num_chan()-1; i>=0; i--)
550 {
551 chan_set_curr(i);
552 if (chan_is_record())
553 {
554 printf("Setting channel #%d as recsrc\n", i);
555 chan_set_rec(CHAN_RECSRC);
556 if (chan_is_recsrc())
557 {
558 printf("Channel #%d (%s) is recsrc\n",
559 i, chan_get_name());
560 }
561 else
562 printf("Setting recsrc failed!\n");
563
564 break;
565 }
566 }
567 }
568
569 query();
570 mixer_close();
571
572 return 0;
573 }
574 #endif
draw_all(void)575