1 // wmsmixer - A mixer designed for WindowMaker with scrollwheel support
2 // Copyright (C) 2003 Damian Kramer <psiren@hibernaculum.net>
3 // Copyright (C) 1998 Sam Hawker <shawkie@geocities.com>
4 // This software comes with ABSOLUTELY NO WARRANTY
5 // This software is free software, and you are welcome to redistribute it
6 // under certain conditions
7 // See the README file for a more complete notice.
10 // Defines, includes and global variables
11 // --------------------------------------
13 // User defines - standard
14 #define WINDOWMAKER false
15 #define USESHAPE false
16 #define AFTERSTEP false
19 #define NAME "wmsmixer"
20 #define CLASS "Wmsmixer"
22 #define VERSION "0.5.1"
24 // User defines - custom
25 #define MIXERDEV "/dev/mixer"
26 #define BACKCOLOR "#202020"
27 #define LEDCOLOR "#00c9c1"
30 #define CLAMP(x, l, h) (((x) > (h)) ? (h) : (((x) < (l)) ? (l) : (x)))
32 // Includes - standard
42 // X-Windows includes - standard
45 #include <X11/Xutil.h>
46 #include <X11/Xproto.h>
48 #include <X11/extensions/shape.h>
61 // Xpm images - standard
62 #include "XPM/wmsmixer.xpm"
63 #include "XPM/tile.xpm"
65 // Xpm images - custom
66 #include "XPM/icons.xpm"
67 #include "XPM/digits.xpm"
68 #include "XPM/chars.xpm"
70 // Variables for command-line arguments - standard
71 bool wmaker
=WINDOWMAKER
;
75 char position
[256]="";
77 bool no_volume_display
= 0;
79 // Variables for command-line arguments - custom
80 char mixdev
[256]=MIXERDEV
;
81 char backcolor
[256]=BACKCOLOR
;
82 char ledcolor
[256]=LEDCOLOR
;
84 // X-Windows basics - standard
85 Atom _XA_GNUSTEP_WM_FUNC
;
93 // X-Windows basics - custom
95 unsigned long color
[4];
99 // Misc custom global variables
100 // ----------------------------
102 // Current state information
112 // For repeating next and prev buttons
113 #define RPTINTERVAL 5
116 // For draggable volume control
121 int icon
[25]={0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
122 char *small_labels
[25] = {"vol ", "bass", "trbl", "synt", "pcm ", "spkr", "line",
123 "mic ", "cd ", "mix ", "pcm2", "rec ", "igai", "ogai",
124 "lin1", "lin2", "lin3", "dig1", "dig2", "dig3", "phin",
125 "phou", "vid ", "rad ", "mon "};
130 // Procedures and functions
131 // ------------------------
133 // Procedures and functions - standard
134 void initXWin(int argc
, char **argv
);
136 void createWin(Window
*win
, int x
, int y
);
137 unsigned long getColor(char *colorname
);
138 unsigned long mixColor(char *colorname1
, int prop1
, char *colorname2
, int prop2
);
140 // Procedures and functions - custom
141 void scanArgs(int argc
, char **argv
);
143 void checkVol(bool forced
);
144 void pressEvent(XButtonEvent
*xev
);
145 void releaseEvent(XButtonEvent
*xev
);
146 void motionEvent(XMotionEvent
*xev
);
153 void drawText(char *text
);
154 void drawBtns(int btns
);
155 void drawBtn(int x
, int y
, int w
, int h
, bool down
);
161 int main(int argc
, char **argv
)
163 scanArgs(argc
, argv
);
164 initXWin(argc
, argv
);
168 gcm
=GCGraphicsExposures
;
169 gcv
.graphics_exposures
=false;
170 gc_gc
=XCreateGC(d_display
, w_root
, gcm
, &gcv
);
172 color
[0]=mixColor(ledcolor
, 0, backcolor
, 100);
173 color
[1]=mixColor(ledcolor
, 100, backcolor
, 0);
174 color
[2]=mixColor(ledcolor
, 60, backcolor
, 40);
175 color
[3]=mixColor(ledcolor
, 25, backcolor
, 75);
177 XpmAttributes xpmattr
;
178 XpmColorSymbol xpmcsym
[4]={{"back_color", NULL
, color
[0]},
179 {"led_color_high", NULL
, color
[1]},
180 {"led_color_med", NULL
, color
[2]},
181 {"led_color_low", NULL
, color
[3]}};
182 xpmattr
.numsymbols
=4;
183 xpmattr
.colorsymbols
=xpmcsym
;
184 xpmattr
.exactColors
=false;
185 xpmattr
.closeness
=40000;
186 xpmattr
.valuemask
=XpmColorSymbols
| XpmExactColors
| XpmCloseness
;
187 XpmCreatePixmapFromData(d_display
, w_root
, wmsmixer_xpm
, &pm_main
, &pm_mask
, &xpmattr
);
188 XpmCreatePixmapFromData(d_display
, w_root
, tile_xpm
, &pm_tile
, NULL
, &xpmattr
);
189 XpmCreatePixmapFromData(d_display
, w_root
, icons_xpm
, &pm_icon
, NULL
, &xpmattr
);
190 XpmCreatePixmapFromData(d_display
, w_root
, digits_xpm
, &pm_digits
, NULL
, &xpmattr
);
191 XpmCreatePixmapFromData(d_display
, w_root
, chars_xpm
, &pm_chars
, NULL
, &xpmattr
);
192 pm_disp
=XCreatePixmap(d_display
, w_root
, 64, 64, DefaultDepth(d_display
, DefaultScreen(d_display
)));
195 if(wmaker
|| ushape
|| astep
)
196 XShapeCombineMask(d_display
, w_activewin
, ShapeBounding
, winsize
/2-32, winsize
/2-32, pm_mask
, ShapeSet
);
198 XCopyArea(d_display
, pm_tile
, pm_disp
, gc_gc
, 0, 0, 64, 64, 0, 0);
200 XSetClipMask(d_display
, gc_gc
, pm_mask
);
201 XCopyArea(d_display
, pm_main
, pm_disp
, gc_gc
, 0, 0, 64, 64, 0, 0);
202 XSetClipMask(d_display
, gc_gc
, None
);
204 mixctl
=new MixCtl(mixdev
);
206 if(!mixctl
->openOK())
207 fprintf(stderr
,"%s : Unable to open mixer device '%s'.\n", NAME
, mixdev
);
209 for(int i
=0;i
<mixctl
->getNrDevices();i
++){
211 fprintf(stderr
,"%s : Sorry, can only use channels 0-24\n", NAME
);
214 if(mixctl
->getSupport(i
)){
224 fprintf(stderr
,"%s : Sorry, no supported channels found.\n", NAME
);
229 XSelectInput(d_display
, w_activewin
, ExposureMask
| ButtonPressMask
| ButtonReleaseMask
| ButtonMotionMask
);
230 XMapWindow(d_display
, w_main
);
234 while(XPending(d_display
)){
235 XNextEvent(d_display
, &xev
);
241 pressEvent(&xev
.xbutton
);
244 releaseEvent(&xev
.xbutton
);
247 motionEvent(&xev
.xmotion
);
250 if(xev
.xclient
.data
.l
[0]==deleteWin
)
256 if(btnstate
& (BTNPREV
| BTNNEXT
)){
258 if(rpttimer
>=RPTINTERVAL
){
259 if(btnstate
& BTNNEXT
)
264 curchannel
=channels
-1;
265 if(curchannel
>=channels
)
280 // printf("%c", text_counter);
288 XFreeGC(d_display
, gc_gc
);
289 XFreePixmap(d_display
, pm_main
);
290 XFreePixmap(d_display
, pm_tile
);
291 XFreePixmap(d_display
, pm_disp
);
292 XFreePixmap(d_display
, pm_mask
);
293 XFreePixmap(d_display
, pm_icon
);
294 XFreePixmap(d_display
, pm_digits
);
295 XFreePixmap(d_display
, pm_chars
);
301 void initXWin(int argc
, char **argv
)
303 winsize
=astep
? ASTEPSIZE
: NORMSIZE
;
305 if((d_display
=XOpenDisplay(display
))==NULL
){
306 fprintf(stderr
,"%s : Unable to open X display '%s'.\n", NAME
, XDisplayName(display
));
309 _XA_GNUSTEP_WM_FUNC
=XInternAtom(d_display
, "_GNUSTEP_WM_FUNCTION", false);
310 deleteWin
=XInternAtom(d_display
, "WM_DELETE_WINDOW", false);
312 w_root
=DefaultRootWindow(d_display
);
319 bool pos
=(XWMGeometry(d_display
, DefaultScreen(d_display
), position
, NULL
, 0, &shints
, &shints
.x
, &shints
.y
,
320 &shints
.width
, &shints
.height
, &shints
.win_gravity
) & (XValue
| YValue
));
321 shints
.min_width
=winsize
;
322 shints
.min_height
=winsize
;
323 shints
.max_width
=winsize
;
324 shints
.max_height
=winsize
;
325 shints
.base_width
=winsize
;
326 shints
.base_height
=winsize
;
327 shints
.flags
=PMinSize
| PMaxSize
| PBaseSize
;
329 createWin(&w_main
, shints
.x
, shints
.y
);
331 if(wmaker
|| astep
|| pos
)
332 shints
.flags
|= USPosition
;
334 wmhints
.initial_state
=WithdrawnState
;
335 wmhints
.flags
=WindowGroupHint
| StateHint
| IconWindowHint
;
336 createWin(&w_icon
, shints
.x
, shints
.y
);
338 wmhints
.icon_window
=w_icon
;
341 wmhints
.initial_state
=NormalState
;
342 wmhints
.flags
=WindowGroupHint
| StateHint
;
345 wmhints
.window_group
=w_main
;
346 XSetWMHints(d_display
, w_main
, &wmhints
);
347 XSetWMNormalHints(d_display
, w_main
, &shints
);
348 XSetCommand(d_display
, w_main
, argv
, argc
);
349 XStoreName(d_display
, w_main
, NAME
);
350 XSetIconName(d_display
, w_main
, NAME
);
351 XSetWMProtocols(d_display
, w_activewin
, &deleteWin
, 1);
356 XDestroyWindow(d_display
, w_main
);
358 XDestroyWindow(d_display
, w_icon
);
359 XCloseDisplay(d_display
);
362 void createWin(Window
*win
, int x
, int y
)
364 XClassHint classHint
;
365 *win
=XCreateSimpleWindow(d_display
, w_root
, x
, y
, winsize
, winsize
, 0, 0, 0);
366 classHint
.res_name
=NAME
;
367 classHint
.res_class
=CLASS
;
368 XSetClassHint(d_display
, *win
, &classHint
);
371 unsigned long getColor(char *colorname
)
374 XWindowAttributes winattr
;
375 XGetWindowAttributes(d_display
, w_root
, &winattr
);
377 XParseColor(d_display
, winattr
.colormap
, colorname
, &color
);
378 color
.flags
=DoRed
| DoGreen
| DoBlue
;
379 XAllocColor(d_display
, winattr
.colormap
, &color
);
383 unsigned long mixColor(char *colorname1
, int prop1
, char *colorname2
, int prop2
)
385 XColor color
, color1
, color2
;
386 XWindowAttributes winattr
;
387 XGetWindowAttributes(d_display
, w_root
, &winattr
);
388 XParseColor(d_display
, winattr
.colormap
, colorname1
, &color1
);
389 XParseColor(d_display
, winattr
.colormap
, colorname2
, &color2
);
391 color
.red
=(color1
.red
*prop1
+color2
.red
*prop2
)/(prop1
+prop2
);
392 color
.green
=(color1
.green
*prop1
+color2
.green
*prop2
)/(prop1
+prop2
);
393 color
.blue
=(color1
.blue
*prop1
+color2
.blue
*prop2
)/(prop1
+prop2
);
394 color
.flags
=DoRed
| DoGreen
| DoBlue
;
395 XAllocColor(d_display
, winattr
.colormap
, &color
);
399 void scanArgs(int argc
, char **argv
)
401 for(int i
=1;i
<argc
;i
++){
402 if(strcmp(argv
[i
], "-h")==0 || strcmp(argv
[i
], "--help")==0) {
403 fprintf(stderr
, "wmsmixer - A mixer designed for WindowMaker with scrollwheel support\n");
404 fprintf(stderr
, "Copyright (C) 2003 Damian Kramer <psiren@hibernaculum.net>\n");
405 fprintf(stderr
, "Copyright (C) 1998 Sam Hawker <shawkie@geocities.com>\n");
406 fprintf(stderr
, "This software comes with ABSOLUTELY NO WARRANTY\n");
407 fprintf(stderr
, "This software is free software, and you are welcome to redistribute it\n");
408 fprintf(stderr
, "under certain conditions\n");
409 fprintf(stderr
, "See the README file for a more complete notice.\n\n");
410 fprintf(stderr
, "usage:\n\n %s [options]\n\noptions:\n\n",argv
[0]);
411 fprintf(stderr
, " -h | --help display this help screen\n");
412 fprintf(stderr
, " -v | --version display the version\n");
413 fprintf(stderr
, " -w use WithdrawnState (for WindowMaker)\n");
414 fprintf(stderr
, " -s shaped window\n");
415 fprintf(stderr
, " -a use smaller window (for AfterStep Wharf)\n");
416 fprintf(stderr
, " -l led_color use the specified color for led display\n");
417 fprintf(stderr
, " -b back_color use the specified color for backgrounds\n");
418 fprintf(stderr
, " -d mix_device use specified device (rather than /dev/mixer)\n");
419 fprintf(stderr
, " -position position set window position (see X manual pages)\n");
420 fprintf(stderr
, " -display display select target display (see X manual pages)\n\n");
423 if(strcmp(argv
[i
], "-v")==0 || strcmp(argv
[i
], "--version")==0) {
424 fprintf(stderr
, "wmsmixer version %s\n", VERSION
);
427 if(strcmp(argv
[i
], "-w")==0)
429 if(strcmp(argv
[i
], "-s")==0)
431 if(strcmp(argv
[i
], "-a")==0)
433 if(strcmp(argv
[i
], "-novol")==0)
434 no_volume_display
= 1;
435 if(strcmp(argv
[i
], "-d")==0){
438 sprintf(mixdev
, "%s", argv
[i
]);
442 if(strcmp(argv
[i
], "-l")==0){
445 sprintf(ledcolor
, "%s", argv
[i
]);
449 if(strcmp(argv
[i
], "-b")==0){
452 sprintf(backcolor
, "%s", argv
[i
]);
456 if(strcmp(argv
[i
], "-position")==0){
459 sprintf(position
, "%s", argv
[i
]);
463 if(strcmp(argv
[i
], "-display")==0){
466 sprintf(display
, "%s", argv
[i
]);
480 sprintf(rcfilen
, "%s/.wmsmixer", getenv("HOME"));
481 if((rcfile
=fopen(rcfilen
, "r"))!=NULL
){
484 fgets(buf
, 250, rcfile
);
485 if((done
=feof(rcfile
))==0){
486 buf
[strlen(buf
)-1]=0;
487 if(strncmp(buf
, "addchannel ", strlen("addchannel "))==0){
488 sscanf(buf
, "addchannel %i", ¤t
);
489 if(current
>=mixctl
->getNrDevices() || mixctl
->getSupport(current
)==false){
490 fprintf(stderr
,"%s : Sorry, this channel (%i) is not supported.\n", NAME
, current
);
494 channel
[channels
]=current
;
498 if(strncmp(buf
, "setchannel ", strlen("setchannel "))==0){
499 sscanf(buf
, "setchannel %i", ¤t
);
500 if(current
>=mixctl
->getNrDevices() || mixctl
->getSupport(current
)==false){
501 fprintf(stderr
,"%s : Sorry, this channel (%i) is not supported.\n", NAME
, current
);
505 if(strncmp(buf
, "setname ", strlen("setname "))==0){
507 fprintf(stderr
,"%s : Sorry, no current channel.\n", NAME
);
509 small_labels
[current
] = (char *)malloc(sizeof(char)*5);
510 sscanf(buf
, "setname %4s", small_labels
[current
]);
513 if(strncmp(buf
, "setmono ", strlen("setmono "))==0){
515 fprintf(stderr
,"%s : Sorry, no current channel.\n", NAME
);
518 sscanf(buf
, "setmono %i", &value
);
519 mixctl
->setLeft(current
, value
);
520 mixctl
->setRight(current
, value
);
521 mixctl
->writeVol(current
);
524 if(strncmp(buf
, "setleft ", strlen("setleft "))==0){
526 fprintf(stderr
, "%s : Sorry, no current channel.\n", NAME
);
529 sscanf(buf
, "setleft %i", &value
);
530 mixctl
->setLeft(current
, value
);
531 mixctl
->writeVol(current
);
534 if(strncmp(buf
, "setright ", strlen("setright "))==0){
536 fprintf(stderr
, "%s : Sorry, no current channel.\n", NAME
);
539 sscanf(buf
, "setleft %i", &value
);
540 mixctl
->setRight(current
, value
);
541 mixctl
->writeVol(current
);
550 void checkVol(bool forced
=true)
552 mixctl
->readVol(channel
[curchannel
], true);
553 int nl
=mixctl
->readLeft(channel
[curchannel
]);
554 int nr
=mixctl
->readRight(channel
[curchannel
]);
562 if(nl
!=curleft
|| nr
!=curright
){
565 if(mixctl
->getStereo(channel
[curchannel
]))
572 if(mixctl
->getStereo(channel
[curchannel
]))
577 if(!no_volume_display
)
584 void pressEvent(XButtonEvent
*xev
)
586 if(xev
->button
== Button4
|| xev
->button
== Button5
) {
588 if(xev
->button
== Button4
) inc
= 4;
591 mixctl
->readVol(channel
[curchannel
], false);
592 mixctl
->setLeft(channel
[curchannel
],
593 CLAMP(mixctl
->readLeft(channel
[curchannel
]) + inc
, 0, 100));
594 mixctl
->setRight(channel
[curchannel
],
595 CLAMP(mixctl
->readRight(channel
[curchannel
]) + inc
, 0, 100));
596 mixctl
->writeVol(channel
[curchannel
]);
601 int x
=xev
->x
-(winsize
/2-32);
602 int y
=xev
->y
-(winsize
/2-32);
603 if(x
>=5 && y
>=47 && x
<=17 && y
<=57){
606 curchannel
=channels
-1;
613 if(x
>=18 && y
>=47 && x
<=30 && y
<=57){
615 if(curchannel
>=channels
)
623 if(x
>=37 && x
<=56 && y
>=8 && y
<=56){
624 int v
=((60-y
)*100)/(2*25);
627 mixctl
->setLeft(channel
[curchannel
], v
);
629 mixctl
->setRight(channel
[curchannel
], v
);
630 mixctl
->writeVol(channel
[curchannel
]);
634 if(x
>=5 && y
>=21 && x
<=30 && y
<=42) {
635 drawText(small_labels
[channel
[curchannel
]]);
641 void releaseEvent(XButtonEvent
*xev
)
644 btnstate
&= ~(BTNPREV
| BTNNEXT
);
645 drawBtns(BTNPREV
| BTNNEXT
);
649 void motionEvent(XMotionEvent
*xev
)
651 int x
=xev
->x
-(winsize
/2-32);
652 int y
=xev
->y
-(winsize
/2-32);
653 if(x
>=37 && x
<=56 && y
>=8 && dragging
){
654 int v
=((60-y
)*100)/(2*25);
658 mixctl
->setLeft(channel
[curchannel
], v
);
660 mixctl
->setRight(channel
[curchannel
], v
);
661 mixctl
->writeVol(channel
[curchannel
]);
668 XCopyArea(d_display
, pm_disp
, w_activewin
, gc_gc
, 0, 0, 64, 64, winsize
/2-32, winsize
/2-32);
670 while(XCheckTypedEvent(d_display
, Expose
, &xev
));
675 drawText(small_labels
[channel
[curchannel
]]);
677 XCopyArea(d_display
, pm_icon
, pm_disp
, gc_gc
, icon
[channel
[curchannel
]]*26, 0, 26, 24, 5, 19);
678 if(mixctl
->getStereo(channel
[curchannel
])) {
687 void drawText(char *text
)
692 for(int i
=0; i
<4; i
++, p
++) {
694 if(p2
>= 'A' && p2
<= 'Z') {
695 XCopyArea(d_display
, pm_chars
, pm_disp
, gc_gc
, 6*((int)p2
-65), 0, 6, 9, 5+(i
*6), 5);
697 else if(p2
>= '0' && p2
<= '9') {
698 XCopyArea(d_display
, pm_digits
, pm_disp
, gc_gc
, 6*((int)p2
-48), 0, 6, 9, 5+(i
*6), 5);
703 XCopyArea(d_display
, pm_digits
, pm_disp
, gc_gc
, 60, 0, 6, 9, 5+(i
*6), 5);
706 if(!no_volume_display
)
714 int vol
= (mixctl
->readLeft(channel
[curchannel
]) +
715 mixctl
->readRight(channel
[curchannel
])) / 2;
717 digits
[0] = (vol
/100) ? 1 : 10;
718 digits
[1] = (vol
/10) == 10 ? 0 : (vol
/10);
722 for(int i
=0; i
<4; i
++) {
723 XCopyArea(d_display
, pm_digits
, pm_disp
, gc_gc
, 6*digits
[i
], 0, 6, 9, 5+(i
*6), 5);
729 XSetForeground(d_display
, gc_gc
, color
[0]);
730 XFillRectangle(d_display
, pm_disp
, gc_gc
, 46, 7, 2, 49);
732 XSetForeground(d_display
, gc_gc
, color
[1]);
733 for(int i
=0;i
<25;i
++) {
734 if(i
==(curleft
*25)/100)
735 XSetForeground(d_display
, gc_gc
, color
[3]);
736 XFillRectangle(d_display
, pm_disp
, gc_gc
, 37, 55-2*i
, 9, 1);
742 XSetForeground(d_display
, gc_gc
, color
[0]);
743 XFillRectangle(d_display
, pm_disp
, gc_gc
, 46, 7, 2, 49);
745 XSetForeground(d_display
, gc_gc
, color
[1]);
746 for(int i
=0;i
<25;i
++) {
747 if(i
==(curright
*25)/100)
748 XSetForeground(d_display
, gc_gc
, color
[3]);
749 XFillRectangle(d_display
, pm_disp
, gc_gc
, 48, 55-2*i
, 9, 1);
755 XSetForeground(d_display
, gc_gc
, color
[1]);
756 for(int i
=0;i
<25;i
++){
757 if(i
==(curright
*25)/100)
758 XSetForeground(d_display
, gc_gc
, color
[3]);
759 XFillRectangle(d_display
, pm_disp
, gc_gc
, 37, 55-2*i
, 20, 1);
764 void drawBtns(int btns
)
767 drawBtn(5, 47, 13, 11, (btnstate
& BTNPREV
));
769 drawBtn(18, 47, 13, 11, (btnstate
& BTNNEXT
));
772 void drawBtn(int x
, int y
, int w
, int h
, bool down
)
775 XCopyArea(d_display
, pm_main
, pm_disp
, gc_gc
, x
, y
, w
, h
, x
, y
);
777 XCopyArea(d_display
, pm_main
, pm_disp
, gc_gc
, x
, y
, 1, h
-1, x
+w
-1, y
+1);
778 XCopyArea(d_display
, pm_main
, pm_disp
, gc_gc
, x
+w
-1, y
+1, 1, h
-1, x
, y
);
779 XCopyArea(d_display
, pm_main
, pm_disp
, gc_gc
, x
, y
, w
-1, 1, x
+1, y
+h
-1);
780 XCopyArea(d_display
, pm_main
, pm_disp
, gc_gc
, x
+1, y
+h
-1, w
-1, 1, x
, y
);