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