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