1 /*
2  * xmixer:gui_xaw.c
3  *
4  * Copyright (C) 1997-98 Rasca, Berlin
5  * EMail: thron@gmx.de
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 #include <stdio.h>
23 #include <stdlib.h>
24 #include <limits.h>
25 #include <X11/Intrinsic.h>
26 #include <X11/StringDefs.h>
27 #define XK_MISCELLANY
28 #define XK_XKB_KEYS
29 #include <X11/keysymdef.h>
30 #include <X11/Shell.h>
31 #include <X11/Xaw/Box.h>
32 #include <X11/Xaw/Panner.h>
33 /**/
34 #include <X11/Xaw/SimpleMenu.h>
35 #include <X11/Xaw/SmeBSB.h>
36 /**/
37 #include <X11/Xaw/Paned.h>
38 #include <X11/Xaw/Form.h>
39 /**/
40 #include <Xw/Button.h>
41 #include <Xw/Label.h>
42 #include <Xw/Toggle.h>
43 /**/
44 #include "gui.h"
45 #include "about.h"
46 #include "mixer.h"
47 #include "input.xbm"
48 #include "norec.xbm"
49 #include "locked.xbm"
50 #include "unlocked.xbm"
51 #include "mute.xbm"
52 #include "icon.xbm"
53 #include "ok.xbm"
54 #include "none.xbm"
55 #include "fallback.h"
56 #include "scf.h"
57 
58 #define LEFT	0
59 #define RIGHT	1
60 
61 typedef struct {
62 	int dev;
63 	int locked;
64 	int max;
65 	int mute;
66 	Widget box;
67 	Widget sll;
68 	Widget slr;
69 	Widget vol;
70 	Widget lck;
71 	Widget rec;
72 	Widget lbl;
73 	Widget mut;
74 	Pixmap rec_bm;
75 	Pixmap norec_bm;
76 	Pixmap lck_bm;
77 	Pixmap unlck_bm;
78 	Pixmap none_bm;
79 	Pixmap mute_bm;
80 	XColor active;
81 } wMixer;
82 
83 typedef struct {
84 
85 	/* resources
86 	 */
87 	char *dev_name;
88 	int update_time;
89 	char *help_command;
90 	Pixel focus_color;
91 
92 	/* private
93 	 */
94 	char *program;
95 	wMixer *devs;
96 	XtAppContext context;
97 	Widget toplevel;
98 	Widget menu_box;
99 	Widget label;
100 	WidgetList mDev;
101 	Pixmap checked;
102 } AppData;
103 
104 #define XtNdeviceName	"deviceName"
105 #define XtCDeviceName	"DeviceName"
106 #define XtNupdateTime	"updateTime"
107 #define XtCUpdateTime	"UpdateTime"
108 #define XtNhelpCommand	"helpCommand"
109 #define XtCHelpCommand	"HelpCommand"
110 #define XtNfocusColor	"focusColor"
111 #define XtCFocusColor	"FocusColor"
112 
113 /* some globals
114  */
115 
116 static XtResource resources [] = {
117 	{
118 		XtNdeviceName,
119 		XtCDeviceName,
120 		XtRString,
121 		sizeof(String),
122 		XtOffsetOf(AppData, dev_name),
123 		XtRString,
124 		DEFAULT_MIXER
125 	},
126 	{
127 		XtNupdateTime,
128 		XtCUpdateTime,
129 		XtRInt,
130 		sizeof(int),
131 		XtOffsetOf(AppData, update_time),
132 		XtRImmediate,
133 		(XtPointer)60
134 	},
135 	{
136 		XtNhelpCommand,
137 		XtCHelpCommand,
138 		XtRString,
139 		sizeof(String),
140 		XtOffsetOf(AppData, help_command),
141 		XtRImmediate,
142 		(XtPointer)NULL
143 	},
144 	{
145 		XtNfocusColor,
146 		XtCFocusColor,
147 		XtRPixel,
148 		sizeof (Pixel),
149 		XtOffsetOf(AppData, focus_color),
150 		XtRImmediate,
151 		(XtPointer)NULL
152 	}
153 };
154 
155 static AppData App;
156 
157 #define Toplevel()	(App.toplevel)
158 
159 /* actions definitions
160  */
161 static void ExitProgAction (Widget, XEvent*, String*, Cardinal*);
162 static void ReleaseMenuAction (Widget, XEvent*, String*, Cardinal*);
163 static void ActivateMenuAction (Widget, XEvent*, String*, Cardinal*);
164 static void ShowMenuAction (Widget, XEvent*, String*, Cardinal*);
165 static void SetFocusAction (Widget, XEvent*, String*, Cardinal*);
166 static void CloseWinAction (Widget, XEvent*, String*, Cardinal*);
167 
168 static XtActionsRec actionTable[] = {
169 		{ "exit-program",	ExitProgAction },
170 		{ "release-menu",	ReleaseMenuAction },
171 		{ "activate-menu",	ActivateMenuAction },
172 		{ "show-menu",		ShowMenuAction },
173 		{ "close-window",	CloseWinAction },
174 		{ "set-focus",		SetFocusAction },
175 	};
176 
177 /* protos
178  */
179 void CbChangeLabel (Widget w, XtPointer client_data, XtPointer call_data);
180 static void RegisterWidget (Widget);
181 static Widget NameToWidget (String);
182 
183 /* actions */
184 
185 /*
186  * exit xmixer
187  */
188 static void
ExitProgAction(Widget w,XEvent * ev,String * parms,Cardinal * num_parms)189 ExitProgAction (Widget w, XEvent *ev, String *parms, Cardinal *num_parms)
190 {
191 	printf ("Program canceld!\n");
192 	exit(0);
193 }
194 
195 /*
196  * release the focus from the menu bar
197  */
198 static void
ReleaseMenuAction(Widget w,XEvent * ev,String * parms,Cardinal * num_parms)199 ReleaseMenuAction (Widget w, XEvent *ev, String *parms, Cardinal *num_parms)
200 {
201 #ifdef DEBUG
202 	printf ("release-menu()\n");
203 #endif
204 }
205 
206 /*
207  * set the focus from the menu bar
208  */
209 static void
ActivateMenuAction(Widget w,XEvent * ev,String * parms,Cardinal * num_parms)210 ActivateMenuAction (Widget w, XEvent *ev, String *parms, Cardinal *num_parms)
211 {
212 #ifdef DEBUG
213 	printf ("activate-menu()\n");
214 #endif
215 	if (*num_parms) {
216 		Widget first;
217 		first = NameToWidget (parms[0]);
218 		if (first) {
219 			XtSetKeyboardFocus (Toplevel(), first);
220 		}
221 	}
222 }
223 
224 /*
225  */
226 static void
ShowMenuAction(Widget w,XEvent * ev,String * parms,Cardinal * num_parms)227 ShowMenuAction (Widget w, XEvent *ev, String *parms, Cardinal *num_parms)
228 {
229 	Dimension height, bheight;
230 #ifdef DEBUG
231 	printf ("show-menu()\n");
232 #endif
233 	if (XtIsManaged(App.menu_box))
234 		return;
235 	XtVaGetValues (XtParent(App.menu_box), XtNheight, &height, NULL);
236 	XtVaGetValues (App.menu_box, XtNheight, &bheight, NULL);
237 	XtManageChild (App.menu_box);
238 	XtVaSetValues (Toplevel(), XtNheight, height + bheight, NULL);
239 }
240 
241 /*
242  */
243 static void
SetFocusAction(Widget w,XEvent * ev,String * parms,Cardinal * num_parms)244 SetFocusAction (Widget w, XEvent *ev, String *parms, Cardinal *num_parms)
245 {
246 #ifdef DEBUG
247 	printf ("set-focus()\n");
248 #endif
249 	if (*num_parms == 2) {
250 		Widget widget;
251 		Widget tree;
252 		tree = NameToWidget (parms[0]);
253 		widget = NameToWidget (parms[1]);
254 		if (tree && widget) {
255 #ifdef DEBUG
256 			printf ("set-focus(%s, %s)\n", XtName(tree), XtName(widget));
257 #endif
258 			XtSetKeyboardFocus (tree, widget);
259 		}
260 	}
261 }
262 
263 /*
264  */
265 static void
update_label(wMixer * m)266 update_label (wMixer *m)
267 {
268 	int vol_left, vol_right = 0;
269 	static char buff[32];
270 
271 	vol_left = mixer_get_vol_left (1, m->dev);
272 	if (mixer_is_stereo (1, m->dev)) {
273 		vol_right = mixer_get_vol_right (1, m->dev);
274 		sprintf (buff, "%3d:%3d", vol_left, vol_right);
275 	} else {
276 		sprintf (buff, "%3d    ", vol_left);
277 	}
278 	XtVaSetValues (App.label, XtNlabel, buff, NULL);
279 }
280 
281 /*
282  * close the named window
283  */
284 static void
CloseWinAction(Widget w,XEvent * ev,String * parms,Cardinal * num_parms)285 CloseWinAction (Widget w, XEvent *ev, String *parms, Cardinal *num_parms)
286 {
287 	if (*num_parms) {
288 		Widget win;
289 		win = XtNameToWidget (Toplevel(), parms[0]);
290 		if (win)
291 			XtDestroyWidget (win);
292 	}
293 }
294 
295 
296 /* callbacks */
297 /*
298  */
299 static void
CbExec(Widget w,XtPointer client_data,XtPointer call_data)300 CbExec (Widget w, XtPointer client_data, XtPointer call_data)
301 {
302 	char *exec;
303 
304 	exec = (char *) client_data;
305 	system (exec);
306 }
307 
308 /*
309  */
310 static void
CbQuit(Widget w,XtPointer client_data,XtPointer call_data)311 CbQuit (Widget w, XtPointer client_data, XtPointer call_data)
312 {
313 	exit (0);
314 }
315 
316 /*
317  * close the named popup shell
318  */
319 static void
CbClose(Widget w,XtPointer client_data,XtPointer call_data)320 CbClose (Widget w, XtPointer client_data, XtPointer call_data)
321 {
322 	XtDestroyWidget ((Widget) client_data);
323 }
324 
325 /*
326  */
327 static void
CbMute(Widget w,XtPointer client_data,XtPointer call_data)328 CbMute (Widget w, XtPointer client_data, XtPointer call_data)
329 {
330 	Position y;
331 	wMixer *dev = (wMixer *)client_data;
332 	XColor color;
333 
334 	if (!XwIsSet(w)) {
335 		dev->mute = 1;
336 		mixer_set_vol_left (1, dev->dev, 0);
337 		if (mixer_is_stereo (1, dev->dev))
338 			mixer_set_vol_right (1, dev->dev, 0);
339 		XtVaGetValues (w, XtNbackground, &color, NULL);
340 	} else {
341 		dev->mute = 0;
342 		XtVaGetValues (dev->sll, XtNsliderY, &y, NULL);
343 		mixer_set_vol_left (1, dev->dev, 100 - y);
344 		if (mixer_is_stereo (1, dev->dev)) {
345 			XtVaGetValues (dev->slr, XtNsliderY, &y, NULL);
346 			mixer_set_vol_right (1, dev->dev, 100 - y);
347 		}
348 		color = dev->active;
349 	}
350 	XtVaSetValues (w, XtNforeground, color, NULL);
351 	update_label (dev);
352 }
353 
354 /*
355  * map or unmap a slider box and adjust the width of the toplevel
356  */
357 static void
CbMap(Widget menu_entry,XtPointer client_data,XtPointer call_data)358 CbMap (Widget menu_entry, XtPointer client_data, XtPointer call_data)
359 {
360 	Widget box = (Widget) client_data;
361 	Dimension width = 0, dev_width = 0, border_width = 0, space = 0;
362 	Dimension height = 0, dev_height;
363 	int change_menu_box = 0;
364 
365 	char *name = XtName (box);
366 	if (strcmp (name, "menu_box") == 0) {
367 		change_menu_box = 1;
368 	}
369 
370 	XtVaGetValues (box, XtNwidth, &dev_width, XtNheight, &dev_height,
371 					XtNborderWidth, &border_width, NULL);
372 	XtVaGetValues (XtParent(box), XtNhSpace, &space, XtNheight, &height, NULL);
373 	XtVaGetValues (Toplevel(), XtNwidth, &width, NULL);
374 	if (XtIsManaged (box)) {
375 		XtUnmanageChild (box);
376 		if (!change_menu_box) {
377 			XtVaSetValues (menu_entry, XtNleftBitmap, None, NULL);
378 			XtVaSetValues (Toplevel(),
379 						XtNwidth, width - dev_width - space - 2 * border_width,
380 						NULL);
381 		} else {
382 			/* disable menu */
383 			XtVaSetValues (Toplevel(),
384 						XtNheight, height - dev_height,
385 						NULL);
386 		}
387 	} else {
388 		XtManageChild (box);
389 		XtVaSetValues (menu_entry, XtNleftBitmap, App.checked, NULL);
390 		XtVaSetValues (Toplevel(),
391 						XtNwidth, width + dev_width + space + 2 * border_width,
392 						NULL);
393 	}
394 }
395 
396 /*
397  * event handler to close the application or the window if window manager
398  * sends a WM_DELETE_WINDOW message
399  */
400 void
WmDelEH(Widget shell,XtPointer xtp,XEvent * event,Boolean * boo)401 WmDelEH (Widget shell, XtPointer xtp, XEvent *event, Boolean *boo)
402 {
403 #ifdef DEBUG
404 	fprintf (stderr, "WmDelEH() event type=%d format=%d\n", event->type,
405 				event->type == ClientMessage ? event->xclient.format : -1);
406 #endif
407 	if (event->type != ClientMessage) /* =33 */
408 		return;
409 	if (event->xclient.format != 32)
410 		return;
411 	if (event->xclient.data.l[0] != *((Atom*)xtp))
412 		return;
413 
414 	if (!XtParent (shell)) {
415 		/* destroy the toplevel widget
416 		 * and terminate the application */
417 		XtDestroyWidget (shell);
418 		exit (0);
419 	} else {
420 		XtDestroyWidget (shell);
421 	}
422 }
423 
424 /*
425  * about popup
426  */
427 static void
CbAbout(Widget w,XtPointer client_data,XtPointer call_data)428 CbAbout (Widget w, XtPointer client_data, XtPointer call_data)
429 {
430 	Widget toplevel, shell, box, esc, label;
431 	Position x, y;
432 	static Atom wm_delete;
433 
434 	toplevel = Toplevel();
435 	wm_delete = (Atom)client_data;
436 	shell= XtVaCreatePopupShell ("about", transientShellWidgetClass, toplevel,
437 			NULL);
438 	box = XtVaCreateManagedWidget ("about_box", boxWidgetClass, shell,
439 			NULL);
440 	label= XtVaCreateManagedWidget ("about_label", xwLabelWidgetClass, box,
441 			XtNlabel, _ABOUT_,
442 			NULL);
443 	esc = XtVaCreateManagedWidget ("Ok", xwButtonWidgetClass, box,
444 			NULL);
445 	XtAddCallback (esc, XtNcallback, CbClose, shell);
446 	XtTranslateCoords (toplevel, 0, 0, &x ,&y);
447 	XtVaSetValues (shell, XtNx, x+25, XtNy, y+25, NULL);
448 	XtPopup (shell, XtGrabNonexclusive);
449 	XtSetKeyboardFocus (box, esc);
450 	XSetWMProtocols (XtDisplay(shell), XtWindow(shell), &wm_delete, 1);
451 	XtAddEventHandler (shell, NoEventMask, True, WmDelEH, &wm_delete);
452 }
453 
454 
455 /*
456  * save current settings in ~/.xmixer.scf
457  */
458 static void
CbSave(Widget w,XtPointer client_data,XtPointer call_data)459 CbSave (Widget w, XtPointer client_data, XtPointer call_data)
460 {
461 	AppData *ad = (AppData *) client_data;
462 	char *pname = ad->program;
463 	const char *fname, *name, *label;
464 	FILE *fp;
465 	int i, nod;
466 
467 	fname = scf_file_name (pname, SCF_PRIVAT);
468 	if (fname) {
469 #ifdef DEBUG
470 		fprintf (stderr, "CbSave() %s\n", fname);
471 #endif
472 		nod = ad->devs[0].max;
473 		fp = fopen (fname, "wb");
474 		if (fp) {
475 			fprintf (fp, "#SCFF 1.0\n");
476 			fprintf (fp, "# version of the xmixer configuration file\n");
477 			fprintf (fp, "version = 1\n\n");
478 			fprintf (fp, "# mixer device name\n");
479 			fprintf (fp, "[%s]\n\n", ad->dev_name);
480 			for (i = 0; i < nod; i++) {
481 				name = mixer_get_name(1, i);
482 				fprintf (fp, "# Settings for the \"%s\"-device = ", name);
483 				if (mixer_is_stereo(1, i))
484 					fprintf (fp, "{ Int[0-100]:leftValue Int[0-100]:rightValue }\n");
485 				else
486 					fprintf (fp, "{ Int[0-100]:value }\n");
487 
488 				fprintf (fp, "%s = { ", mixer_get_name(1, i));
489 				if (mixer_is_stereo(1, i)) {
490 					fprintf (fp, "%d %d }\n", mixer_get_vol_left(1,i), mixer_get_vol_right(1,i));
491 				} else {
492 					fprintf (fp, "%d }\n", mixer_get_vol_left(1,i));
493 				}
494 				fprintf (fp, "# show slider for device \"%s\" = Bool[True, False]:Map\n", name);
495 				if (XtIsManaged(ad->devs[i].box))
496 					fprintf (fp, "%s_map = True\n", name);
497 				else
498 					fprintf (fp, "%s_map = False\n", name);
499 				label = NULL;
500 				XtVaGetValues (ad->devs[i].lbl, XtNlabel, &label, NULL);
501 				if (label) {
502 					fprintf (fp,"# label of the device \"%s\" = String:Label\n",
503 							name);
504 					fprintf (fp,"%s_label = \"%s\"\n\n", name, label);
505 				}
506 			}
507 			fclose (fp);
508 		}
509 	}
510 }
511 
512 
513 
514 /*
515  */
516 static void
CbPanL(Widget w,XtPointer client_data,XtPointer call_data)517 CbPanL (Widget w, XtPointer client_data, XtPointer call_data)
518 {
519 	wMixer *m;
520 	int y, vol;
521 
522 	m = (wMixer *) client_data;
523 	y = (int) ((XawPannerReport *)call_data)->slider_y;
524 	vol= 100 - y;
525 	if (!m->mute)
526 		vol = mixer_set_vol_left (1, m->dev, vol);
527 #ifdef DEBUG
528 	fprintf (stderr, "CbPanL() %d vol=%d\n", m->dev, vol);
529 #endif
530 	if (m->locked && mixer_is_stereo (1, m->dev)) {
531 		XtVaSetValues (m->slr, XtNsliderY, y, NULL);
532 		if (!m->mute)
533 			mixer_set_vol_right (1, m->dev, vol);
534 	}
535 	update_label (m);
536 }
537 
538 /*
539  */
540 static void
CbPanR(Widget w,XtPointer client_data,XtPointer call_data)541 CbPanR (Widget w, XtPointer client_data, XtPointer call_data)
542 {
543 	wMixer *m;
544 	int y, vol;
545 
546 	m = (wMixer *) client_data;
547 	y = (int) ((XawPannerReport *)call_data)->slider_y;
548 	vol=100 - y;
549 #ifdef DEBUG
550 	fprintf (stderr, "CbPanR() %d vol=%d\n", m->dev, vol);
551 #endif
552 	if (!m->mute)
553 		mixer_set_vol_right (1, m->dev, vol);
554 	if (m->locked) {
555 		XtVaSetValues (m->sll, XtNsliderY, y, NULL);
556 		if (!m->mute)
557 			mixer_set_vol_left (1, m->dev, vol);
558 	}
559 	update_label (m);
560 }
561 
562 
563 /*
564  */
565 static void
CbLock(Widget w,XtPointer client_data,XtPointer call_data)566 CbLock (Widget w, XtPointer client_data, XtPointer call_data)
567 {
568 	int state;
569 	wMixer *m;
570 
571 	state = XwIsSet(w);
572 	m = (wMixer *) client_data;
573 
574 	if (!mixer_is_stereo (1, m->dev)) {
575 		XtVaSetValues (w, XtNstate, True, NULL);
576 		return;
577 	}
578 	if (state) {
579 		XtVaSetValues (w, XtNbitmap, m->lck_bm, NULL);
580 		m->locked = 1;
581 	} else {
582 		XtVaSetValues (w, XtNbitmap, m->unlck_bm, NULL);
583 		m->locked = 0;
584 	}
585 }
586 
587 /*
588  */
589 static void
CbRec(Widget w,XtPointer client_data,XtPointer call_data)590 CbRec (Widget w, XtPointer client_data, XtPointer call_data)
591 {
592 	int i;
593 	wMixer *devs;
594 
595 	devs = (wMixer *) client_data;
596 
597 	if (XwIsSet(w)) {
598 		for (i = 0; i < devs[0].max; i++) {
599 			if (mixer_is_rec_dev (1, i)) {
600 				if (w == devs[i].rec) {
601 					mixer_set_rec (1, i, 1);
602 					XtVaSetValues (w, XtNbitmap, devs[i].rec_bm, NULL);
603 				} else {
604 					if (mixer_exclusive_input (1)) {
605 						mixer_set_rec (1, i, 0);
606 						XtVaSetValues (devs[i].rec, XtNstate, False,
607 							XtNbitmap, devs[i].norec_bm, NULL);
608 					}
609 				}
610 			}
611 		}
612 	} else {
613 		for (i = 0; i < devs[0].max; i++) {
614 			if (w == devs[i].rec) {
615 				mixer_set_rec (1, i, 0);
616 				XtVaSetValues (w, XtNbitmap, devs[i].norec_bm, NULL);
617 				break;
618 			}
619 		}
620 	}
621 }
622 
623 /*
624  * update the values, some other programs may have
625  * changed them..
626  */
627 static void
TCbUpdate(XtPointer client_data,XtIntervalId id)628 TCbUpdate (XtPointer client_data, XtIntervalId id)
629 {
630 	int no_of_devs, i, vol;
631 	wMixer *devs;
632 
633 	devs = (wMixer *) client_data;
634 	no_of_devs = mixer_num_of_devs (1);
635 	for (i = 0; i < no_of_devs; i++) {
636 		vol = mixer_get_vol_left (1, i);
637 		if (devs[i].mute) {
638 			continue;
639 		}
640 		if (mixer_is_stereo (1, i))  {
641 			XtVaSetValues (devs[i].sll, XtNsliderY, 100-vol, NULL);
642 			vol = mixer_get_vol_right (1, i);
643 			XtVaSetValues (devs[i].slr, XtNsliderY, 100-vol, NULL);
644 #ifdef DEBUG2
645 			fprintf (stderr,
646 				"%s %d:%d\n", mixer_get_label(1,i), vol, mixer_get_vol_left(1,i));
647 #endif
648 		} else {
649 			XtVaSetValues (devs[i].sll, XtNsliderY, 100-vol, NULL);
650 #ifdef DEBUG2
651 			fprintf (stderr,
652 				"%s %d\n", mixer_get_label(1,i), vol);
653 #endif
654 		}
655 	}
656 	XtAppAddTimeOut (App.context, App.update_time * 1000,
657 			(XtTimerCallbackProc)TCbUpdate, devs);
658 }
659 
660 /*
661  * reload saved settings
662  */
663 static void
CbReload(Widget w,XtPointer client_data,XtPointer call_data)664 CbReload (Widget w, XtPointer client_data, XtPointer call_data)
665 {
666 	AppData *ad = (AppData *) client_data;
667 	scf_id id;
668 	int nod, i;
669 	string name;
670 	char key[32];
671 	int map;
672 	int left, right;
673 
674 	id = scf_init (ad->program, NULL);
675 	if (scf_read (id)) {
676 		nod = mixer_num_of_devs (1);
677 		for ( i = 0; i < nod; i++) {
678 			name = (const string) mixer_get_name (1, i);
679 			if (scf_get_array_int_val (id, ad->dev_name, name, 0, &left) == SCF_TRUE)
680 				mixer_set_vol_left (1, i, left);
681 			if (mixer_is_stereo (1, i)) {
682 				if (scf_get_array_int_val (id, ad->dev_name, name, 1, &right) == SCF_TRUE)
683 					mixer_set_vol_right (1,i, right);
684 			}
685 			sprintf (key, "%s_map", name);
686 			if (scf_get_bool_val (id, ad->dev_name, key, &map)) {
687 				if (map == SCF_FALSE) {
688 					if (XtIsManaged(ad->devs[i].box))
689 						CbMap (ad->mDev[i], ad->devs[i].box, NULL);
690 				} else
691 					if (!XtIsManaged(ad->devs[i].box))
692 						CbMap (ad->mDev[i], ad->devs[i].box, NULL);
693 			}
694 		}
695 		TCbUpdate ((XtPointer)ad->devs, 0);
696 	}
697 	scf_fini (id);
698 }
699 
700 
701 /*
702  */
703 void
focusEH(Widget slider,XtPointer subtree,XEvent * event,Boolean * boo)704 focusEH (Widget slider, XtPointer subtree, XEvent *event, Boolean *boo)
705 {
706 	static Widget prev = None;
707 	static Pixel pp;
708 	int i;
709 	Widget box;
710 	wMixer *m;
711 #ifdef DEBUG
712 	printf ("focus() widget=%s\n", XtName(slider));
713 #endif
714 	box = XtParent(slider);
715 	if (box != prev) {
716 		if (prev) {
717 			XtVaSetValues (prev, XtNborderColor, pp, NULL);
718 		}
719 		XtVaGetValues (box, XtNborderColor, &pp, NULL);
720 		prev = box;
721 		XtVaSetValues (box, XtNborderColor, App.focus_color, NULL);
722 	}
723 	XtSetKeyboardFocus (Toplevel(), slider);
724 	m = App.devs;
725 	for (i = 0; i < m->max; i++) {
726 		if (m[i].box == box) {
727 			update_label(&(m[i]));
728 		}
729 	}
730 }
731 
732 /*
733  */
734 static void
CbMenuTab(Widget w,XtPointer client,XtPointer call)735 CbMenuTab (Widget w, XtPointer client, XtPointer call)
736 {
737 #ifdef DEBUG
738 	printf ("CbMenuTab()\n");
739 #endif
740 	XtSetKeyboardFocus (Toplevel(), (Widget)client);
741 }
742 
743 /*
744  * not ready!
745  */
746 void
tabEH(Widget w,XtPointer xtp,XEvent * event,Boolean * boo)747 tabEH (Widget w, XtPointer xtp, XEvent *event, Boolean *boo)
748 {
749 	int len, dev_id, nod, i;
750 	KeySym ks;
751 	XKeyEvent *xkey;
752 	XComposeStatus cs;
753 	char key[8];
754 	Widget dev;
755 	AppData *ad = (AppData *) xtp;
756 
757 	xkey = &event->xkey;
758 	key[1] = '\0';
759 	len = XLookupString (xkey, key, 8, &ks, &cs);
760 	dev = XtParent(w);
761 #ifdef DEBUG
762 	fprintf (stderr, "tabEH() widget=%s box=%s keysym=%X\n", XtName(w), XtName(dev), (int)ks);
763 #endif
764 	if ((ks == XK_Tab) || (ks == XK_ISO_Left_Tab) ||
765 		(ks == XK_Left) || (ks == XK_Right)) {
766 		/*
767 		 * find next or the previous widget
768 		 */
769 		dev_id = atoi (XtName(dev));
770 		nod = mixer_num_of_devs (1);
771 		for (i = 0; i < nod; i++) {
772 			if (ks == XK_Tab || ks == XK_Right) {
773 				dev_id++;
774 				if (dev_id > nod-1)
775 					dev_id = 0;
776 			} else {
777 				dev_id--;
778 				if (dev_id < 0)
779 					dev_id = nod-1;
780 			}
781 			if (XtIsManaged (ad->devs[dev_id].box)) {
782 				focusEH (ad->devs[dev_id].sll, XtParent(ad->devs[dev_id].box), NULL, NULL);
783 				return;
784 			}
785 		}
786 	}
787 #ifdef DEBUG
788 	 else {
789 		printf ("tabEH() key sym=%X shift? %d\n", (int)ks, event->xkey.state & ShiftMask);
790 	}
791 #endif
792 }
793 
794 /*
795  */
796 void
mouseEH(Widget w,XtPointer xtp,XEvent * event,Boolean * boo)797 mouseEH (Widget w, XtPointer xtp, XEvent *event, Boolean *boo)
798 {
799 #	define offset 15
800 	KeySym ks;
801 	XComposeStatus cs;
802 	int x,y;
803 #ifdef DEBUG
804 	printf ("mouseEH()\n");
805 #endif
806 	x = y = 0;
807 	XLookupString (&(event->xkey), NULL, 0, &ks, &cs);
808 	switch (ks) {
809 		case XK_Right:
810 			x = offset;
811 			break;
812 		case XK_Left:
813 			x = -offset;
814 			break;
815 		case XK_Up:
816 			y = -offset;
817 			break;
818 		case XK_Down:
819 			y = offset;
820 			break;
821 		default:
822 			break;
823 	}
824 	XWarpPointer (XtDisplay(w), None, None, 0, 0, 0, 0, x, y);
825 }
826 
827 
828 /*
829  */
830 char *
gui_init(int * argc,char *** argv)831 gui_init (int *argc, char ***argv)
832 {
833 	App.toplevel = XtVaAppInitialize (&App.context, "XMixer", NULL, 0,
834 			argc, *argv, fallback, NULL);
835 	RegisterWidget (App.toplevel);
836 	XtVaGetApplicationResources (App.toplevel,
837 			&App,
838 			resources,
839 			XtNumber (resources),
840 			NULL);
841 	XtAppAddActions (App.context, actionTable, sizeof(actionTable)/sizeof(XtActionsRec));
842 	return ((char *)App.dev_name);
843 }
844 
845 
846 
847 /*
848  * gui main()
849  */
gui_main(char * mixer_dev,char * pname)850 int gui_main (char *mixer_dev, char *pname)
851 {
852 	Widget toplevel, frame, menu_box, quit, save, load, about, mixer;
853 	Widget file_mb, file_menu, help_mb, help_menu, man;
854 	Widget map_mb, map_menu, *menu_entries;
855 	Pixmap icon_bm;
856 	wMixer *devs;
857 	int i, vol, no_of_devs, map;
858 	scf_id scf;
859 	char key[32], wname[8];
860 	const char *name;
861 	static Atom wm_delete;
862 
863 	no_of_devs   = mixer_num_of_devs (1);
864 	App.program  = pname;
865 	App.dev_name = mixer_dev;
866 	toplevel = App.toplevel;
867 	wm_delete = XInternAtom (XtDisplay(toplevel), "WM_DELETE_WINDOW", False);
868 
869 	devs = (wMixer *) malloc (sizeof (wMixer) * no_of_devs);
870 	App.devs = devs;
871 
872 	icon_bm = XCreateBitmapFromData (XtDisplay(toplevel),
873 				RootWindowOfScreen (XtScreen(toplevel)),
874 				icon_bits, icon_width, icon_height);
875 
876 	XtVaSetValues (toplevel, XtNiconPixmap, icon_bm, NULL);
877 
878 	App.checked = XCreateBitmapFromData (XtDisplay(toplevel),
879 				RootWindowOfScreen (XtScreen(toplevel)),
880 				ok_bits, ok_width, ok_height);
881 
882 	/* the main application frame
883 	 */
884 	frame = XtVaCreateManagedWidget (
885 			"frame", panedWidgetClass, toplevel,
886 			NULL);
887 	RegisterWidget (frame);
888 
889 	App.menu_box = menu_box = XtVaCreateManagedWidget (
890 			"menu_box", boxWidgetClass, frame,
891 			XtNorientation, XtorientHorizontal,
892 			XtNshowGrip, False,
893 			NULL);
894 	RegisterWidget (menu_box);
895 
896 	/* menus */
897 	file_mb = XtVaCreateManagedWidget ( "file_mb", xwButtonWidgetClass,
898 				menu_box,
899 				XtNmenuName, "file_menu",
900 				NULL);
901 	RegisterWidget (file_mb);
902 	file_menu = XtVaCreatePopupShell (
903 			"file_menu", simpleMenuWidgetClass, file_mb,
904 			NULL);
905 	XtAddEventHandler (file_menu, KeyPressMask, False, mouseEH, NULL);
906 
907 	map_mb = XtVaCreateManagedWidget ( "map_mb", xwButtonWidgetClass,
908 				menu_box,
909 				XtNmenuName, "map_menu",
910 				NULL);
911 
912 	map_menu = XtVaCreatePopupShell ( "map_menu", simpleMenuWidgetClass,
913 				map_mb, NULL);
914 
915 	XtAddEventHandler (map_menu, KeyPressMask, False, mouseEH, NULL);
916 
917 	help_mb = XtVaCreateManagedWidget (
918 				"help_mb", xwButtonWidgetClass, menu_box,
919 				XtNmenuName, "help_menu",
920 				NULL);
921 	help_menu = XtVaCreatePopupShell (
922 				"help_menu", simpleMenuWidgetClass, help_mb,
923 				NULL);
924 	XtAddEventHandler (help_menu, KeyPressMask, False, mouseEH, NULL);
925 	App.label = XtVaCreateManagedWidget (
926 				"label", xwLabelWidgetClass, menu_box,
927 				XtNlabel, "???:???", NULL);
928 
929 	save  = XtVaCreateManagedWidget (
930 				"save", smeBSBObjectClass, file_menu, NULL);
931 	XtAddCallback (save, XtNcallback, CbSave, &App);
932 
933 	load  = XtVaCreateManagedWidget (
934 				"load", smeBSBObjectClass, file_menu, NULL);
935 	XtAddCallback (load, XtNcallback, CbReload, &App);
936 
937 	quit  = XtVaCreateManagedWidget (
938 				"quit", smeBSBObjectClass, file_menu, NULL);
939 	XtAddCallback (quit, XtNcallback, CbQuit, NULL);
940 
941 	about  = XtVaCreateManagedWidget (
942 				"about", smeBSBObjectClass, help_menu, NULL);
943 	XtAddCallback (about, XtNcallback, CbAbout, (XtPointer)wm_delete);
944 
945 	man = XtVaCreateManagedWidget (
946 				"man", smeBSBObjectClass, help_menu, NULL);
947 	XtAddCallback (man, XtNcallback, CbExec, App.help_command);
948 
949 	XtAddCallback (file_mb, XtNtabCallback, CbMenuTab, map_mb);
950 	XtAddCallback (map_mb, XtNtabCallback, CbMenuTab, help_mb);
951 	XtAddCallback (help_mb, XtNtabCallback, CbMenuTab, file_mb);
952 
953 	mixer = XtVaCreateManagedWidget (
954 				"mixer_box", boxWidgetClass, frame,
955 				XtNorientation, XtorientHorizontal,
956 				NULL);
957 
958 	App.mDev =
959 	menu_entries = (Widget *) malloc (sizeof(Widget) * (no_of_devs + 1));
960 	for (i = 0; i < no_of_devs; i++) {
961 		devs[i].dev = i;
962 		devs[i].max = no_of_devs;
963 
964 		sprintf (wname, "%d", i);
965 		devs[i].box = XtVaCreateManagedWidget (
966 					wname, formWidgetClass, mixer, NULL);
967 		RegisterWidget (devs[i].box);
968 
969 		devs[i].lbl = XtVaCreateManagedWidget (
970 				mixer_get_label (1, i), xwButtonWidgetClass, devs[i].box,
971 				XtNborderWidth, 0, XtNresize, True, XtNresizable, True,
972 				NULL);
973 		XtAddCallback (devs[i].lbl, XtNcallback, CbChangeLabel,
974 						(XtPointer)wm_delete);
975 
976 		devs[i].sll = XtVaCreateManagedWidget (
977 				"left", pannerWidgetClass, devs[i].box,
978 				XtNfromVert, devs[i].lbl,
979 				NULL);
980 		XtAddCallback(devs[i].sll,XtNreportCallback,CbPanL,(XtPointer)&devs[i]);
981 		XtAddEventHandler (devs[i].sll, ButtonPressMask, False, focusEH, mixer);
982 		XtAddEventHandler (devs[i].sll, KeyPressMask, False, tabEH, &App);
983 		if (mixer_is_stereo (1, i)) {
984 			devs[i].slr = XtVaCreateManagedWidget (
985 				"right", pannerWidgetClass, devs[i].box,
986 				XtNfromVert, devs[i].lbl,
987 				XtNfromHoriz, devs[i].sll,
988 				NULL);
989 			XtAddCallback (devs[i].slr, XtNreportCallback, CbPanR,
990 								(XtPointer)&devs[i]);
991 			XtAddEventHandler (devs[i].slr, ButtonPressMask, False,
992 								focusEH, mixer);
993 			XtAddEventHandler (devs[i].slr, KeyPressMask, False,
994 								tabEH, &App);
995 		}
996 
997 		devs[i].lck = XtVaCreateManagedWidget (
998 			"lck", xwToggleWidgetClass, devs[i].box,
999 			XtNstate, True,
1000 			XtNfromVert, devs[i].sll,
1001 			NULL);
1002 		devs[i].locked = 1;
1003 		XtAddCallback (devs[i].lck, XtNcallback, CbLock, (XtPointer)&devs[i]);
1004 
1005 		if (mixer_is_rec_dev (1, i)) {
1006 			devs[i].rec = XtVaCreateManagedWidget (
1007 				"rec", xwToggleWidgetClass, devs[i].box,
1008 				XtNfromHoriz, devs[i].lck,
1009 				XtNfromVert, devs[i].sll,
1010 				NULL);
1011 			XtAddCallback (devs[i].rec, XtNcallback, CbRec, (XtPointer) devs);
1012 		} else {
1013 			devs[i].rec = XtVaCreateManagedWidget (
1014 				"rec", xwToggleWidgetClass, devs[i].box,
1015 				XtNsensitive, False,
1016 				/* XtNshadowType, XtShadowNone, */
1017 				XtNfromHoriz, devs[i].lck,
1018 				XtNfromVert, devs[i].sll,
1019 				NULL);
1020 		}
1021 		devs[i].mut = XtVaCreateManagedWidget ("mut", xwToggleWidgetClass,
1022 				devs[i].box,
1023 				XtNstate, True,
1024 				XtNfromHoriz, devs[i].rec,
1025 				XtNfromVert, devs[i].sll,
1026 				NULL);
1027 		XtVaGetValues (devs[i].mut, XtNforeground, &(devs[i].active), NULL);
1028 		XtAddCallback (devs[i].mut, XtNcallback, CbMute, &(devs[i]));
1029 		menu_entries[i] = XtVaCreateManagedWidget (mixer_get_label (1,i),
1030 							smeBSBObjectClass, map_menu,
1031 							XtNleftMargin, ok_width + 2,
1032 							XtNleftBitmap, App.checked, NULL);
1033 		XtAddCallback (menu_entries[i], XtNcallback, CbMap, devs[i].box);
1034 	}
1035 	/* */
1036 	menu_entries[i] = XtVaCreateManagedWidget ("menu",
1037 							smeBSBObjectClass, map_menu,
1038 							XtNleftMargin, ok_width + 2,
1039 							XtNleftBitmap, App.checked, NULL);
1040 	XtAddCallback (menu_entries[i], XtNcallback, CbMap, App.menu_box);
1041 
1042 	/* prevent flickering while unmapping some devices */
1043 	XtVaSetValues (toplevel, XtNmappedWhenManaged, False, NULL);
1044 
1045 	XtRealizeWidget (toplevel);
1046 
1047 	devs[0].rec_bm = XCreateBitmapFromData (XtDisplay (mixer), XtWindow (mixer),
1048 				input_bits, input_width, input_height);
1049 	devs[0].norec_bm=XCreateBitmapFromData (XtDisplay (mixer), XtWindow (mixer),
1050 				norec_bits, norec_width, norec_height);
1051 	devs[0].lck_bm = XCreateBitmapFromData (XtDisplay (mixer), XtWindow (mixer),
1052 				locked_bits, locked_width, locked_height);
1053 	devs[0].unlck_bm=XCreateBitmapFromData (XtDisplay (mixer), XtWindow (mixer),
1054 				unlocked_bits, unlocked_width, unlocked_height);
1055 	devs[0].none_bm=XCreateBitmapFromData (XtDisplay (mixer), XtWindow (mixer),
1056 				none_bits, none_width, none_height);
1057 	devs[0].mute_bm=XCreateBitmapFromData (XtDisplay (mixer), XtWindow (mixer),
1058 				mute_bits, mute_width, mute_height);
1059 
1060 	for ( i = 0; i < no_of_devs; i++) {
1061 		devs[i].rec_bm = devs[0].rec_bm;
1062 		devs[i].norec_bm = devs[0].norec_bm;
1063 		devs[i].lck_bm = devs[0].lck_bm;
1064 		devs[i].unlck_bm = devs[0].unlck_bm;
1065 		devs[i].none_bm = devs[0].none_bm;
1066 		devs[i].mute_bm = devs[0].mute_bm;
1067 		if (mixer_is_rec_dev (1, i)) {
1068 			if (mixer_is_rec_on (1, i)) {
1069 				XtVaSetValues (devs[i].rec,
1070 					XtNstate, True,
1071 					XtNbitmap, devs[i].rec_bm,
1072 					NULL);
1073 			} else {
1074 				XtVaSetValues (devs[i].rec,
1075 					XtNstate, False,
1076 					XtNbitmap, devs[i].norec_bm,
1077 					NULL);
1078 			}
1079 		} else {
1080 			XtVaSetValues (devs[i].rec, XtNbitmap, devs[i].none_bm, NULL);
1081 		}
1082 		vol = mixer_get_vol_left (1, i);
1083 		XtVaSetValues (devs[i].mut, XtNbitmap, devs[i].mute_bm, NULL);
1084 		XtVaSetValues (devs[i].sll, XtNsliderY, 100-vol, NULL);
1085 		if (mixer_is_stereo (1, i)) {
1086 			if (vol == mixer_get_vol_right (1, i)) {
1087 				XtVaSetValues (devs[i].lck, XtNbitmap, devs[i].lck_bm, NULL);
1088 				XtVaSetValues (devs[i].slr, XtNsliderY, 100-vol, NULL);
1089 			} else {
1090 				XtVaSetValues (devs[i].lck, XtNbitmap, devs[i].unlck_bm,
1091 								XtNstate, False, NULL);
1092 				vol = mixer_get_vol_right (1, i);
1093 				XtVaSetValues (devs[i].slr, XtNsliderY, 100-vol, NULL);
1094 				devs[i].locked = 0;
1095 			}
1096 		} else {
1097 			XtVaSetValues (devs[i].lck,
1098 					XtNbitmap, devs[i].none_bm,
1099 					XtNstate, False,
1100 					XtNsensitive, False,
1101 					/* XtNshadowType, XtShadowNone, */
1102 					NULL);
1103 		}
1104 	}
1105 
1106 	/* restore settings for map/unmap
1107 	 */
1108 	scf = scf_init (pname, NULL);
1109 	if (scf_read (scf)) {
1110 		char *l;
1111 		for ( i = 0; i < no_of_devs; i++) {
1112 			name = mixer_get_name (1, i);
1113 			sprintf (key, "%s_map", name);
1114 			if (scf_get_bool_val (scf, mixer_dev, key, &map)) {
1115 				if (map == SCF_FALSE) {
1116 					CbMap (menu_entries[i], devs[i].box, NULL);
1117 				}
1118 			}
1119 			sprintf (key, "%s_label", name);
1120 			l = scf_get_val (scf, mixer_dev, key);
1121 			if (l) {
1122 				XtVaSetValues (devs[i].lbl, XtNlabel, l, NULL);
1123 				XtVaSetValues (menu_entries[i], XtNlabel, l, NULL);
1124 			}
1125 		}
1126 	}
1127 	scf_fini (scf);
1128 
1129 
1130 	XtMapWidget (toplevel);
1131 	XtAppAddTimeOut (App.context, App.update_time * 1000,
1132 					(XtTimerCallbackProc) TCbUpdate, (XtPointer)devs);
1133 
1134 	XSetWMProtocols (XtDisplay(toplevel), XtWindow(toplevel), &wm_delete, 1);
1135 	XtAddEventHandler (toplevel, NoEventMask, True, WmDelEH, &wm_delete);
1136 
1137 	XtInstallAccelerators (frame, file_mb);
1138 	XtInstallAccelerators (frame, map_mb);
1139 	XtInstallAccelerators (frame, help_mb);
1140 
1141 	XtAppMainLoop (App.context);
1142 	return (ERROR);
1143 }
1144 
1145 
1146 /*
1147  * for an internal list ..
1148  */
1149 typedef struct widgets {
1150 	Widget w;
1151 	struct widgets *next;
1152 } Widgets;
1153 
1154 static Widgets *first = NULL;
1155 
1156 /*
1157  */
1158 static void
RegisterWidget(Widget w)1159 RegisterWidget (Widget w)
1160 {
1161 	Widgets *t = first;
1162 	Widgets *new = malloc (sizeof (Widgets));
1163 	if (!new)
1164 		return;
1165 	new->w = w;
1166 	new->next = NULL;
1167 	if (!t) {
1168 		first = new;
1169 	} else {
1170 		while (t->next)
1171 			t = t->next;
1172 		t->next = new;
1173 	}
1174 }
1175 
1176 /*
1177  */
1178 static Widget
NameToWidget(String name)1179 NameToWidget (String name)
1180 {
1181 	Widgets *t = first;
1182 	while (t && name) {
1183 		if (strcmp (XtName(t->w), name) == 0)
1184 			return (t->w);
1185 		t = t->next;
1186 	}
1187 	return (None);
1188 }
1189 
1190