1 /*
2  *   xmmix - Motif(tm) Audio Mixer
3  *
4  *   Copyright (C) 1994-1996  Ti Kan
5  *   E-mail: ti@amb.org
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 as published by
9  *   the Free Software Foundation; either version 2 of the License, or
10  *   (at your option) any later version.
11  *
12  *   This program is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with this program; if not, write to the Free Software
19  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  */
22 #ifndef LINT
23 static char *_mixer_c_ident_ = "@(#)mixer.c	3.5 96/11/07";
24 #endif
25 
26 #include "appenv.h"
27 #include "patchlevel.h"
28 #include "widget.h"
29 #include "mixer.h"
30 
31 
32 #if defined(SOUND_MIXER_LOUD) && defined(SOUND_MIXER_MUTE) && \
33     defined(SOUND_MIXER_ENHANCE)
34 #if SOUND_MIXER_LOUD == SOUND_MIXER_MUTE
35 /* HACK for new sound drivers */
36 #undef SOUND_MIXER_LOUD
37 #undef SOUND_MIXER_MUTE
38 #undef SOUND_MIXER_ENHANCE
39 #endif
40 #endif
41 
42 /* Ioctl data direction flags */
43 #define IOC_DATA_NONE	0
44 #define IOC_DATA_IN	1
45 #define IOC_DATA_OUT	2
46 
47 
48 extern appdata_t	app_data;
49 extern widgets_t	widgets;
50 extern bool_t		exit_flag;
51 extern FILE		*errfp;
52 
53 
54 int			maxdevs;	/* Maximum number of devices */
55 
56 STATIC int		dev_fd = -1,	/* Mixer device file descriptor */
57 			fsmode;		/* File selection mode */
58 
59 STATIC char		drv_ver[128];	/* Sound driver version */
60 
61 STATIC ctlinfo_t	ctlinfo = {	/* Current state of all controls */
62 	0, 0, 0, 0, 0, 0, FALSE, FALSE,
63 	{
64 		{  0,  0, TRUE },	/* 0 */
65 		{ 50, 50, TRUE },	/* 1 */
66 		{ 50, 50, TRUE },	/* 2 */
67 		{  0,  0, TRUE },	/* 3 */
68 		{  0,  0, TRUE },	/* 4 */
69 		{  0,  0, TRUE },	/* 5 */
70 		{  0,  0, TRUE },	/* 6 */
71 		{  0,  0, TRUE },	/* 7 */
72 		{  0,  0, TRUE },	/* 8 */
73 		{  0,  0, TRUE },	/* 9 */
74 		{  0,  0, TRUE },	/* 10 */
75 		{  0,  0, TRUE },	/* 11 */
76 		{  0,  0, TRUE },	/* 12 */
77 		{  0,  0, TRUE },	/* 13 */
78 		{  0,  0, TRUE },	/* 14 */
79 		{  0,  0, TRUE },	/* 15 */
80 		{  0,  0, TRUE },	/* 16 */
81 		{  0,  0, TRUE },	/* 17 */
82 		{  0,  0, TRUE },	/* 18 */
83 		{  0,  0, TRUE },	/* 19 */
84 		{  0,  0, TRUE },	/* 20 */
85 		{  0,  0, TRUE },	/* 21 */
86 		{  0,  0, TRUE },	/* 22 */
87 		{  0,  0, TRUE },	/* 23 */
88 		{  0,  0, TRUE },	/* 24 */
89 		{  0,  0, TRUE },	/* 25 */
90 		{  0,  0, TRUE },	/* 26 */
91 		{  0,  0, TRUE },	/* 27 */
92 		{  0,  0, TRUE },	/* 28 */
93 		{  0,  0, TRUE },	/* 29 */
94 		{  0,  0, TRUE },	/* 30 */
95 		{  0,  0, TRUE }	/* 31 */
96 	}
97 };
98 
99 STATIC ctlinfo_t	ctlsav;		/* Saved state of all controls */
100 
101 
102 /***********************
103  *  internal routines  *
104  ***********************/
105 
106 
107 /*
108  * do_ioctl
109  *	Perform ioctl command.  If file is not yet open or if we
110  *	are in demo mode, just quietly return.  If an error
111  *	status is returned, print an appropriate error message
112  *	and exit.
113  *
114  * Args:
115  *	cmd - The ioctl command
116  *	arg - The ioctl argument
117  *	name - The ioctl command string
118  *	dir - Data direction
119  *		IOC_DATA_IN
120  *		IOC_DATA_OUT
121  *		IOC_DATA_NONE
122  *
123  * Return:
124  *	Nothing
125  */
126 STATIC void
do_ioctl(long cmd,int * arg,char * name,int dir)127 do_ioctl(long cmd, int *arg, char *name, int dir)
128 {
129 	int	ret;
130 	char	errmsg[STR_BUF_SZ];
131 
132 	if (dev_fd < 0 || app_data.demo)
133 		return;
134 
135 	sprintf(errmsg, "\n%s ioctl failed", name);
136 	if (app_data.debug) {
137 		fprintf(errfp, "%s: cmd=0x%x (%s) ", PROGNAME, cmd, name);
138 
139 		if (dir == IOC_DATA_OUT)
140 			fprintf(errfp, "*arg=0x%x", *arg);
141 	}
142 
143 	if ((ret = ioctl(dev_fd, cmd, arg)) < 0) {
144 		perror(errmsg);
145 		exit_flag = TRUE;
146 	}
147 
148 	if (app_data.debug) {
149 		if (ret == 0 && dir == IOC_DATA_IN)
150 			fprintf(errfp, "*arg=0x%x", *arg);
151 		fprintf(errfp, "\n");
152 	}
153 }
154 
155 
156 /*
157  * mx_queryhw
158  *	Query mixer settings and update current ctlinfo state.
159  *
160  * Args:
161  *	m - Pointer to the main widgets structure
162  *
163  * Return:
164  *	Nothing
165  */
166 STATIC void
mx_queryhw(widgets_t * m)167 mx_queryhw(widgets_t *m)
168 {
169 	int		i,
170 			level,
171 			curmask;
172 	char		iocstr[STR_BUF_SZ];
173 	static bool_t	first = TRUE;
174 
175 	if (app_data.demo) {
176 		if (first) {
177 			/* Save start-up settings */
178 			first = FALSE;
179 			ctlsav = ctlinfo;	/* Structure copy */
180 		}
181 		return;
182 	}
183 
184 	/* Read record source */
185 	do_ioctl(
186 		SOUND_MIXER_READ_RECSRC,
187 		&ctlinfo.recsrc,
188 		"SOUND_MIXER_READ_RECSRC",
189 		IOC_DATA_IN
190 	);
191 
192 	/* Read control settings */
193 	for (i = 0; i < maxdevs; i++) {
194 		curmask = (1 << i);
195 
196 		if (m->sl[i].supp) {
197 			sprintf(iocstr, "MIXER_READ[%d]:%s", i, m->sl[i].name);
198 			do_ioctl(MIXER_READ(i), &level, iocstr, IOC_DATA_IN);
199 
200 			ctlinfo.slinfo[i].left = (level & 0xff);
201 			ctlinfo.slinfo[i].right = ((level >> 8) & 0xff);
202 
203 			/* Sanity check */
204 			if (ctlinfo.slinfo[i].left > 100)
205 				ctlinfo.slinfo[i].left = 100;
206 			else if (ctlinfo.slinfo[i].left < 0)
207 				ctlinfo.slinfo[i].left = 0;
208 			if (ctlinfo.slinfo[i].right > 100)
209 				ctlinfo.slinfo[i].right = 100;
210 			else if (ctlinfo.slinfo[i].right < 0)
211 				ctlinfo.slinfo[i].right = 0;
212 
213 			if ((curmask & ctlinfo.stereodevs) == 0) {
214 				ctlinfo.slinfo[i].left =
215 					ctlinfo.slinfo[i].right =
216 					(ctlinfo.slinfo[i].left +
217 					 ctlinfo.slinfo[i].right) / 2;
218 				ctlinfo.slinfo[i].locked = TRUE;
219 			}
220 
221 			if (ctlinfo.slinfo[i].left != ctlinfo.slinfo[i].right)
222 				ctlinfo.slinfo[i].locked = FALSE;
223 		}
224 	}
225 
226 #ifdef SOUND_ONOFF_MAX
227 	/* Additional toggle capabilitites */
228 	for (i = SOUND_ONOFF_MIN; i <= SOUND_ONOFF_MAX; i++) {
229 		curmask = (1 << i);
230 
231 		if (curmask & ctlinfo.devmask) {
232 			sprintf(iocstr, "MIXER_READ[%d]:%s", i, m->sl[i].name);
233 			do_ioctl(MIXER_READ(i), &level, iocstr, IOC_DATA_IN);
234 
235 			switch (i) {
236 
237 #ifdef SOUND_MIXER_MUTE
238 			case SOUND_MIXER_MUTE:
239 				ctlinfo.mute = (level != 0);
240 				break;
241 #endif	/* SOUND_MIXER_MUTE */
242 
243 #ifdef SOUND_MIXER_LOUD
244 			case SOUND_MIXER_LOUD:
245 				ctlinfo.loudness = (level != 0);
246 				break;
247 #endif	/* SOUND_MIXER_LOUD */
248 
249 #ifdef SOUND_MIXER_ENHANCE
250 			case SOUND_MIXER_ENHANCE:
251 				/* Hack: The enhance values are hard wired. */
252 				switch (level) {
253 				case 80:
254 					ctlinfo.enhance = 3;
255 					break;
256 				case 60:
257 					ctlinfo.enhance = 2;
258 					break;
259 				case 40:
260 					ctlinfo.enhance = 1;
261 					break;
262 				case 0:
263 				default:
264 					ctlinfo.enhance = 0;
265 					break;
266 				}
267 				break;
268 #endif	/* SOUND_MIXER_ENHANCE */
269 
270 			default:
271 				/* Unsupported feature: ignore */
272 				break;
273 			}
274 		}
275 	}
276 #endif	/* SOUND_ONOFF_MAX */
277 
278 	if (first) {
279 		/* Save start-up settings */
280 		first = FALSE;
281 		ctlsav = ctlinfo;	/* Structure copy */
282 	}
283 }
284 
285 
286 /*
287  * mx_sethw
288  *	Set all sound hardware settings to the current ctlinfo state.
289  *
290  * Args:
291  *	m - Pointer to the main widgets structure
292  *
293  * Return:
294  *	Nothing
295  */
296 STATIC void
mx_sethw(widgets_t * m)297 mx_sethw(widgets_t *m)
298 {
299 	int				i;
300 	XmScaleCallbackStruct		s;
301 	XmToggleButtonCallbackStruct	t;
302 	XmPushButtonCallbackStruct	p;
303 
304 	/* Fake callbacks */
305 	s.reason = XmCR_VALUE_CHANGED;
306 	t.reason = XmCR_VALUE_CHANGED;
307 	p.reason = XmCR_ACTIVATE;
308 
309 	for (i = 0; i < maxdevs; i++) {
310 		if (!m->sl[i].supp)
311 			continue;
312 
313 		/* Left slider */
314 		s.value = ctlinfo.slinfo[i].left;
315 		mx_slider_l(m->sl[i].widget_l, (XtPointer) i, (XtPointer) &s);
316 
317 		/* Right slider */
318 		s.value = ctlinfo.slinfo[i].right;
319 		mx_slider_r(m->sl[i].widget_r, (XtPointer) i, (XtPointer) &s);
320 
321 		/* Lock button */
322 		t.set = (Boolean) ctlinfo.slinfo[i].locked;
323 		mx_lock_btn(m->sl[i].widget_lock_btn, (XtPointer) i,
324 			    (XtPointer) &t);
325 
326 		/* Rec button */
327 		if (m->sl[i].recsupp) {
328 #ifdef SOUND_CAP_EXCL_INPUT
329 			if ((ctlinfo.recsrc & (1 << i)) ||
330 			    ctlinfo.caps != SOUND_CAP_EXCL_INPUT)
331 #endif
332 			{
333 				if (ctlinfo.recsrc & (1 << i))
334 					t.set = True;
335 				else
336 					t.set = False;
337 
338 				mx_rec_btn(m->sl[i].widget_rec_btn,
339 					   (XtPointer) i, (XtPointer) &t);
340 			}
341 		}
342 	}
343 
344 	/* Mute button */
345 	if (m->mute_supp) {
346 		t.set = (Boolean) ctlinfo.mute;
347 		mx_mute_btn(m->mute_btn, NULL, (XtPointer) &t);
348 	}
349 
350 	/* Loudness button */
351 	if (m->loud_supp) {
352 		t.set = (Boolean) ctlinfo.loudness;
353 		mx_loud_btn(m->loud_btn, NULL, (XtPointer) &t);
354 	}
355 
356 	/* Enhance buttons */
357 	if (m->enh_supp) {
358 		mx_enhance_btn(m->enh_btn[ctlinfo.enhance],
359 			       (XtPointer) ctlinfo.enhance, (XtPointer) &p);
360 	}
361 }
362 
363 
364 /*
365  * mx_updctl
366  *	Update all control positions on screen to the current ctlinfo
367  *	state.
368  *
369  * Args:
370  *	m - Pointer to the main widgets structure
371  *
372  * Return:
373  *	Nothing
374  */
375 STATIC void
mx_updctl(widgets_t * m)376 mx_updctl(widgets_t *m)
377 {
378 	int	i;
379 
380 	for (i = 0; i < maxdevs; i++) {
381 		if (!m->sl[i].supp)
382 			continue;
383 
384 		/* Left and right sliders */
385 		XmScaleSetValue(
386 			m->sl[i].widget_l,
387 			ctlinfo.slinfo[i].left
388 		);
389 		XmScaleSetValue(
390 			m->sl[i].widget_r,
391 			ctlinfo.slinfo[i].right
392 		);
393 
394 		/* Lock button */
395 		XmToggleButtonSetState(
396 			m->sl[i].widget_lock_btn,
397 			ctlinfo.slinfo[i].locked, False
398 		);
399 
400 		/* Rec button */
401 		if (m->sl[i].recsupp) {
402 			if (ctlinfo.recsrc & (1 << i)) {
403 				XmToggleButtonSetState(
404 					m->sl[i].widget_rec_btn,
405 					True, False
406 				);
407 			}
408 			else {
409 				XmToggleButtonSetState(
410 					m->sl[i].widget_rec_btn,
411 					False, False
412 				);
413 			}
414 		}
415 	}
416 
417 	/* Mute button */
418 	if (m->mute_supp)
419 		XmToggleButtonSetState(m->mute_btn, ctlinfo.mute, False);
420 
421 	/* Loudness button */
422 	if (m->loud_supp)
423 		XmToggleButtonSetState(m->loud_btn, ctlinfo.loudness, False);
424 
425 	/* Stereo enhance menu */
426 	if (m->enh_supp) {
427 		XtVaSetValues(m->enh_opt,
428 			XmNmenuHistory, m->enh_btn[ctlinfo.enhance],
429 			NULL
430 		);
431 	}
432 }
433 
434 
435 /*
436  * mx_readfile
437  *	Read mixer settings from file and update control settings.
438  *
439  * Args:
440  *	path - File path string
441  *
442  * Return:
443  *	TRUE on success, FALSE on failure
444  */
445 STATIC bool_t
mx_readfile(char * path)446 mx_readfile(char *path)
447 {
448 	FILE	*fp;
449 	int	i,
450 		curmask,
451 		val1,
452 		val2,
453 		val3,
454 		val4;
455 	char	*p,
456 		buf[STR_BUF_SZ];
457 
458 	if (app_data.debug)
459 		fprintf(errfp, "Reading mixer file: %s\n", path);
460 
461 	/* Open file for reading */
462 	if ((fp = fopen(path, "r")) == NULL)
463 		return FALSE;
464 
465 	/* Read first line of mixer settings file */
466 	if (fgets(buf, sizeof(buf), fp) == NULL) {
467 		fclose(fp);
468 		return FALSE;
469 	}
470 
471 	/* Mixer settings file signature check */
472 	if (strncmp(buf, "# xmmix ", 8) != 0) {
473 		fclose(fp);
474 		return FALSE;
475 	}
476 
477 	/* Read the rest of the mixer settings file */
478 	while (fgets(buf, sizeof(buf), fp) != NULL) {
479 		if (buf[0] == '#')
480 			/* Comment line */
481 			continue;
482 
483 		if ((p = strchr(buf, '=')) == NULL)
484 			/* Invalid line */
485 			continue;
486 
487 		*p = '\0';
488 
489 		if (sscanf(p+1, "%d,%d,%d,%d\n",
490 			   &val1, &val2, &val3, &val4) == 4) {
491 
492 			for (i = 0; i < maxdevs; i++) {
493 				if (strcmp(buf, widgets.sl[i].name) == 0)
494 					break;
495 			}
496 
497 			if (i >= maxdevs)
498 				/* Unrecognized keyword */
499 				continue;
500 
501 			curmask = (1 << i);
502 
503 			if (!(ctlinfo.devmask & curmask))
504 				/* Device not supported */
505 				continue;
506 
507 			ctlinfo.slinfo[i].left = val1;
508 			ctlinfo.slinfo[i].right = val2;
509 
510 			if (val1 == val2)
511 				ctlinfo.slinfo[i].locked = (bool_t) (val3 > 0);
512 			else
513 				ctlinfo.slinfo[i].locked = FALSE;
514 
515 			if (val4 > 0 && widgets.sl[i].recsupp &&
516 			    (ctlinfo.recmask & curmask))
517 				ctlinfo.recsrc |= (1 << i);
518 			else
519 				ctlinfo.recsrc &= ~(1 << i);
520 		}
521 #ifdef SOUND_ONOFF_MAX
522 		else if (sscanf(p+1, "%d\n", &val1) == 1) {
523 
524 #ifdef SOUND_MIXER_MUTE
525 			if (strcmp(buf, "mute") == 0 &&
526 			    ctlinfo.devmask & (1 << SOUND_MIXER_MUTE)) {
527 				ctlinfo.mute = (bool_t) (val1 > 0);
528 			}
529 #endif	/* SOUND_MIXER_MUTE */
530 
531 #ifdef SOUND_MIXER_ENHANCE
532 			if (strcmp(buf, "enhance") == 0 &&
533 				 ctlinfo.devmask & (1 << SOUND_MIXER_ENHANCE)) {
534 				ctlinfo.enhance = val1;
535 			}
536 #endif	/* SOUND_MIXER_ENHANCE */
537 
538 #ifdef SOUND_MIXER_LOUD
539 			if (strcmp(buf, "loudness") == 0 &&
540 				 ctlinfo.devmask & (1 << SOUND_MIXER_LOUD)) {
541 				ctlinfo.loudness = (bool_t) (val1 > 0);
542 			}
543 #endif	/* SOUND_MIXER_LOUD */
544 
545 		}
546 #endif	/* SOUND_ONOFF_MAX */
547 	}
548 
549 	fclose(fp);
550 
551 	/* Update controls to match current status */
552 	mx_updctl(&widgets);
553 
554 	/* Set all controls */
555 	mx_sethw(&widgets);
556 
557 	return TRUE;
558 }
559 
560 
561 /*
562  * mx_writefile
563  *	Write current mixer settings to file.
564  *
565  * Args:
566  *	path - File path string
567  *
568  * Return:
569  *	TRUE on success, FALSE on failure
570  */
571 STATIC bool_t
mx_writefile(char * path)572 mx_writefile(char *path)
573 {
574 	FILE	*fp;
575 	int	i;
576 
577 	if (app_data.debug)
578 		fprintf(errfp, "Writing mixer file: %s\n", path);
579 
580 	/* Open file for writing */
581 	if ((fp = fopen(path, "w")) == NULL)
582 		return FALSE;
583 
584 	/* Write first two lines of mixer settings file */
585 	fprintf(fp, "# xmmix %s Mixer Settings File\n", VERSION);
586 	fprintf(fp, "# Copyright (C) 1994-1996 Ti Kan\n#\n");
587 
588 	/* Write all control settings */
589 	for (i = 0; i < MAXDEVS; i++) {
590 		if (widgets.sl[i].name == NULL)
591 			break;
592 
593 		fprintf(fp, "%s=%d,%d,%d,%d\n",
594 			widgets.sl[i].name,
595 			ctlinfo.slinfo[i].left,
596 			ctlinfo.slinfo[i].right,
597 			ctlinfo.slinfo[i].locked,
598 			(int) ((ctlinfo.recsrc & (1 << i)) > 0));
599 	}
600 
601 #ifdef SOUND_ONOFF_MAX
602 	fprintf(fp, "%s=%d\n", "mute", (int) ctlinfo.mute);
603 	fprintf(fp, "%s=%d\n", "enhance", ctlinfo.enhance);
604 	fprintf(fp, "%s=%d\n", "loudness", (int) ctlinfo.loudness);
605 #endif
606 
607 	return (fclose(fp) == 0);
608 }
609 
610 
611 /*
612  * mx_warning
613  *	Pop up a warning message dialog box.
614  *
615  * Args:
616  *	msg - The message string to display
617  *
618  * Return:
619  *	Nothing
620  */
621 STATIC void
mx_warning(char * msg)622 mx_warning(char *msg)
623 {
624 	XmString	xs;
625 
626 	xs = XmStringCreateLtoR(msg, XmSTRING_DEFAULT_CHARSET);
627 	XtVaSetValues(widgets.warning, XmNmessageString, xs, NULL);
628 	XmStringFree(xs);
629 
630 	if (!XtIsManaged(widgets.warning))
631 		XtManageChild(widgets.warning);
632 }
633 
634 
635 /***********************
636  *   public routines   *
637  ***********************/
638 
639 
640 /*
641  * mx_init_drv
642  *	Sound driver version-specific initialization routine.
643  *	Query sound driver version and set appropriate capabilities.
644  *	Note: this routine will likely require modification if
645  *	future sound driver versions add new mixer features.
646  *
647  * Args:
648  *	None
649  *
650  * Return:
651  *	Nothing
652  */
653 void
mx_init_drv(void)654 mx_init_drv(void)
655 {
656 	FILE	*fp;
657 	char	*p,
658 		tmpbuf[128];
659 
660 	if (app_data.debug) {
661 		fprintf(errfp, "XMMIX v%s%s PL%d DEBUG MODE\n\n",
662 			VERSION, VERSION_EXT, PATCHLEVEL);
663 
664 		if (app_data.demo)
665 			fprintf(errfp, "DEMO MODE!!!\n");
666 		fprintf(errfp, "Compiled with soundcard.h version %d\n",
667 			SOUND_VERSION);
668 		fprintf(errfp, "device=%s\n", app_data.device);
669 		fprintf(errfp, "helpPath=%s\n", app_data.helppath);
670 		fprintf(errfp, "autoLoadOnStartUp=%s\n",
671 			app_data.autoload == NULL ? "" : app_data.autoload);
672 		fprintf(errfp, "resetOnExit=%s\n",
673 			app_data.exitreset ? "True" : "False");
674 	}
675 
676 	/* Set maximum number of devices supported */
677 	maxdevs = MAXDEVS_V3;
678 
679 	strcpy(drv_ver, "unknown");
680 
681 	/* Query sound driver version */
682 	if ((fp = fopen("/dev/sndstat", "r")) == NULL) {
683 		if (!app_data.demo) {
684 			fprintf(errfp, "%s: %s: %s\n",
685 				PROGNAME,
686 				"Cannot open /dev/sndstat",
687 				"assuming pre-3.0 sound driver.");
688 			maxdevs = MAXDEVS_V2;
689 		}
690 		return;
691 	}
692 
693 	if (fgets(tmpbuf, sizeof(tmpbuf), fp) == NULL) {
694 		if (!app_data.demo) {
695 			fprintf(errfp, "%s: %s: %s\n",
696 				PROGNAME,
697 				"Cannot read /dev/sndstat",
698 				"assuming pre-3.0 sound driver.");
699 			maxdevs = MAXDEVS_V2;
700 		}
701 		fclose(fp);
702 		return;
703 	}
704 
705 	fclose(fp);
706 
707 	/* Eat newline */
708 	tmpbuf[strlen(tmpbuf) - 1] = '\0';
709 
710 	p = tmpbuf;
711 	if (strncmp(p, "Sound Driver", 12) == 0) {
712 		/* Old style output:
713 		 * Skip forward to the first colon character
714 		 */
715 		for (; *p != '\0' && *p != ':'; p++)
716 			;
717 
718 		if (*p != ':') {
719 			if (!app_data.demo) {
720 				fprintf(errfp, "%s: %s: %s\n",
721 					PROGNAME,
722 					"Cannot parse info from /dev/sndstat",
723 					"assuming pre-3.0 sound driver.");
724 				maxdevs = MAXDEVS_V2;
725 			}
726 			strcpy(drv_ver, tmpbuf);
727 			return;
728 		}
729 
730 		/* Skip blanks */
731 		p++;
732 		while (*p == ' ' || *p == '\t')
733 			p++;
734 	}
735 
736 	strcpy(drv_ver, p);
737 
738 	if (!app_data.demo && *p >= '0' && *p < '3')
739 		/* Running on old sound driver: set max devices accordingly */
740 		maxdevs = MAXDEVS_V2;
741 
742 	if (app_data.debug) {
743 		fprintf(errfp, "Sound driver [%s]\n", drv_ver);
744 		fprintf(errfp, "Running with maxdevs=%d\n\n", maxdevs);
745 	}
746 }
747 
748 
749 /*
750  * mx_init_hw
751  *	Pre-realize mixer initialization routine.
752  *	Query sound board mixer feature capabilities and current
753  *	hardware settings.
754  *
755  * Args:
756  *	m - Pointer to the main widgets structure
757  *
758  * Return:
759  *	Nothing
760  */
761 void
mx_init_hw(widgets_t * m)762 mx_init_hw(widgets_t *m)
763 {
764 	int	i,
765 		level,
766 		curmask;
767 	char	errmsg[STR_BUF_SZ],
768 		iocstr[STR_BUF_SZ];
769 
770 	if (app_data.demo) {
771 		/* Fake all capabilities */
772 		for (i = 0; i < maxdevs; i++) {
773 			curmask = (1 << i);
774 
775 			ctlinfo.devmask |= curmask;
776 			ctlinfo.recmask |= curmask;
777 			ctlinfo.stereodevs |= curmask;
778 
779 			m->sl[i].supp = TRUE;
780 			if (m->sl[i].type == CTL_INPUT)
781 				m->sl[i].recsupp = TRUE;
782 			else
783 				m->sl[i].recsupp = FALSE;
784 		}
785 
786 #ifdef SOUND_ONOFF_MAX
787 		for (i = SOUND_ONOFF_MIN; i <= SOUND_ONOFF_MAX; i++)
788 			ctlinfo.devmask |= (1 << i);
789 
790 		m->mute_supp = TRUE;
791 		m->loud_supp = TRUE;
792 		m->enh_supp = TRUE;
793 #endif
794 
795 		/* Save start-up settings */
796 		ctlsav = ctlinfo;	/* Structure copy */
797 		return;
798 	}
799 
800 	/* Open device */
801 	sprintf(errmsg, "%s: Cannot open device %s",
802 		PROGNAME, app_data.device);
803 	if ((dev_fd = open(app_data.device, O_RDONLY)) < 0) {
804 		perror(errmsg);
805 		exit_flag = TRUE;
806 	}
807 
808 	/* Read device mask */
809 	do_ioctl(
810 		SOUND_MIXER_READ_DEVMASK,
811 		&ctlinfo.devmask,
812 		"SOUND_MIXER_READ_DEVMASK",
813 		IOC_DATA_IN
814 	);
815 
816 	/* Read record mask */
817 	do_ioctl(
818 		SOUND_MIXER_READ_RECMASK,
819 		&ctlinfo.recmask,
820 		"SOUND_MIXER_READ_RECMASK",
821 		IOC_DATA_IN
822 	);
823 
824 	/* Read stereo devices */
825 	do_ioctl(
826 		SOUND_MIXER_READ_STEREODEVS,
827 		&ctlinfo.stereodevs,
828 		"SOUND_MIXER_READ_STEREODEVS",
829 		IOC_DATA_IN
830 	);
831 
832 #ifdef SOUND_MIXER_CAPS
833 	/* Check mixer capability */
834 	do_ioctl(
835 		SOUND_MIXER_READ_CAPS,
836 		&ctlinfo.caps,
837 		"SOUND_MIXER_READ_CAPS",
838 		IOC_DATA_IN
839 	);
840 #endif
841 
842 	/* Set flags */
843 	for (i = 0; i < maxdevs; i++) {
844 		curmask = (1 << i);
845 
846 		if ((curmask & ctlinfo.devmask) == 0)
847 			m->sl[i].supp = FALSE;
848 		else
849 			m->sl[i].supp = TRUE;
850 
851 		if (m->sl[i].type == CTL_INPUT && (curmask & ctlinfo.recmask))
852 			m->sl[i].recsupp = TRUE;
853 		else
854 			m->sl[i].recsupp = FALSE;
855 	}
856 
857 #ifdef SOUND_ONOFF_MAX
858 	/* Additional toggle capabilitites */
859 	for (i = SOUND_ONOFF_MIN; i <= SOUND_ONOFF_MAX; i++) {
860 		curmask = (1 << i);
861 
862 		switch (i) {
863 
864 #ifdef SOUND_MIXER_MUTE
865 		case SOUND_MIXER_MUTE:
866 			if (curmask & ctlinfo.devmask)
867 				m->mute_supp = TRUE;
868 			else
869 				m->mute_supp = FALSE;
870 			break;
871 #endif	/* SOUND_MIXER_MUTE */
872 
873 #ifdef SOUND_MIXER_LOUD
874 		case SOUND_MIXER_LOUD:
875 			if (curmask & ctlinfo.devmask)
876 				m->loud_supp = TRUE;
877 			else
878 				m->loud_supp = FALSE;
879 			break;
880 #endif	/* SOUND_MIXER_LOUD */
881 
882 #ifdef SOUND_MIXER_ENHANCE
883 		case SOUND_MIXER_ENHANCE:
884 			if (curmask & ctlinfo.devmask)
885 				m->enh_supp = TRUE;
886 			else
887 				m->enh_supp = FALSE;
888 			break;
889 #endif	/* SOUND_MIXER_ENHANCE */
890 
891 		default:
892 			/* Unsupported feature: ignore */
893 			break;
894 		}
895 	}
896 #else
897 	m->mute_supp = FALSE;
898 	m->loud_supp = FALSE;
899 	m->enh_supp = FALSE;
900 #endif	/* SOUND_ONOFF_MAX */
901 }
902 
903 
904 /*
905  * mx_start
906  *	Post-realize mixer initialization routine
907  *
908  * Args:
909  *	m - Pointer to the main widgets structure
910  *
911  * Return:
912  *	Nothing
913  */
914 void
mx_start(widgets_t * m)915 mx_start(widgets_t *m)
916 {
917 	char	msg[256];
918 
919 	if (app_data.autoload != NULL && app_data.autoload[0] != '\0' &&
920 	    strcmp(app_data.autoload, "/dev/null") != 0) {
921 		if (mx_readfile(app_data.autoload)) {
922 			/* Successful auto-load */
923 			return;
924 		}
925 		else {
926 			sprintf(msg,
927 			    "Autoload: Cannot read mixer settings file:\n%s",
928 			    app_data.autoload);
929 			mx_warning(msg);
930 		}
931 	}
932 
933 	/* Update screen controls to match current status */
934 	mx_updctl(m);
935 }
936 
937 
938 /**************** vv Callback routines vv ****************/
939 
940 
941 /*
942  * mx_slider_l
943  *	Left slider callback routine
944  */
945 /*ARGSUSED*/
946 void
mx_slider_l(Widget w,XtPointer client_data,XtPointer call_data)947 mx_slider_l(Widget w, XtPointer client_data, XtPointer call_data)
948 {
949 	int			level,
950 				i = (int)(void *) client_data;
951 	XmScaleCallbackStruct	*p =
952 		(XmScaleCallbackStruct *)(void *) call_data;
953 	char			iocstr[STR_BUF_SZ];
954 
955 	ctlinfo.slinfo[i].left = p->value;
956 
957 	if (ctlinfo.slinfo[i].locked) {
958 		ctlinfo.slinfo[i].right = p->value;
959 		XmScaleSetValue(widgets.sl[i].widget_r, p->value);
960 	}
961 
962 	level = (ctlinfo.slinfo[i].right << 8) | ctlinfo.slinfo[i].left;
963 
964 	sprintf(iocstr, "MIXER_WRITE[%d]:%s", i, widgets.sl[i].name);
965 	do_ioctl(MIXER_WRITE(i), &level, iocstr, IOC_DATA_OUT);
966 }
967 
968 
969 /*
970  * mx_slider_r
971  *	Right slider callback routine
972  */
973 /*ARGSUSED*/
974 void
mx_slider_r(Widget w,XtPointer client_data,XtPointer call_data)975 mx_slider_r(Widget w, XtPointer client_data, XtPointer call_data)
976 {
977 	int			level,
978 				i = (int)(void *) client_data;
979 	XmScaleCallbackStruct	*p =
980 		(XmScaleCallbackStruct *)(void *) call_data;
981 	char			iocstr[STR_BUF_SZ];
982 
983 	ctlinfo.slinfo[i].right = p->value;
984 
985 	if (ctlinfo.slinfo[i].locked) {
986 		ctlinfo.slinfo[i].left = p->value;
987 		XmScaleSetValue(widgets.sl[i].widget_l, p->value);
988 	}
989 
990 	level = (ctlinfo.slinfo[i].right << 8) | ctlinfo.slinfo[i].left;
991 
992 	sprintf(iocstr, "MIXER_WRITE[%d]:%s", i, widgets.sl[i].name);
993 	do_ioctl(MIXER_WRITE(i), &level, iocstr, IOC_DATA_OUT);
994 }
995 
996 
997 /*
998  * mx_lock_btn
999  *	Slider lock button callback routine
1000  */
1001 void
mx_lock_btn(Widget w,XtPointer client_data,XtPointer call_data)1002 mx_lock_btn(Widget w, XtPointer client_data, XtPointer call_data)
1003 {
1004 	int				level,
1005 					i = (int)(void *) client_data;
1006 	XmToggleButtonCallbackStruct	*p =
1007 		(XmToggleButtonCallbackStruct *)(void *) call_data;
1008 	char				iocstr[STR_BUF_SZ];
1009 
1010 	if (ctlinfo.slinfo[i].locked == p->set)
1011 		/* No change */
1012 		return;
1013 
1014 	if (p->set) {
1015 		ctlinfo.slinfo[i].locked = TRUE;
1016 		ctlinfo.slinfo[i].left = ctlinfo.slinfo[i].right =
1017 			(ctlinfo.slinfo[i].left + ctlinfo.slinfo[i].right) / 2;
1018 
1019 		XmScaleSetValue(
1020 			widgets.sl[i].widget_l,
1021 			ctlinfo.slinfo[i].left
1022 		);
1023 		XmScaleSetValue(
1024 			widgets.sl[i].widget_r,
1025 			ctlinfo.slinfo[i].right
1026 		);
1027 
1028 		level = (ctlinfo.slinfo[i].right << 8) | ctlinfo.slinfo[i].left;
1029 
1030 		sprintf(iocstr, "MIXER_WRITE[%d]:%s", i, widgets.sl[i].name);
1031 		do_ioctl(MIXER_WRITE(i), &level, iocstr, IOC_DATA_OUT);
1032 	}
1033 	else {
1034 		if ((1 << i) & ctlinfo.stereodevs)
1035 			ctlinfo.slinfo[i].locked = FALSE;
1036 		else {
1037 			XmToggleButtonSetState(w, (Boolean) !p->set, False);
1038 			XBell(XtDisplay(w), 50);
1039 		}
1040 	}
1041 }
1042 
1043 
1044 /*
1045  * mx_rec_btn
1046  *	Record source select button callback routine
1047  */
1048 void
mx_rec_btn(Widget w,XtPointer client_data,XtPointer call_data)1049 mx_rec_btn(Widget w, XtPointer client_data, XtPointer call_data)
1050 {
1051 	int				i = (int)(void *) client_data,
1052 					j,
1053 					curmask;
1054 	XmToggleButtonCallbackStruct	*p =
1055 		(XmToggleButtonCallbackStruct *)(void *) call_data;
1056 
1057 #ifdef SOUND_CAP_EXCL_INPUT
1058 	if (ctlinfo.caps == SOUND_CAP_EXCL_INPUT) {
1059 		if (!p->set) {
1060 			XmToggleButtonSetState(w, True, False);
1061 			return;
1062 		}
1063 
1064 		for (j = 0; j < maxdevs; j++) {
1065 			if (!widgets.sl[j].supp || i == j)
1066 				continue;
1067 
1068 			curmask = (1 << j);
1069 
1070 			if ((curmask & ctlinfo.recmask) &&
1071 			    (curmask & ctlinfo.recsrc)) {
1072 				XmToggleButtonSetState(
1073 					widgets.sl[j].widget_rec_btn,
1074 					False,
1075 					False
1076 				);
1077 			}
1078 
1079 		}
1080 
1081 		ctlinfo.recsrc = 0;
1082 	}
1083 #endif
1084 
1085 	if (p->set)
1086 		ctlinfo.recsrc |= (1 << i);
1087 	else
1088 		ctlinfo.recsrc &= ~(1 << i);
1089 
1090 	do_ioctl(
1091 		SOUND_MIXER_WRITE_RECSRC,
1092 		&ctlinfo.recsrc,
1093 		"SOUND_MIXER_WRITE_RECSRC",
1094 		IOC_DATA_OUT
1095 	);
1096 }
1097 
1098 
1099 /*
1100  * mx_flat_btn
1101  *	Bass/Treble Flat button callback routine.
1102  */
1103 /*ARGSUSED*/
1104 void
mx_flat_btn(Widget w,XtPointer client_data,XtPointer call_data)1105 mx_flat_btn(Widget w, XtPointer client_data, XtPointer call_data)
1106 {
1107 	int	level;
1108 
1109 	ctlinfo.slinfo[SOUND_MIXER_BASS].left = 50;
1110 	ctlinfo.slinfo[SOUND_MIXER_BASS].right = 50;
1111 	ctlinfo.slinfo[SOUND_MIXER_TREBLE].left = 50;
1112 	ctlinfo.slinfo[SOUND_MIXER_TREBLE].right = 50;
1113 
1114 	XmScaleSetValue(widgets.sl[SOUND_MIXER_BASS].widget_l, 50);
1115 	XmScaleSetValue(widgets.sl[SOUND_MIXER_BASS].widget_r, 50);
1116 	XmScaleSetValue(widgets.sl[SOUND_MIXER_TREBLE].widget_l, 50);
1117 	XmScaleSetValue(widgets.sl[SOUND_MIXER_TREBLE].widget_r, 50);
1118 
1119 	level = (50 << 8) | 50;
1120 
1121 	do_ioctl(
1122 		SOUND_MIXER_WRITE_BASS,
1123 		&level,
1124 		"SOUND_MIXER_WRITE_BASS",
1125 		IOC_DATA_OUT
1126 	);
1127 	do_ioctl(
1128 		SOUND_MIXER_WRITE_TREBLE,
1129 		&level,
1130 		"SOUND_MIXER_WRITE_TREBLE",
1131 		IOC_DATA_OUT
1132 	);
1133 }
1134 
1135 
1136 /*
1137  * mx_mute_btn
1138  *	Mute button callback routine.
1139  */
1140 /*ARGSUSED*/
1141 void
mx_mute_btn(Widget w,XtPointer client_data,XtPointer call_data)1142 mx_mute_btn(Widget w, XtPointer client_data, XtPointer call_data)
1143 {
1144 	int				level;
1145 	XmToggleButtonCallbackStruct	*p =
1146 		(XmToggleButtonCallbackStruct *)(void *) call_data;
1147 
1148 	if (ctlinfo.mute == p->set)
1149 		/* No change */
1150 		return;
1151 
1152 	ctlinfo.mute = p->set;
1153 
1154 	level = (int) p->set;
1155 
1156 #ifdef SOUND_MIXER_MUTE
1157 	do_ioctl(
1158 		SOUND_MIXER_WRITE_MUTE,
1159 		&level,
1160 		"SOUND_MIXER_WRITE_MUTE",
1161 		IOC_DATA_OUT
1162 	);
1163 #endif
1164 }
1165 
1166 
1167 /*
1168  * mx_loud_btn
1169  *	Loudness button callback routine.
1170  */
1171 /*ARGSUSED*/
1172 void
mx_loud_btn(Widget w,XtPointer client_data,XtPointer call_data)1173 mx_loud_btn(Widget w, XtPointer client_data, XtPointer call_data)
1174 {
1175 	int				level;
1176 	XmToggleButtonCallbackStruct	*p =
1177 		(XmToggleButtonCallbackStruct *)(void *) call_data;
1178 
1179 	if (ctlinfo.loudness == p->set)
1180 		/* No change */
1181 		return;
1182 
1183 	ctlinfo.loudness = p->set;
1184 
1185 	level = (int) p->set;
1186 
1187 #ifdef SOUND_MIXER_LOUD
1188 	do_ioctl(
1189 		SOUND_MIXER_WRITE_LOUD,
1190 		&level,
1191 		"SOUND_MIXER_WRITE_LOUD",
1192 		IOC_DATA_OUT
1193 	);
1194 #endif
1195 }
1196 
1197 
1198 /*
1199  * mx_enhance_btn
1200  *	Stereo enhance button callback routine.
1201  */
1202 /*ARGSUSED*/
1203 void
mx_enhance_btn(Widget w,XtPointer client_data,XtPointer call_data)1204 mx_enhance_btn(Widget w, XtPointer client_data, XtPointer call_data)
1205 {
1206 	int	level,
1207 		i = (int)(void *) client_data;
1208 
1209 	if (ctlinfo.enhance == i)
1210 		/* No change */
1211 		return;
1212 
1213 	ctlinfo.enhance = i;
1214 
1215 	/* Hack: The enhance values are hard wired */
1216 	switch (i) {
1217 	case 0:
1218 		level = 0;
1219 		break;
1220 	case 1:
1221 		level = 40;
1222 		break;
1223 	case 2:
1224 		level = 60;
1225 		break;
1226 	case 3:
1227 		level = 80;
1228 		break;
1229 	default:
1230 		return;
1231 	}
1232 
1233 #ifdef SOUND_MIXER_ENHANCE
1234 	do_ioctl(
1235 		SOUND_MIXER_WRITE_ENHANCE,
1236 		&level,
1237 		"SOUND_MIXER_WRITE_ENHANCE",
1238 		IOC_DATA_OUT
1239 	);
1240 #endif
1241 }
1242 
1243 
1244 /*
1245  * mx_load
1246  *	Load button callback routine.
1247  */
1248 /*ARGSUSED*/
1249 void
mx_load(Widget w,XtPointer client_data,XtPointer call_data)1250 mx_load(Widget w, XtPointer client_data, XtPointer call_data)
1251 {
1252 	XmString	xs;
1253 
1254 	/* Pop up file selection box window */
1255 	if (!XtIsManaged(widgets.fsform)) {
1256 		fsmode = FS_LOAD;
1257 		xs = XmStringCreateSimple("Load Mixer Settings");
1258 		XtVaSetValues(widgets.fsform, XmNdialogTitle, xs, NULL);
1259 		XtManageChild(widgets.fsform);
1260 		XmStringFree(xs);
1261 	}
1262 }
1263 
1264 
1265 /*
1266  * mx_save
1267  *	Save button callback routine.
1268  */
1269 /*ARGSUSED*/
1270 void
mx_save(Widget w,XtPointer client_data,XtPointer call_data)1271 mx_save(Widget w, XtPointer client_data, XtPointer call_data)
1272 {
1273 	XmString	xs;
1274 
1275 	/* Pop up file selection box window */
1276 	if (!XtIsManaged(widgets.fsform)) {
1277 		fsmode = FS_SAVE;
1278 		xs = XmStringCreateSimple("Save Mixer Settings");
1279 		XtVaSetValues(widgets.fsform, XmNdialogTitle, xs, NULL);
1280 		XtManageChild(widgets.fsform);
1281 		XmStringFree(xs);
1282 	}
1283 }
1284 
1285 
1286 /*
1287  * mx_exit
1288  *	Exit button callback routine.
1289  */
1290 /*ARGSUSED*/
1291 void
mx_exit(Widget w,XtPointer client_data,XtPointer call_data)1292 mx_exit(Widget w, XtPointer client_data, XtPointer call_data)
1293 {
1294 	if (app_data.exitreset)
1295 		mx_reset(w, client_data, call_data);
1296 
1297 	exit_flag = TRUE;
1298 }
1299 
1300 
1301 /*
1302  * mx_reset
1303  *	Reset button callback routine.
1304  */
1305 /*ARGSUSED*/
1306 void
mx_reset(Widget w,XtPointer client_data,XtPointer call_data)1307 mx_reset(Widget w, XtPointer client_data, XtPointer call_data)
1308 {
1309 	if (app_data.debug)
1310 		fprintf(errfp, "Resetting mixer\n");
1311 
1312 	/* Restore start-up settings */
1313 	ctlinfo = ctlsav;	/* Structure copy */
1314 
1315 	/* Update controls to match current status */
1316 	mx_updctl(&widgets);
1317 
1318 	/* Set all controls */
1319 	mx_sethw(&widgets);
1320 }
1321 
1322 
1323 /*
1324  * mx_manpg
1325  *	Man Page button callback routine.
1326  */
1327 /*ARGSUSED*/
1328 void
mx_manpg(Widget w,XtPointer client_data,XtPointer call_data)1329 mx_manpg(Widget w, XtPointer client_data, XtPointer call_data)
1330 {
1331 	FILE		*fp;
1332 	char		*helptext = NULL,
1333 			buf[STR_BUF_SZ * 2];
1334 
1335 	if (XtIsManaged(widgets.helpform))
1336 		return;
1337 
1338 	if ((fp = fopen(app_data.helppath, "r")) == NULL) {
1339 		/* Can't read help file on this widget */
1340 		sprintf(buf, "Cannot open help file: %s", app_data.helppath);
1341 		XmTextSetString(widgets.helptxt, buf);
1342 		XtManageChild(widgets.helpform);
1343 		return;
1344 	}
1345 
1346 	while (fgets(buf, sizeof(buf), fp) != NULL) {
1347 		if (buf[0] == '#')
1348 			/* Comment */
1349 			continue;
1350 
1351 		if (helptext == NULL) {
1352 			helptext = (char *) XtMalloc(strlen(buf) + 1);
1353 
1354 			if (helptext != NULL)
1355 				*helptext = '\0';
1356 		}
1357 		else {
1358 			helptext = (char *) XtRealloc(
1359 				helptext,
1360 				strlen(helptext) + strlen(buf) + 1
1361 			);
1362 		}
1363 
1364 		if (helptext == NULL) {
1365 			fprintf(errfp, "%s: Out of memory\n", PROGNAME);
1366 			exit_flag = TRUE;
1367 		}
1368 
1369 		strcat(helptext, buf);
1370 	}
1371 
1372 	fclose(fp);
1373 
1374 	XmTextSetString(widgets.helptxt, helptext);
1375 	XtFree(helptext);
1376 	XtManageChild(widgets.helpform);
1377 }
1378 
1379 
1380 /*
1381  * mx_about
1382  *	About button callback routine.
1383  */
1384 /*ARGSUSED*/
1385 void
mx_about(Widget w,XtPointer client_data,XtPointer call_data)1386 mx_about(Widget w, XtPointer client_data, XtPointer call_data)
1387 {
1388 	char		txt[512];
1389 	XmString	xs,
1390 			xs_progname,
1391 			xs_desc,
1392 			xs_info,
1393 			xs_tmp;
1394 
1395 	if (XtIsManaged(widgets.about))
1396 		return;
1397 
1398 	xs_progname = XmStringCreateLtoR(PROGNAME, CHSET1);
1399 
1400 	sprintf(txt, "   v%s%s PL%d\n%s\n%s\n%s\n\n",
1401 		VERSION,
1402 		VERSION_EXT,
1403 		PATCHLEVEL,
1404 		"Motif(tm) Audio Mixer",
1405 		"Copyright (C) 1994-1996  Ti Kan",
1406 		"E-mail: ti@amb.org");
1407 
1408 	xs_desc = XmStringCreateLtoR(txt, CHSET2);
1409 
1410 	sprintf(txt, "%s %d\n\nDevice: %s\nDriver: %s\n\n%s\n%s\n",
1411 		"Compiled with Unix Sound System soundcard.h version",
1412 		SOUND_VERSION,
1413 		app_data.demo ?
1414 		"DEMO MODE - does not operate sound hardware" :
1415 		app_data.device,
1416 		drv_ver,
1417 		"This is free software and comes with no warranty.",
1418 		"See the GNU General Public License for details.");
1419 
1420 	xs_info = XmStringCreateLtoR(txt, CHSET3);
1421 
1422 	/* Set the dialog box message */
1423 	xs_tmp = XmStringConcat(xs_progname, xs_desc);
1424 	xs = XmStringConcat(xs_tmp, xs_info);
1425 
1426 	XtVaSetValues(widgets.about, XmNmessageString, xs, NULL);
1427 	XmStringFree(xs_progname);
1428 	XmStringFree(xs_desc);
1429 	XmStringFree(xs_info);
1430 	XmStringFree(xs_tmp);
1431 	XmStringFree(xs);
1432 
1433 	/* Pop up the about dialog box */
1434 	XtManageChild(widgets.about);
1435 }
1436 
1437 
1438 /*
1439  * mx_fsok_btn
1440  *	File selection box OK button callback routine.
1441  */
1442 /*ARGSUSED*/
1443 void
mx_fsok_btn(Widget w,XtPointer client_data,XtPointer call_data)1444 mx_fsok_btn(Widget w, XtPointer client_data, XtPointer call_data)
1445 {
1446 	XmFileSelectionBoxCallbackStruct	*p =
1447 		(XmFileSelectionBoxCallbackStruct *)(void *) call_data;
1448 	struct stat				stbuf;
1449 	char					*file,
1450 						msg[256];
1451 
1452 	if (!XmStringGetLtoR(p->value, XmSTRING_DEFAULT_CHARSET, &file) ||
1453 	    file == NULL) {
1454 		XBell(XtDisplay(w), 50);
1455 		return;
1456 	}
1457 
1458 	if (stat(file, &stbuf) < 0) {
1459 		if (errno != ENOENT) {
1460 			sprintf(msg, "Invalid mixer settings file:\n%s",
1461 				file);
1462 			mx_warning(msg);
1463 			return;
1464 		}
1465 	}
1466 	else {
1467 		switch (stbuf.st_mode & S_IFMT) {
1468 		case S_IFREG:
1469 			break;
1470 		default:
1471 			sprintf(msg, "Invalid mixer settings file:\n%s",
1472 				file);
1473 			mx_warning(msg);
1474 			return;
1475 		}
1476 	}
1477 
1478 	switch (fsmode) {
1479 	case FS_LOAD:
1480 		if (!mx_readfile(file)) {
1481 			sprintf(msg, "Cannot read mixer settings file:\n%s",
1482 				file);
1483 			mx_warning(msg);
1484 			return;
1485 		}
1486 		break;
1487 	case FS_SAVE:
1488 		if (!mx_writefile(file)) {
1489 			sprintf(msg, "Cannot write mixer settings file:\n%s",
1490 				file);
1491 			mx_warning(msg);
1492 			return;
1493 		}
1494 		break;
1495 	default:
1496 		XBell(XtDisplay(w), 50);
1497 		return;
1498 	}
1499 
1500 	/* Pop down file selection box window */
1501 	if (XtIsManaged(widgets.fsform))
1502 		XtUnmanageChild(widgets.fsform);
1503 }
1504 
1505 
1506 /*
1507  * mx_fscancel_btn
1508  *	File selection box Cancel button callback routine.
1509  */
1510 /*ARGSUSED*/
1511 void
mx_fscancel_btn(Widget w,XtPointer client_data,XtPointer call_data)1512 mx_fscancel_btn(Widget w, XtPointer client_data, XtPointer call_data)
1513 {
1514 	/* Pop down file selection box window */
1515 	if (XtIsManaged(widgets.fsform))
1516 		XtUnmanageChild(widgets.fsform);
1517 }
1518 
1519 
1520 /*
1521  * mx_focuschg
1522  *	Main window focus change callback routine.
1523  */
1524 /*ARGSUSED*/
1525 void
mx_focuschg(Widget w,XtPointer client_data,XtPointer call_data)1526 mx_focuschg(Widget w, XtPointer client_data, XtPointer call_data)
1527 {
1528 	XmAnyCallbackStruct	*p = (XmAnyCallbackStruct *)(void *) call_data;
1529 	Widget			form = (Widget) client_data;
1530 
1531 	if (p->reason != XmCR_FOCUS || form == (Widget) NULL)
1532 		return;
1533 
1534 	/* Query hardware mixer settings */
1535 	mx_queryhw(&widgets);
1536 
1537 	/* Update screen controls */
1538 	mx_updctl(&widgets);
1539 }
1540 
1541 
1542 /**************** ^^ Callback routines ^^ ****************/
1543 
1544