1 /*======================================================================
2 xmix: An X11 interface for Linux derived from:
3 ----------
4 xmix: X interface to the Sound Blaster mixer.
5 [ This file is a part of SBlast-BSD-1.5 ]
6
7 Steve Haehnichen <shaehnic@ucsd.edu>
8
9 xmix.c,v 1.5 1992/09/14 03:17:21 steve Exp
10
11 Copyright (C) 1992 Steve Haehnichen.
12
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 1, or (at your option)
16 any later version.
17
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26
27 * xmix.c,v
28 * Revision 1.5 1992/09/14 03:17:21 steve
29 * Released with driver v1.5.
30 * Converted over to stereo CD levels.
31 *
32 * Revision 1.4 1992/06/12 22:33:39 steve
33 * Much better.
34 * Moved many resources into source code.
35 * Ready for release in v1.4
36 *
37 * Revision 1.3 1992/06/08 04:36:26 steve
38 * Works fine.
39 *
40 ----------
41 Linux/Sound driver V2.0 port by Hal Brand (brand@netcom.com or brand1@llnl.gov)
42 Let's call this xmix V2.0 since it is significantly changed.
43
44 Patches for using the sound driver capabilities from:
45 <rick@digibd.com>
46 I added a little bit of code to have X11 disable the controls but BIG THANKS
47 to Rick! I'll call this version V2.1
48
49 ======================================================================*/
50
51 #include <stdio.h>
52 #include <sys/errno.h>
53 #include <fcntl.h>
54
55 /*
56 * Include files required for all Toolkit programs
57 */
58 #include <X11/Intrinsic.h> /* Intrinsics Definitions */
59 #include <X11/StringDefs.h> /* Standard Name-String definitions */
60 #include <X11/Shell.h> /* Shell Definitions */
61
62 /*
63 * Public include file for widgets we actually use in this file.
64 */
65 #include <X11/Xaw/Command.h> /* Athena Command Widget */
66 #include <X11/Xaw/Box.h>
67 #include <X11/Xaw/Form.h>
68 #include <X11/Xaw/Toggle.h>
69 #include <X11/Xaw/Scrollbar.h>
70 #include <X11/Xaw/Label.h>
71
72 #include "xmix.icon.bit" /* The icon, of course */
73 #include "locked.bit" /* L & R locked together (From dirt) */
74 #include "unlocked.bit" /* L & R free to be different (From dirt)*/
75 #include "circle_on.bit"
76 #include "circle_off.bit"
77 #include "square_empty.bit"
78 #include "square_with_x.bit"
79
80 #include <sys/soundcard.h>
81
82 #define SOUND_FULL_SCALE 100.0
83 #define MAX_SOUND_VOL 95
84 #define MIN_SOUND_VOL 05
85 #define SLIDER_LENGTH (0.05)
86
87
88
89 /*
90 * Here's the debugging macro I use here and there, so I can turn
91 * them all on or off in one place.
92 */
93 /* #define DEBUG */
94 #ifdef DEBUG
95 #define DPRINTF(x) printf x
96 #else
97 #define DPRINTF(x)
98 #endif
99
100
101 /*
102 * Some of these Xt names are too long for nice formatting
103 */
104 #define MW XtVaCreateManagedWidget
105 #define PORTION(val) (1.0 - (float)val / SOUND_FULL_SCALE)
106 #define MK_BITMAP(bits, width, height) \
107 XCreateBitmapFromData (XtDisplay (topLevel), \
108 RootWindowOfScreen (XtScreen (topLevel)),\
109 bits, width, height)
110 #define SCROLLBAR_RES XtNwidth, 23, XtNheight, 100
111 #define CENTER(widg) XtVaSetValues (widg, XtNwidth,\
112 centering_width, NULL)
113
114
115 typedef struct stereovolume
116 {
117 unsigned char left;
118 unsigned char right;
119 } StereoVolume;
120
121
122 typedef struct volctrl
123 {
124 int mixer_id;
125 Widget formw;
126 Widget labelw;
127 Widget leftw;
128 Widget rightw;
129 Widget lockw;
130 int locked;
131 StereoVolume volume;
132 int supported;
133 } VolumeControl;
134
135
136
137 /*
138 * I'm lazy. Everything is global today.
139 */
140 Pixmap icon_pixmap, locked_pixmap, unlocked_pixmap;
141 Pixmap circle_on_pixmap, circle_off_pixmap;
142 Pixmap square_empty_pixmap, square_with_x_pixmap;
143 Widget topLevel, quit, whole, buttons, sliders;
144 Widget sources, line_src, mic_src, cd_src, source_label;
145 Widget line_src_label, mic_src_label, cd_src_label;
146 int centering_width;
147 int mixer_fd;
148
149 VolumeControl master, line, dsp, fm, cd, mic, bass, treble, reclvl;
150
151
152
153 /*
154 * Convert all the pixmap data into Pixmap objects.
155 */
install_pixmaps(void)156 static void install_pixmaps (void)
157 {
158 /* The icon bitmap */
159 icon_pixmap = MK_BITMAP (xmix_bits, xmix_width, xmix_height);
160 XtVaSetValues (topLevel, XtNiconPixmap, icon_pixmap, NULL);
161
162 locked_pixmap = MK_BITMAP (locked_bits, locked_width, locked_height);
163 unlocked_pixmap = MK_BITMAP (unlocked_bits, unlocked_width, unlocked_height);
164 circle_on_pixmap =
165 MK_BITMAP (circle_on_bits, circle_on_width, circle_on_height);
166 circle_off_pixmap =
167 MK_BITMAP (circle_off_bits, circle_off_width, circle_off_height);
168 square_empty_pixmap =
169 MK_BITMAP (square_empty_bits, square_empty_width, square_empty_height);
170 square_with_x_pixmap =
171 MK_BITMAP (square_with_x_bits, square_with_x_width, square_with_x_height);
172 }
173
174
175
sync_slider(VolumeControl * vcptr)176 static void sync_slider(VolumeControl *vcptr)
177 {
178 float portion;
179
180 if (!vcptr->supported)
181 return;
182 if (ioctl(mixer_fd,MIXER_READ(vcptr->mixer_id),&vcptr->volume) == -1)
183 perror("Error reading volumes in sync_slider");
184 portion = PORTION(vcptr->volume.left);
185 XawScrollbarSetThumb (vcptr->leftw, portion, SLIDER_LENGTH);
186 if (vcptr->rightw != NULL)
187 {
188 portion = PORTION(vcptr->volume.right);
189 XawScrollbarSetThumb (vcptr->rightw, portion, SLIDER_LENGTH);
190 }
191 return;
192 }
193
194
195
196
sync_lock(VolumeControl * vcptr)197 static void sync_lock(VolumeControl *vcptr)
198 {
199 if (vcptr->locked)
200 XtVaSetValues (vcptr->lockw, XtNbitmap, locked_pixmap, NULL);
201 else
202 XtVaSetValues (vcptr->lockw, XtNbitmap, unlocked_pixmap, NULL);
203 CENTER (vcptr->lockw);
204 }
205
206
207
Handle_lock(Widget w,XtPointer client_data,XtPointer call_data)208 static void Handle_lock(Widget w, XtPointer client_data, XtPointer call_data)
209 {
210 VolumeControl *vcptr = client_data;
211
212 vcptr->locked = !(vcptr->locked);
213 if (vcptr->locked && vcptr->volume.left != vcptr->volume.right)
214 {
215 vcptr->volume.left = vcptr->volume.right =
216 ((vcptr->volume.left + vcptr->volume.right)/2);
217 if (ioctl(mixer_fd,MIXER_WRITE(vcptr->mixer_id),&vcptr->volume) == -1)
218 perror("Error in Handle_lock writing volumes");
219 sync_slider(vcptr);
220 }
221 sync_lock(vcptr);
222 }
223
224
225
226 /*
227 * Match the shown button to the mixer's idea of the recording source.
228 */
sync_source_display(void)229 static void sync_source_display(void)
230 {
231 int source,id;
232
233 if (ioctl(mixer_fd,SOUND_MIXER_READ_RECSRC,&source) == -1)
234 perror("Error reading mixer recording source");
235 DPRINTF(("Current recording source is %d\n",source));
236 id = 0;
237 if (source == 0)
238 {
239 id = SOUND_MIXER_MIC;
240 source = 1 << id;
241 if (ioctl(mixer_fd,SOUND_MIXER_WRITE_RECSRC,&source) == -1)
242 perror("Error setting mixer recording source to MIC!");
243 }
244 else
245 {
246 while ((source & 1) == 0)
247 {
248 source = source>>1;
249 ++id;
250 }
251 }
252 DPRINTF(("Current recording source ID=%d\n",id));
253 switch (id)
254 {
255 case SOUND_MIXER_MIC:
256 XtVaSetValues (mic_src, XtNbitmap, circle_on_pixmap, NULL);
257 XtVaSetValues (cd_src, XtNbitmap, circle_off_pixmap, NULL);
258 XtVaSetValues (line_src, XtNbitmap, circle_off_pixmap, NULL);
259 break;
260 case SOUND_MIXER_CD:
261 XtVaSetValues (mic_src, XtNbitmap, circle_off_pixmap, NULL);
262 XtVaSetValues (cd_src, XtNbitmap, circle_on_pixmap, NULL);
263 XtVaSetValues (line_src, XtNbitmap, circle_off_pixmap, NULL);
264 break;
265 case SOUND_MIXER_LINE:
266 XtVaSetValues (mic_src, XtNbitmap, circle_off_pixmap, NULL);
267 XtVaSetValues (cd_src, XtNbitmap, circle_off_pixmap, NULL);
268 XtVaSetValues (line_src, XtNbitmap, circle_on_pixmap, NULL);
269 break;
270 default:
271 fprintf (stderr, "Invalid recording source!\n");
272 }
273 return;
274 }
275
276
277
278
279 /*
280 * Callback for selecting a new source.
281 * Basically, we set the mixer to the new source, and then
282 * let sync_source_display handle the visual feedback.
283 * (Easier than radioGroups.)
284 */
Handle_source(Widget w,XtPointer client_data,XtPointer call_data)285 static void Handle_source(Widget w, XtPointer client_data,
286 XtPointer call_data)
287 {
288 int source = 1 << ((int) client_data);
289
290 DPRINTF(("Setting recording source to %d (%d)\n",source,(int)client_data));
291 if (ioctl(mixer_fd,SOUND_MIXER_WRITE_RECSRC,&source) == -1)
292 perror("Error writing mixer recording source");
293
294 sync_source_display();
295 }
296
297
298
299
300 /*
301 * Quit button callback function
302 */
Quit(Widget w,XtPointer client_data,XtPointer call_data)303 void Quit(Widget w, XtPointer client_data, XtPointer call_data)
304 {
305 DPRINTF (("Exiting...\n"));
306 exit (0);
307 }
308
309
310
set_slider(VolumeControl * vcptr,Widget w,int value)311 static void set_slider(VolumeControl *vcptr, Widget w, int value)
312 {
313 float portion = PORTION(value);
314 int update_needed;
315
316 DPRINTF (("set_slider: val = %d, portion = %f\n",value,portion));
317
318 if (vcptr->locked)
319 {
320 update_needed = (vcptr->volume.left != value);
321 DPRINTF (("set_slider: locked; updating\n"));
322 vcptr->volume.left = vcptr->volume.right = value;
323 XawScrollbarSetThumb(vcptr->leftw, portion, SLIDER_LENGTH);
324 XawScrollbarSetThumb(vcptr->rightw, portion, SLIDER_LENGTH);
325 }
326 else
327 {
328 if (vcptr->leftw == w)
329 {
330 update_needed = (vcptr->volume.left != value);
331 vcptr->volume.left = value;
332 }
333 else
334 {
335 update_needed = (vcptr->volume.right != value);
336 vcptr->volume.right = value;
337 }
338 XawScrollbarSetThumb(w, portion, SLIDER_LENGTH);
339 }
340
341 /*
342 * If the new value is at a different notch than the current setting,
343 * then inform the mixer, and adopt the setting.
344 * Otherwise, don't waste time setting the mixer.
345 */
346 if (update_needed)
347 {
348 DPRINTF (("set_slider: updating mixer\n"));
349 if (vcptr->supported
350 && ioctl(mixer_fd,MIXER_WRITE(vcptr->mixer_id),&vcptr->volume) == -1)
351 perror("Error writing mixer in Handle_slider");
352 }
353 return;
354 }
355
356
357
358 /*
359 * This is for pushing a slider to MAX or MIN position.
360 * Of questionable utility, yes..
361 */
Handle_slam_slider(Widget w,XtPointer client_data,XtPointer call_data)362 static void Handle_slam_slider(Widget w, XtPointer client_data,
363 XtPointer call_data)
364 {
365 int val;
366 VolumeControl *vcptr = client_data;
367
368 val = (vcptr->leftw == w) ? vcptr->volume.left : vcptr->volume.right;
369 if ((int)call_data < 0)
370 {
371 if (val >= MAX_SOUND_VOL)
372 val = 10*((MAX_SOUND_VOL-1)/10);
373 else
374 val -= 10;
375 if (val < MIN_SOUND_VOL) val = MIN_SOUND_VOL;
376 }
377 else
378 {
379 if (val <= MIN_SOUND_VOL)
380 val = 10*((MIN_SOUND_VOL+9)/10);
381 else
382 val += 10;
383 if (val > MAX_SOUND_VOL) val = MAX_SOUND_VOL;
384 }
385
386 set_slider(vcptr,w,val);
387 return;
388 }
389
390
391
392 /*
393 * XtNjumpProc callback for volume fader scrollbar widgets.
394 * Great pains are taken to make the slider accurately reflect
395 * the granular mixer setting, without actually querying the mixer
396 * device. (This is faster.)
397 */
Handle_slider(Widget w,XtPointer client_data,XtPointer call_data)398 static void Handle_slider(Widget w, XtPointer client_data,
399 XtPointer call_data)
400 {
401 int val;
402 VolumeControl *vcptr = client_data;
403
404 DPRINTF (("Handle_slider got %f\n", *(float*)call_data));
405 val = (int)(.5 + (SOUND_FULL_SCALE * (1.0 - *(float*)call_data)));
406 if (val > MAX_SOUND_VOL)
407 val = MAX_SOUND_VOL;
408 else if (val < MIN_SOUND_VOL)
409 val = MIN_SOUND_VOL;
410 set_slider(vcptr,w,val);
411 return;
412 }
413
414
415
sync_sliders(void)416 static void sync_sliders(void)
417 {
418 sync_slider(&master);
419 sync_slider(&bass);
420 sync_slider(&treble);
421 sync_slider(&line);
422 sync_slider(&dsp);
423 sync_slider(&fm);
424 sync_slider(&cd);
425 sync_slider(&mic);
426 sync_slider(&reclvl);
427 }
428
429
430
431
432 /*
433 * Rescan the mixer settings and make all the indicators reflect
434 * the current values.
435 */
sync_display(void)436 static void sync_display(void)
437 {
438 DPRINTF(("Updating..\n"));
439 sync_source_display();
440 sync_sliders();
441 }
442
443
444
set_supported(VolumeControl * vcptr,int is_sup)445 static void set_supported(VolumeControl *vcptr, int is_sup)
446 {
447 vcptr->supported = is_sup;
448 XtSetSensitive(vcptr->formw,is_sup);
449 return;
450 }
451
452
main(int argc,char ** argv)453 void main (int argc, char **argv)
454 {
455 XtAppContext app_context;
456 int scroll_sep, longway;
457 Widget version;
458 int supported;
459 char mixer_name[32];
460
461 {
462 int i;
463
464 longway=0;
465 strcpy(mixer_name, "/dev/mixer");
466
467 i=1;
468 while (i<argc) {
469 if (strcmp(argv[i],"-l")==0) longway=1;
470
471 if (strcmp(argv[i],"-device")==0) {
472 i++;
473 if (i>=argc) {
474 fprintf(stderr, "Error: no device specified\n");
475 exit(1);
476 }
477 strcpy(mixer_name, argv[i]);
478 }
479 i++;
480 };
481 }
482
483 topLevel = XtVaAppInitialize (&app_context,
484 "XMix", /* Application class */
485 NULL, 0, /* command line option list */
486 &argc, argv, /* command line args */
487 NULL, /* for missing app-defaults file */
488 NULL); /* terminate varargs list */
489
490
491 whole = MW ("whole", formWidgetClass, topLevel, NULL);
492
493 sliders = MW ("sliders", formWidgetClass, whole, NULL);
494
495 master.mixer_id = SOUND_MIXER_VOLUME;
496 master.formw = MW ("master_form", formWidgetClass, sliders, NULL);
497 master.labelw = MW ("master_label", labelWidgetClass, master.formw,
498 XtNlabel, "Master", NULL);
499 master.leftw = MW ("master_l", scrollbarWidgetClass, master.formw,
500 SCROLLBAR_RES, XtNfromVert, master.labelw,NULL);
501 master.rightw = MW ("master_r", scrollbarWidgetClass, master.formw,
502 SCROLLBAR_RES, XtNfromHoriz, master.leftw,
503 XtNfromVert, master.labelw, NULL);
504 master.lockw = MW ("master_lock", commandWidgetClass, master.formw,
505 XtNfromVert, master.leftw, NULL);
506
507 bass.mixer_id = SOUND_MIXER_BASS;
508 bass.formw = MW ("bass_form", formWidgetClass, sliders,
509 XtNfromHoriz, master.formw, NULL);
510 bass.labelw = MW ("bass_label", labelWidgetClass, bass.formw,
511 XtNlabel, "Bass", NULL);
512 bass.leftw = MW ("bass_l", scrollbarWidgetClass, bass.formw, SCROLLBAR_RES,
513 XtNfromVert, bass.labelw, NULL);
514 bass.rightw = MW ("bass_r", scrollbarWidgetClass, bass.formw, SCROLLBAR_RES,
515 XtNfromHoriz, bass.leftw, XtNfromVert, bass.labelw, NULL);
516 bass.lockw = MW ("bass_lock", commandWidgetClass, bass.formw,
517 XtNfromVert, bass.leftw, NULL);
518
519 treble.mixer_id = SOUND_MIXER_TREBLE;
520 treble.formw = MW ("treble_form", formWidgetClass, sliders,
521 XtNfromHoriz, bass.formw, NULL);
522 treble.labelw = MW ("treble_label", labelWidgetClass, treble.formw,
523 XtNlabel, "Treble", NULL);
524 treble.leftw = MW ("treble_l", scrollbarWidgetClass, treble.formw,
525 SCROLLBAR_RES, XtNfromVert, treble.labelw, NULL);
526 treble.rightw = MW ("treble_r", scrollbarWidgetClass, treble.formw,
527 SCROLLBAR_RES, XtNfromHoriz, treble.leftw,
528 XtNfromVert, treble.labelw, NULL);
529 treble.lockw = MW ("treble_lock", commandWidgetClass, treble.formw,
530 XtNfromVert, treble.leftw, NULL);
531
532 line.mixer_id = SOUND_MIXER_LINE;
533 line.formw = MW ("line_form", formWidgetClass, sliders,
534 XtNfromHoriz, treble.formw, NULL);
535 line.labelw = MW ("line_label", labelWidgetClass, line.formw,
536 XtNlabel, "Line", NULL);
537 line.leftw = MW ("line_l", scrollbarWidgetClass, line.formw, SCROLLBAR_RES,
538 XtNfromVert, line.labelw, NULL);
539 line.rightw = MW ("line_r", scrollbarWidgetClass, line.formw, SCROLLBAR_RES,
540 XtNfromHoriz, line.leftw, XtNfromVert, line.labelw, NULL);
541 line.lockw = MW ("line_lock", commandWidgetClass, line.formw,
542 XtNfromVert, line.leftw, NULL);
543
544 dsp.mixer_id = SOUND_MIXER_PCM;
545 dsp.formw = MW ("dsp_form", formWidgetClass, sliders,
546 longway ? XtNfromHoriz : XtNfromVert,
547 longway ? line.formw : master.formw,
548 NULL);
549 dsp.labelw = MW ("dsp_label", labelWidgetClass, dsp.formw,
550 XtNlabel, "DSP", NULL);
551 dsp.leftw = MW ("dsp_l", scrollbarWidgetClass, dsp.formw, SCROLLBAR_RES,
552 XtNfromVert, dsp.labelw, NULL);
553 dsp.rightw = MW ("dsp_r", scrollbarWidgetClass, dsp.formw, SCROLLBAR_RES,
554 XtNfromVert, dsp.labelw,
555 XtNfromHoriz, dsp.leftw, NULL);
556 dsp.lockw = MW ("dsp_lock", commandWidgetClass, dsp.formw,
557 XtNfromVert, dsp.leftw, NULL);
558
559 fm.mixer_id = SOUND_MIXER_SYNTH;
560 fm.formw = MW ("fm_form", formWidgetClass, sliders,
561 XtNfromHoriz, dsp.formw,
562 longway ? NULL : XtNfromVert, bass.formw, NULL);
563 fm.labelw = MW ("fm_label", labelWidgetClass, fm.formw,
564 XtNlabel, "FM", NULL);
565 fm.leftw = MW ("fm_l", scrollbarWidgetClass, fm.formw, SCROLLBAR_RES,
566 XtNfromVert, fm.labelw, NULL);
567 fm.rightw = MW ("fm_r", scrollbarWidgetClass, fm.formw, SCROLLBAR_RES,
568 XtNfromVert, fm.labelw,
569 XtNfromHoriz, fm.leftw, NULL);
570 fm.lockw = MW ("fm_lock", commandWidgetClass, fm.formw,
571 XtNfromVert, fm.leftw, NULL);
572
573 cd.mixer_id = SOUND_MIXER_CD;
574 cd.formw = MW ("cd_form", formWidgetClass, sliders,
575 XtNfromHoriz, fm.formw,
576 longway ? NULL : XtNfromVert, treble.formw, NULL);
577 cd.labelw = MW ("cd_label", labelWidgetClass, cd.formw,
578 XtNlabel, "CD", NULL);
579 cd.leftw = MW ("cd_l", scrollbarWidgetClass, cd.formw, SCROLLBAR_RES,
580 XtNfromVert, cd.labelw, NULL);
581 cd.rightw = MW ("cd_r", scrollbarWidgetClass, cd.formw, SCROLLBAR_RES,
582 XtNfromVert, cd.labelw, XtNfromHoriz, cd.leftw, NULL);
583 cd.lockw = MW ("cd_lock", commandWidgetClass, cd.formw,
584 XtNfromVert, cd.leftw, NULL);
585
586 mic.mixer_id = SOUND_MIXER_MIC;
587 mic.formw = MW ("mic_form", formWidgetClass, sliders,
588 XtNfromHoriz, cd.formw,
589 longway ? NULL : XtNfromVert, line.formw, NULL);
590 mic.labelw = MW ("mic_label", labelWidgetClass, mic.formw,
591 XtNlabel, "Mic", NULL);
592 mic.leftw = MW ("mic_level", scrollbarWidgetClass, mic.formw, SCROLLBAR_RES,
593 XtNfromVert, mic.labelw, NULL);
594 mic.rightw = NULL;
595 mic.lockw = NULL;
596
597 reclvl.mixer_id = SOUND_MIXER_RECLEV;
598 reclvl.formw = MW ("reclvl_form", formWidgetClass, sliders,
599 XtNfromHoriz, mic.formw,
600 longway ? NULL : XtNfromVert, line.formw, NULL);
601 reclvl.labelw = MW ("reclvl_label", labelWidgetClass, reclvl.formw,
602 XtNlabel, "Reclvl", NULL);
603 reclvl.leftw = MW ("reclvl_level", scrollbarWidgetClass, reclvl.formw,
604 SCROLLBAR_RES, XtNfromVert, reclvl.labelw, NULL);
605 reclvl.rightw = NULL;
606 reclvl.lockw = NULL;
607
608 buttons = MW ("buttons", formWidgetClass, whole,
609 XtNfromHoriz, sliders, NULL);
610
611
612 version = MW ("Version", labelWidgetClass, buttons,
613 XtNlabel, "XMix V2.1", NULL);
614
615 sources = MW ("sources", formWidgetClass, buttons,
616 XtNfromVert, version, NULL);
617 source_label = MW ("source_label", labelWidgetClass, sources,
618 XtNlabel, "Recording Source", NULL);
619 line_src = MW ("line_src", commandWidgetClass, sources,
620 XtNfromVert, source_label, NULL);
621 line_src_label = MW ("line_src_label", labelWidgetClass, sources,
622 XtNlabel, "Line In",
623 XtNfromVert, source_label,
624 XtNfromHoriz, line_src, NULL);
625 mic_src = MW ("mic_src", commandWidgetClass, sources,
626 XtNfromVert, line_src, NULL);
627 mic_src_label = MW ("mic_src_label", labelWidgetClass, sources,
628 XtNlabel, "Microphone",
629 XtNfromVert, line_src,
630 XtNfromHoriz, mic_src, NULL);
631 cd_src = MW ("cd_src", commandWidgetClass, sources,
632 XtNfromVert, mic_src, NULL);
633 cd_src_label = MW ("cd_src_label", labelWidgetClass, sources,
634 XtNlabel, "CD",
635 XtNfromVert, mic_src,
636 XtNfromHoriz, cd_src, NULL);
637 quit = MW ("quit", commandWidgetClass, buttons,
638 XtNfromVert, sources,
639 XtNlabel, "Quit",
640 XtNwidth, 50,
641 XtNheight, 20,
642 NULL);
643
644
645 XtVaGetValues (mic.leftw, XtNwidth, ¢ering_width, NULL);
646 CENTER (mic.labelw);
647 XtVaGetValues (reclvl.leftw, XtNwidth, ¢ering_width, NULL);
648 CENTER (reclvl.labelw);
649 XtVaGetValues (master.formw, XtNhorizDistance, &scroll_sep, NULL);
650 XtVaGetValues (master.leftw, XtNwidth, ¢ering_width, NULL);
651 centering_width = centering_width * 2 + scroll_sep;
652 CENTER (master.labelw);
653 CENTER (bass.labelw);
654 CENTER (treble.labelw);
655 CENTER (line.labelw);
656 CENTER (dsp.labelw);
657 CENTER (fm.labelw);
658 CENTER (cd.labelw);
659
660 XtAddEventHandler (topLevel, EnterWindowMask, FALSE,
661 (XtEventHandler) sync_display, NULL);
662 XtAddCallback (quit, XtNcallback, Quit, 0 /* client_data */ );
663 XtAddCallback (master.leftw, XtNjumpProc, Handle_slider, &master);
664 XtAddCallback (master.leftw, XtNscrollProc, Handle_slam_slider, &master);
665 XtAddCallback (master.rightw, XtNjumpProc, Handle_slider, &master);
666 XtAddCallback (master.rightw, XtNscrollProc, Handle_slam_slider, &master);
667 XtAddCallback (master.lockw, XtNcallback, Handle_lock, &master);
668 XtAddCallback (bass.leftw, XtNjumpProc, Handle_slider, &bass);
669 XtAddCallback (bass.leftw, XtNscrollProc, Handle_slam_slider, &bass);
670 XtAddCallback (bass.rightw, XtNjumpProc, Handle_slider, &bass);
671 XtAddCallback (bass.rightw, XtNscrollProc, Handle_slam_slider, &bass);
672 XtAddCallback (bass.lockw, XtNcallback, Handle_lock, &bass);
673 XtAddCallback (treble.leftw, XtNjumpProc, Handle_slider, &treble);
674 XtAddCallback (treble.leftw, XtNscrollProc, Handle_slam_slider, &treble);
675 XtAddCallback (treble.rightw, XtNjumpProc, Handle_slider, &treble);
676 XtAddCallback (treble.rightw, XtNscrollProc, Handle_slam_slider, &treble);
677 XtAddCallback (treble.lockw, XtNcallback, Handle_lock, &treble);
678 XtAddCallback (line.leftw, XtNjumpProc, Handle_slider, &line);
679 XtAddCallback (line.leftw, XtNscrollProc, Handle_slam_slider, &line);
680 XtAddCallback (line.rightw, XtNjumpProc, Handle_slider, &line);
681 XtAddCallback (line.rightw, XtNscrollProc, Handle_slam_slider, &line);
682 XtAddCallback (line.lockw, XtNcallback, Handle_lock, &line);
683 XtAddCallback (dsp.leftw, XtNjumpProc, Handle_slider, &dsp);
684 XtAddCallback (dsp.leftw, XtNscrollProc, Handle_slam_slider, &dsp);
685 XtAddCallback (dsp.rightw, XtNjumpProc, Handle_slider, &dsp);
686 XtAddCallback (dsp.rightw, XtNscrollProc, Handle_slam_slider, &dsp);
687 XtAddCallback (dsp.lockw, XtNcallback, Handle_lock, &dsp);
688 XtAddCallback (fm.leftw, XtNjumpProc, Handle_slider, &fm);
689 XtAddCallback (fm.leftw, XtNscrollProc, Handle_slam_slider, &fm);
690 XtAddCallback (fm.rightw, XtNjumpProc, Handle_slider, &fm);
691 XtAddCallback (fm.rightw, XtNscrollProc, Handle_slam_slider, &fm);
692 XtAddCallback (fm.lockw, XtNcallback, Handle_lock, &fm);
693 XtAddCallback (cd.leftw, XtNjumpProc, Handle_slider, &cd);
694 XtAddCallback (cd.leftw, XtNscrollProc, Handle_slam_slider, &cd);
695 XtAddCallback (cd.rightw, XtNjumpProc, Handle_slider, &cd);
696 XtAddCallback (cd.rightw, XtNscrollProc, Handle_slam_slider, &cd);
697 XtAddCallback (cd.lockw, XtNcallback, Handle_lock, &cd);
698 XtAddCallback (mic.leftw, XtNjumpProc, Handle_slider, &mic);
699 XtAddCallback (mic.leftw, XtNscrollProc, Handle_slam_slider, &mic);
700 XtAddCallback (reclvl.leftw, XtNjumpProc, Handle_slider, &reclvl);
701 XtAddCallback (reclvl.leftw, XtNscrollProc, Handle_slam_slider, &reclvl);
702
703 XtAddCallback (line_src, XtNcallback, Handle_source,
704 (XtPointer)SOUND_MIXER_LINE);
705 XtAddCallback (mic_src, XtNcallback, Handle_source,
706 (XtPointer)SOUND_MIXER_MIC);
707 XtAddCallback (cd_src, XtNcallback, Handle_source,
708 (XtPointer)SOUND_MIXER_CD);
709
710
711 install_pixmaps ();
712
713 /* Open the mixer device */
714 mixer_fd = open (mixer_name, O_RDWR, 0);
715 if (mixer_fd < 0) {
716 char msg[32];
717 sprintf(msg, "Error opening mixer device %s", mixer_name);
718 perror (msg), exit (1);
719 }
720
721
722 if (ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &supported) == -1)
723 supported = 0xffff; /* Assume all are supported */
724
725 /*
726 * Match the display settings to the current mixer configuration.
727 */
728 sync_display();
729
730 /*
731 * Pick some reasonable lock settings to start with.
732 * Two equal volume levels start off with that pair linked.
733 */
734
735 master.locked = (master.volume.left == master.volume.right);
736 bass.locked = (bass.volume.left == bass.volume.right);
737 treble.locked = (treble.volume.left == treble.volume.right);
738 line.locked = (line.volume.left == line.volume.right);
739 dsp.locked = (dsp.volume.left == dsp.volume.right);
740 fm.locked = (fm.volume.left == fm.volume.right);
741 cd.locked = (cd.volume.left == cd.volume.right);
742 mic.locked = 0;
743 reclvl.locked = 0;
744
745 printf("supported = 0x%x\n",supported);
746 set_supported(&master,(supported & SOUND_MASK_VOLUME) != 0);
747 set_supported(&bass,(supported & SOUND_MASK_BASS) != 0);
748 set_supported(&treble,(supported & SOUND_MASK_TREBLE) != 0);
749 set_supported(&line,(supported & SOUND_MASK_LINE) != 0);
750 set_supported(&dsp,(supported & SOUND_MASK_PCM) != 0);
751 set_supported(&fm,(supported & SOUND_MASK_SYNTH) != 0);
752 set_supported(&cd,(supported & SOUND_MASK_CD) != 0);
753 set_supported(&mic,(supported & SOUND_MASK_MIC) != 0);
754 set_supported(&reclvl,(supported & SOUND_MASK_RECLEV) != 0);
755
756 /*
757 * Update the lock bitmaps to reflect linking
758 */
759 sync_lock(&master);
760 sync_lock(&bass);
761 sync_lock(&treble);
762 sync_lock(&line);
763 sync_lock(&dsp);
764 sync_lock(&fm);
765 sync_lock(&cd);
766
767 XtRealizeWidget (topLevel); /* Action! */
768 XtAppMainLoop (app_context); /* Loop for events */
769 }
770