[Pkg-wmaker-commits] [wmix] 01/10: Import Upstream version 3.0

Doug Torrance dtorrance-guest at moszumanska.debian.org
Fri Sep 29 02:16:30 UTC 2017


This is an automated email from the git hooks/post-receive script.

dtorrance-guest pushed a commit to branch master
in repository wmix.

commit 2061103c62e09a225f831f812ae0388d9474c862
Author: Doug Torrance <dtorrance at piedmont.edu>
Date:   Thu Sep 28 22:02:02 2017 -0400

    Import Upstream version 3.0
---
 AUTHORS             |  21 ++
 BUGS                |   4 +
 COPYING             |   3 +
 INSTALL             |  11 +
 Makefile            |  18 ++
 NEWS                |  76 +++++++
 README              |  93 ++++++++
 include/common.h    |  50 +++++
 include/led-off.xpm |  18 ++
 include/led-on.xpm  |  24 +++
 include/master.xpm  | 161 ++++++++++++++
 include/misc.h      |  26 +++
 include/mixer.h     |  77 +++++++
 include/ui_x.h      |  40 ++++
 misc.c              | 163 ++++++++++++++
 mixer-oss.c         | 408 +++++++++++++++++++++++++++++++++++
 sample.wmixrc       |  16 ++
 ui_x.c              | 604 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 wmix.1x.gz          | Bin 0 -> 584 bytes
 wmix.c              | 399 ++++++++++++++++++++++++++++++++++
 20 files changed, 2212 insertions(+)

diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..81f9c65
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,21 @@
+timecop [timecop at japan.co.jp]
+the main author of wmix
+
+Daniel Richard G. [skunk at mit.edu]
+author of volume.app, where I got the knob math and mixer routines from
+(which was in turn based on mixer routines in wmix 2.x)
+
+/* the people below have contributed code in the past, though most of the
+   code has been rewritten in 3.0 */
+
+gilles querret [gilles.querret at free.fr]
+author of the mousewheel patch for wmix
+
+Travis Whitton [travis at atlantic.net]
+submitted a patch to restore volume and balance when pressing mute button,
+and also did changes in the Makefile to make sure it works on systems other
+than my own.
+
+Vinnie Yesue [vinnie at fizz.sonicity.com]
+Did the work on changing mixermax stuff, because he had one of those broken
+soundcards that made things break (see BUGS)
diff --git a/BUGS b/BUGS
new file mode 100644
index 0000000..4c7dce3
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,4 @@
+If you find any, make sure and let me know.
+
+Since this is almost a complete rewrite, I am sure I missed something
+somewhere. If you find any problems, please let me know.
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..eb96c7a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,3 @@
+#ifndef GNU_GPL_V2
+#include <GNU_GPL_V2.h>
+#endif
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..6301f0d
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,11 @@
+Since your reading this, you are probably done with the tar zxvf part.
+
+1. make
+2. become root
+3. make install
+4. wmix
+
+pretty easy.  See README for instructions on how to use the mixer and about
+some information on the programming involved.
+
+timecop [timecop at japan.co.jp]
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..6ec329a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,18 @@
+CC		= gcc
+CFLAGS		= -O3 -W -Wall
+LDFLAGS		= -L/usr/X11R6/lib
+OBJECTS		= misc.o mixer-oss.o ui_x.o wmix.o
+
+# where to install this program (also for packaging stuff)
+DESTDIR		=
+PREFIX		= $(DESTDIR)/usr/X11R6
+INSTALL		= -m 755
+
+wmix: $(OBJECTS)
+	$(CC) -o $@ $(LDFLAGS) $(OBJECTS) -lXpm -lXext -lX11 -lm
+
+clean:
+	rm -rf *.o wmix *~
+
+install: wmix
+	install $(INSTALL) wmix $(PREFIX)/bin
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..4ee3f7d
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,76 @@
+3.0 NEWS:
+Major code rewrite. Now uses much better mixer library. New knob drawing code.
+New config parsing code - no more segfaults. New mouse control code for knob
+and slider dragging - more intuitive. Documentation updates. Removed runtime
+config parsing, only reading config file once on startup. New command line
+options to specify X display, config file, and mixer device. Removed "mixermax"
+code until someone complains. Current channel title scrolling is now
+configurable.
+
+2.2 NEWS:
+This is a bugfix release.  On-Screen-Display code now looks for some common
+large bitmap font.  Check that $HOME is set before trying to write a config
+file.  Wmix makes a pid file for use with signal changing volume up/down.
+Minor code reorganization.
+
+2.11 NEWS:
+Mostly bugfixes and other random stuff.  OSD now really stays on top provided
+you run a GNOME-compliant window manager (wmaker/E/saw-what-ever,etc)
+
+2.1 NEWS:
+Configuration file implemented.  Mousewheel functionality, OSD display,
+OSD color, main mixer device, and OSS bug work-around can now be selected
+through WMixer rc file, by default placed in ~/.wmixrc
+Changes to the rc file are loaded dynamically, while the mixer is running -
+you can change the OSD color, for example, and see the results right away.
+
+2.04 NEWS:
+New feature: Volume of the current channel can be controlled using signals.
+Sending SIGUSR1 will increase the volume by 1 step, and SIGUSR2 will decrase
+the volume by 1 step.  This could be useful if you have a newer style keyboard
+with volume buttons and all the other extra shit.  You can tell your favorite
+window manager to run a program on keypress, and that program could be
+something like "killall -USR1 wmix" for volume up key, and "killall -USR2 wmix"
+for volume down.  How to assign programs to specific keys is up to you.  I know
+Blackbox and WindowMaker can both do it.  Keep in mind most X-servers don't
+automatically support those extra keys, so some hacking might be required.
+Usually they are sent as a Win95 key scancode plus another scancode.  I don't
+have one of these keyboards so I don't know.  If enough people ask for it
+I will add 2 more signal handlers to switch the current "channel" back and
+forth.
+
+2.02 NEWS:
+Version 2.02 adds mouse wheel support to adjust volume, thanks to the 
+patch from Gilles QUERRET <gilles.querret at free.fr>.  Now instead of
+dragging the knob, you can use your mouse wheel to adjust the current channel
+volume.  Position the cursor anywhere on the dockapp, and move the wheel.
+This behaviour is off by default, but you can compile wmix with -DMOUSEWHEEL
+in the Makefile to enable this feature.  Incase your mouse has more than 2
+buttons and a wheel, you can set which button signals the wheel generates as
+"up" and "down" in mix.c, lines 45 and 46.  The default is for a standard
+mouse with 2 buttons and a wheel.  WHEEL_STEP is the amount to adjust the knob.
+The default of 3 should be good for everyone.  Changing the volume using the
+wheel also brings up OSD for the current channel following same rules as if the
+knob was used directly.
+
+2.01 NEWS:
+New feature, which isn't really new, but a feature move from WMixer 1.5, is the
+On-Screen-Display (OSD), just like the one you probably have on your TV.  The
+concept was the only thing copied from Wmixer 1.5 though, the code has been
+rewritten to use a modeless window instead of drawing on the root window,
+and draw code has been optimized not to draw any unnecessary stuff. The OSD
+only comes up when the Knob is turned, i.e. during manual adjustment.  OSD stays
+hidden during automatic updates due to mixer reads or changing rec/balance etc.
+However, due to the way the OSD timer is done, as long as you are moving inside
+the dockapp (adjusting balance, or just generally moving the mouse inside the
+dockapp window, the OSD will stay lit.  After all movement is gone, and in
+approximately 1.5 seconds, the OSD fades out.  If some Xlib programmer knows
+a /NON GAY/ way to handle always-on-top, feel free to mail me a diff that makes
+the OSD continuously on top while being displayed.  Right now, it comes up
+on top, but if you move a window over it or something, it gets hidden.  Not
+too much of a loss, but still could be improved.  OSD follows the same low-CPU
+use guidelines as the rest of the code.  No updates happen unless something
+changes that requires an update.  Also, expose events after uncovering the OSD
+window are not handled - if you can manage to obscure it in 1.5 seconds and
+want to see it again after that, you can always go back and twiddle the knob
+or something.
diff --git a/README b/README
new file mode 100644
index 0000000..82446f3
--- /dev/null
+++ b/README
@@ -0,0 +1,93 @@
+                    _        _____  ___  
+__      ___ __ ___ (_)_  __ |___ / / _ \    timecop at japan.co.jp
+\ \ /\ / / '_ ` _ \| \ \/ /   |_ \| | | |   skunk at mit.edu
+ \ V  V /| | | | | | |>  <   ___) | |_| |
+  \_/\_/ |_| |_| |_|_/_/\_\ |____(_)___/ 
+______________________________________________________________________________
+
+* This is a complete dockapp mixer utilizing the OSS mixer API
+* Has a nice On-Screen-Display to visualize current volume levels
+* Can adjust main volume, balance, recording status, and mute/unmute channels
+* Supports mousewheel to adjust the volume settings
+* Supports user specified signals to adjust the volume remotely
+* User configuration file can be used to set options
+
+How to use it:
+
+.---------------------------.
+| Message Area Message Area | <- (1)
+| [ Rec ]  [ ST ]  [ Mute ] | <- (2) (3) (4)
+| <- ->             xxx     | <- (5) (6) (7)
+| L        R        ___     |
+| |  |||   |       /the\    |
+| |--|||---|      |K NOB|   | <- (8) (9)
+|    ||| <- slider \___/    |
+`---------------------------'
+
+1) Current channel name is displayed in this area. If scrolling messages are
+   enabled in config, the text will scroll every 30 seconds or so, using long
+   sound channel names. If scrolling is disabled, short 5-character channel
+   names are used instead, and the message constantly stays on the screen. In
+   scroll mode, clicking in this area will re-scroll current message.
+
+2) Clicking REC will toggle record status for current channel, if the channel
+   is capable of recording. Some sound cards will allow you to set multiple
+   record sources. Some don't, so you can only have one selected at any time.
+   If you click here and nothing happens, the channel is not record-capable.
+
+3) ST indicator is lit if the current channel is stereo.
+
+4) Clicking MUTE will mute the current channel. Old volume settings are
+   remembered, so clicking it again will un-mute. Also see the knob section
+   because there is more than one way to do it :)
+   
+5) Clicking <- will switch to previous channel
+
+6) Clicking -> will switch to next channel
+
+7) Numeric indicator of current volume from 0 to 100 percent. Doesn't change
+   when the channel is muted, so you can still adjust the volume and then
+   unmute.
+
+8) If ST light is on (Stereo channel), you can move this slider left and
+   right to set balance. Click on the slider, and drag the mouse left or
+   right to adjust. Double-clicking on the slider will center the balance.
+   If you are on a mono channel, the slider doesn't move or do anything.
+   
+9) Ah, yes, the knob. One of the biggest visual differences since version
+   2.x of WMix is that the knob now rendered in real-time opposed to switching
+   pixmaps with various knob angles. Bow down to Daniel Richard G. for coming
+   up with this awesome routine. Also utilizes new "cursor-hiding-and-moving"
+   code which first appeared in volume.app by the same guy. Now you don't
+   have to scroll the mouse off your desk to adjust the volume. Anyway, simply
+   click on the knob and drag the mouse up or down to adjust the volume.
+   After you release the button, your mouse cursor is right back where you
+   started. Very Nifty! You can also double-click on the knob to mute the
+   current channel. Red LED will turn off... Channel is muted!
+
+Most people have mice with wheels these days, to this support is enabled by
+default. Scrolling the mouse wheel up or down will adjust the volume by 3%
+up or down. Buttons and step size are configurable from the config file -
+see below.
+
+You can also check out ~/.wmix.pid, and use SIGUSR1/2 to adjust the volume
+up or down by the configured step size.
+
+The config file:
+
+1 = yes, 0 = no
+
+mousewheel=1			# use mousewheel?
+scrolltext=1			# scroll the system messages?
+osd=1				# display OSD?
+osdcolor=green			# color of the OSD (from rgb.txt)
+wheelbtn1=4			# which mousewheel button is "up"
+wheelbtn2=5			# which mousewheel button is "down"
+wheelstep=3			# the step for mousewheel adjustment
+
+Most defaults are good for normal use, and if there is no config file,
+the settings you see above are used. If you want to hack on a config file,
+copy sample.wmixrc to ~/.wmixrc and change it around. Comments are ignored,
+but don't put comments at the end of line like I did here.
+
+timecop [timecop at japan.co.jp]
diff --git a/include/common.h b/include/common.h
new file mode 100644
index 0000000..1e297cb
--- /dev/null
+++ b/include/common.h
@@ -0,0 +1,50 @@
+/* WMix 3.0 -- a mixer using the OSS mixer API.
+ * Copyright (C) 2000, 2001
+ *	Daniel Richard G. <skunk at mit.edu>,
+ *	timecop <timecop at japan.co.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+typedef unsigned int bool;
+
+#define false 0
+#define true (!false)
+
+#define NULL_CURSOR 1
+#define NORMAL_CURSOR 2
+#define HAND_CURSOR 3
+#define BAR_CURSOR 4
+
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
+
+#define MAX_DOUBLE_CLICK_TIME 0.5
+#define BUTTON_WHEEL_UP 4
+#define BUTTON_WHEEL_DOWN 5
+
+typedef struct _Config Config;
+
+struct _Config {
+    char 	*file;			/* full path to config file name */
+    unsigned int osd        : 1;	/* show OSD? */
+    unsigned int mousewheel : 1;	/* mousewheel enabled? */
+    unsigned int scrolltext : 1;	/* scroll channel names? */
+    unsigned int wheel_button_up;	/* up button */
+    unsigned int wheel_button_down;	/* down button */
+    float	 scrollstep;		/* scroll mouse step adjustment */
+    char	*osd_color;		/* osd color */
+};
diff --git a/include/led-off.xpm b/include/led-off.xpm
new file mode 100644
index 0000000..6f417d5
--- /dev/null
+++ b/include/led-off.xpm
@@ -0,0 +1,18 @@
+/* XPM */
+static char * led_off_xpm[] = {
+"6 6 9 1",
+" 	c None",
+".	c #494949",
+"+	c #414141",
+"@	c #484848",
+"#	c #404040",
+"$	c #474747",
+"%	c #C0C0C0",
+"&	c #454545",
+"*	c #464646",
+" .++@ ",
+".####$",
+"+#%##+",
+"+####+",
+"$####&",
+" *++& "};
diff --git a/include/led-on.xpm b/include/led-on.xpm
new file mode 100644
index 0000000..9a68ba7
--- /dev/null
+++ b/include/led-on.xpm
@@ -0,0 +1,24 @@
+/* XPM */
+static char * led_on_xpm[] = {
+"6 6 15 1",
+" 	c None",
+".	c #7D4141",
+"+	c #A02F2F",
+"@	c #9F2E2E",
+"#	c #7A3E3E",
+"$	c #C81B1B",
+"%	c #F50404",
+"&	c #C71A1A",
+"*	c #783C3C",
+"=	c #FF0000",
+"-	c #9C2B2B",
+";	c #9B2A2A",
+">	c #C61919",
+",	c #763A3A",
+"'	c #793D3D",
+" .+@# ",
+".$%%&*",
+"+%==%-",
+"@%==%;",
+"#&%%>,",
+" '--, "};
diff --git a/include/master.xpm b/include/master.xpm
new file mode 100644
index 0000000..94789c5
--- /dev/null
+++ b/include/master.xpm
@@ -0,0 +1,161 @@
+/* XPM */
+static char * master_xpm[] = {
+"255 109 49 1",
+" 	c None",
+".	c #22B2AE",
+"+	c #202020",
+"@	c #C5C5C5",
+"#	c #3A3B39",
+"$	c #2A3029",
+"%	c #01564E",
+"&	c #272A27",
+"*	c #151614",
+"=	c #1D1A1A",
+"-	c #111012",
+";	c #000000",
+">	c #341F1E",
+",	c #4F5051",
+"'	c #004941",
+")	c #3E4A46",
+"!	c #2A2A29",
+"~	c #161612",
+"{	c #121212",
+"]	c #161616",
+"^	c #121614",
+"/	c #1A1A1A",
+"(	c #262626",
+"_	c #3A3632",
+":	c #37302E",
+"<	c #2C2223",
+"[	c #241D1E",
+"}	c #262222",
+"|	c #212624",
+"1	c #222222",
+"2	c #1E1E1E",
+"3	c #1E1216",
+"4	c #121A17",
+"5	c #1D221F",
+"6	c #162622",
+"7	c #31322E",
+"8	c #1A1618",
+"9	c #161215",
+"0	c #2E2E2B",
+"a	c #1E1A1A",
+"b	c #3C3A36",
+"c	c #43433D",
+"d	c #1A1213",
+"e	c #0A1612",
+"f	c #DEDEEF",
+"g	c #8494A5",
+"h	c #027E72",
+"i	c #ADBDC6",
+"j	c #034A40",
+"                                                                 ......................++@      ####$###                                                                                                                                                       ",
+"                                                                 .%%..%%%.%%%...%%.%%%.+@     ##&+++*+++&$                                                                                                                                                     ",
+"                                                                 .%.%.%...%....%....%..@    ###$&=*-*=***-+#                                                                                                                                                   ",
+"  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;   .%%%.%%..%.....%%..%..    #>##&&-*===*****=&                                                                                                                                                  ",
+"  ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@   .%%..%...%.......%.%..   ##&#&+&-=*-----**-*&                                                                                                                                                 ",
+"  ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@   .%.%.%%%.%%%..%%%..%..  #&+$+=&=**--=-*--*===$                                                                                                                                                ",
+"  ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@   ......................  &++&===*&#,#$===*--***                                                                                                                                                ",
+"  ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@   '''''''''''''''''''''' #=*****&),$#$&&#$***-*=+                                                                                                                                               ",
+"  ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@   '%%''%%%'%%%'''%%'%%%' &***-*&#$&&&=+++=&*-*=-=                                                                                                                                               ",
+"  ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@   '%'%'%'''%''''%''''%''#+-**-=#$+=&++#+&$#&*--**&                                                                                                                                              ",
+"  ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@   '%%%'%%''%'''''%%''%''#*----&&&+&$+&=+##++=*-*-+                                                                                                                                              ",
+"  ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@   '%%''%'''%'''''''%'%''$*=*-*&=&&=&&&=+$&=&+-**-+                                                                                                                                              ",
+"  ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@   '%'%'%%%'%%%''%%%''%''$-*-*=&=+=&&=&+&+=+&#=-*-*                                                                                                                                              ",
+"  ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@   ''''''''''''''''''''''!~{]^/(_:<[}[<|122111{{{34                                                                                                                                              ",
+"  ;+.............+++++++++++.........++''''''''''''''''''''++@   ....................  !/{]{]252|67_2(11111({]{8/                                                                                                                                              ",
+"  ;+.%%..%%%.%%%.+++++++++++..%%.%%%.++'%'''%'%''%'%%%'%%%'++@   .%...%.%..%.%%%.%%%.  :9]]]{12(210(1211(112]{a^1                                                                                                                                              ",
+"  ;+.%.%.%...%...+++++++++++.%....%..++'%%'%%'%''%''%''%'''++@   .%%.%%.%..%..%..%...  b9]{]^(c_(1121((1111]{{d^a                                                                                                                                              ",
+"  ;+.%%%.%%..%...+++++++++++..%%..%..++'%'%'%'%''%''%''%%''++@   .%.%.%.%..%..%..%%..   /9{{{2b!211112(11(1]{]98                                                                                                                                               ",
+"  ;+.%%..%...%...+++++++++++....%.%..++'%'''%'%''%''%''%'''++@   .%...%.%..%..%..%...   !{]{]{11(!(2111112]{]{{/                                                                                                                                               ",
+"  ;+.%.%.%%%.%%%.+++++++++++.%%%..%..++'%'''%''%%'''%''%%%'++@   .%...%..%%...%..%%%.    /9{]{]/2((1(((11]{{]{{                                                                                                                                                ",
+"  ;+.............+++++++++++.........++''''''''''''''''''''++@   ....................    0~~{{{{]/((122]]{]]//a                                                                                                                                                ",
+"  ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@   ''''''''''''''''''''     1]{]]{{{]{{{]{{{]{]/                                                                                                                                                 ",
+"  ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@   '%'''%'%''%'%%%'%%%'      2]{]{{]{{{{{{]{]{/                                                                                                                                                  ",
+"  ;;;;;;;;;;;;;;;;;;+++++++++++++++++++++++++++++++++++...+++@   '%%'%%'%''%''%''%'''       1~/d8^~~^d/99{^/                                                                                                                                                   ",
+"                   ;++++++++++++++++++++++++++++++++++.+++.++@   '%'%'%'%''%''%''%%''         58a9{{9^9e{^                                                                                                                                                     ",
+"                   ;++++++++++++++++++++++++++++++++++.+++.++@   '%'''%'%''%''%''%'''           2~^33^^2                                                                                                                                                       ",
+"  ffffffg ffffffg  ;++++++++++++++++++++++++++++++++++h+++h++@   '%'''%''%%'''%''%%%'                                                                                                                                                                          ",
+"  fiiiii; fiiiii;  ;++++++++++++++++++++++++++++++++++.+++.++@   ''''''''''''''''''''                                                                                                                                                                          ",
+"  fiii;i; fi;iii;  ;++++++++++++++++++++++++++++++++++.+++.++@                                                                                                                                                                                                 ",
+"  fii;;i; fi;;ii;  ;+++++++++++++++++++++++++++++++++++...+++@   ffffffg ffffffg+ffffffg                                                                                                                                                                       ",
+"  fi;;;i; fi;;;i;  ;+++++++++++++++++++++++++++++++++++++++++@   fiiiii; fiiiii;+fiiiii;                                                                                                                                                                       ",
+"  fi;;;i; fi;;;i;  ;+++++++++++++++++++++++++++++++++++++++++@   fiiiii; fiii;i;+fi;iii;                                                                                                                                                                       ",
+"  fii;;i; fi;;ii;  ;+++++++++++++++++++++++@@@@@@@@@@@@@@@@@@@   fii;ii; fii;;i;+fi;;ii;                                                                                                                                                                       ",
+"  fiii;i; fi;iii;  ;+++++++++++++++++++++@@                      fii;ii; fi;;;i;+fi;;;i;                                                                                                                                                                       ",
+"  fiiiii; fiiiii;  ;+++++++++++++++++++@@                        fii;ii; fi;;;i;+fi;;;i;                                                                                                                                                                       ",
+"  g;;;;;; g;;;;;;  ;++++++++++++++++++@      ####$###            fii;ii; fii;;i;+fi;;ii;                                                                                                                                                                       ",
+"                   ;+++++++++++++++++@     ##&+++*+++&$          fii;ii; fiii;i;+fi;iii;                                                                                                                                                                       ",
+"                   ;++++++++++++++++@    ###$&=*-*=***-+#        fii;ii; fiiiii;+fiiiii;                                                                                                                                                                       ",
+"  ;;;;;;;;;;;;;;;;;;+++++++++++++++@    #>##&&-*===*****=&       fii;ii; g;;;;;;+g;;;;;;                                                                                                                                                                       ",
+"  ;++++++++++++++++++++++++++++++++@   ##&#&+&-=*-----**-*&      fii;ii;                                                                                                                                                                                       ",
+"  ;+.+++++++++++++++++++++++..++++@   #&+$+=&=**--=-*--*===$     fii;ii;                                                                                                                                                                                       ",
+"  ;+.+++++++++++++++++++++++.+.+++@   &++&===*&#,#$===*--***     fiiiii;                                                                                                                                                                                       ",
+"  ;+.+++++++++++++++++++++++..+++@   #=*****&),$#$&&#$***-*=+    fiiiii;                                                                                                                                                                                       ",
+"  ;+.+++++++++ffffffg+++++++.+.++@   &***-*&#$&&&=+++=&*-*=-=    g;;;;;;                                                                                                                                                                                       ",
+"  ;+...+++++++fiiiii;+++++++.+.++@  #+-**-=#$+=&++#+&$#&*--**&                                                                                                                                                                                                 ",
+"  ;+++++++++++fiiiii;++++++++++++@  #*----&&&+&$+&=+##++=*-*-+   .+++++++++++++++++++++++..+                                                                                                                                                                   ",
+"  ;++.++++++++fii;ii;++++++++.+++@  $*=*-*&=&&=&&&=+$&=&+-**-+   .+++++++++++++++++++++++.+.                                                                                                                                                                   ",
+"  ;++.+++.++++fii;ii;++++.+++.+++@  $-*-*=&=+=&&=&+&+=+&#=-*-*   .+++++++++++++++++++++++..+                                                                                                                                                                   ",
+"  ;++.+++.+++.fii;ii;.+++.+++.+++@  !~{]^/(_:<[}[<|122111{{{34   .+++++++++++++++++++++++.+.                                                                                                                                                                   ",
+"  ;++.+++.+++.fii;ii;.+++.+++.+++@  !/{]{]252|67_2(11111({]{8/   ...+++++++++++++++++++++.+.                                                                                                                                                                   ",
+"  ;+++++++++++fii;ii;++++++++++++@  :9]]]{12(210(1211(112]{a^1   +++++++++++++++++++++++++++                                                                                                                                                                   ",
+"  ;++@@@@@@@@@fii;ii;@@@@@@@@@+++@  b9]{]^(c_(1121((1111]{{d^a   +.+++++++++++++++++++++++.+                                                                                                                                                                   ",
+"  ;++ at ++++++++fii;ii;++++++++;+++@   /9{{{2b!211112(11(1]{]98    +.+++.+++++++++++++++.+++.+                                                                                                                                                                   ",
+"  ;++ at ++++++++fii;ii;++++++++;+++@   !{]{]{11(!(2111112]{]{{/    +.+++.+++.+++++++.+++.+++.+                                                                                                                                                                   ",
+"  ;++;;;;;;;;;fii;ii;;;;;;;;;;+++@    /9{]{]/2((1(((11]{{]{{     +.+++.+++.+++.+++.+++.+++.+                                                                                                                                                                   ",
+"  ;+++++++++++fiiiii;++++++++++++@    0~~{{{{]/((122]]{]]//a     +++++++++++++++++++++++++++                                                                                                                                                                   ",
+"  ;+++++++++++fiiiii;++++++++++++@     1]{]]{{{]{{{]{{{]{]/      +@@@@@@@@@@@@@@@@@@@@@@@@@+                                                                                                                                                                   ",
+"  ;+++++++++++g;;;;;;++++++++++++@      2]{]{{]{{{{{{]{]{/       + at +++++++++++++++++++++++;+                                                                                                                                                                   ",
+"  ;++++++++++++++++++++++++++++++@       1~/d8^~~^d/99{^/        + at +++++++++++++++++++++++;+                                                                                                                                                                   ",
+"  ;++++++++++++++++++++++++++++++@         58a9{{9^9e{^          +;;;;;;;;;;;;;;;;;;;;;;;;;+                                                                                                                                                                   ",
+"  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@           2~^33^^2            +++++++++++++++++++++++++++                                                                                                                                                                   ",
+"                                                                 +++++++++++++++++++++++++++                                                                                                                                                                   ",
+"                                                                 +++++++++++++++++++++++++++                                                                                                                                                                   ",
+"                                                                 +++++++++++++++++++++++++++                                                                                                                                                                   ",
+"                                                                 +++++++++++++++++++++++++++                                                                                                                                                                   ",
+"                                                                                                                                                                                                                                                               ",
+"                                                                                                                                                                                                                                                               ",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++                                                                                                                                                                               ",
+"++...++++++h+h...h+h...h+h+++h+h...h+h...h+h...h+h...h+h...h++++++++++++++++++++                                                                                                                                                                               ",
+"+.+++.+++++.+++++.+++++.+.+++.+.+++++.+++++++++.+.+++.+.+++.++++++++++++++++++++                                                                                                                                                                               ",
+"+.+++.+++++.+++++.+++++.+.+++.+.+++++.+++++++++.+.+++.+.+++.++++++++++++++++++++                                                                                                                                                                               ",
+"+h+++h+++++h+h...h++...h+h...h+h...h+h...h+++++h+j...j+h...h+h...h++++++++++++++                                                                                                                                                                               ",
+"+.+++.+++++.+.+++++++++.+++++.+++++.+.+++.+++++.+.+++.+++++.++++++++++++++++++++                                                                                                                                                                               ",
+"+.+++.+++++.+.+++++++++.+++++.+++++.+.+++.+++++.+.+++.+++++.+++++++++++++++hh+++                                                                                                                                                                               ",
+"++...++++++.+h...h+h...h+++++h+h...h+h...h+++++h+h...h+h...h+++++++++++++++..+++                                                                                                                                                                               ",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++                                                                                                                                                                               ",
+"                                                                                                                                                                                                                                                               ",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++                                                                                               ",
+"+j...j+h...++h...h+h...++h...h+h...h+h...h+h+++h+++h+++++++h+h+++h+h+++++.+++.+h...++h...h+h...h+h...h+h...h+h...h+h...h+h+++h+h+++h+h+++h+h+++h+h+++h+h...h++++                                                                                               ",
+"+.+++.+.+++.+.+++++.+++.+.+++++.+++++.+++++.+++.+++.+++++++.+.+++.+.+++++..+..+.+++.+.+++.+.+++.+.+++.+.+++.+.+++++++.+++.+++.+.+++.+.+++.+.+++.+.+++.+++++.++++                                                                                               ",
+"+.+++.+.+++.+.+++++.+++.+.+++++.+++++.+++++.+++.+++.+++++++.+.++.j+.+++++.+.+.+.+++.+.+++.+.+++.+.+++.+.+++.+.+++++++.+++.+++.+.+++.+.+++.+j.+.j+.+++.++++.j++++                                                                                               ",
+"+h...h+h...++h+++++h+++h+h...++h...++hj..h+h...h+++h+++++++h+h..j++h+++++h+++h+h+++h+h+++h+h...h+h.++h+h...++h...h+++h+++h+++h+h+++h+h+++h++j.j++h...h++j.j+++++                                                                                               ",
+"+.+++.+.+++.+.+++++.+++.+.+++++.+++++.+++.+.+++.+++.+++++++.+.++.j+.+++++.+++.+.+++.+.+++.+.+++++.+.+.+.+++.+++++.+++.+++.+++.+.+++.+.+.+.+j.+.j+++++.+j.+++++++                                                                                               ",
+"+.+++.+.+++.+.+++++.+++.+.+++++.+++++.+++.+.+++.+++.+++++++.+.+++.+.+++++.+++.+.+++.+.+++.+.+++++.++..+.+++.+++++.+++.+++.+++.+.+++.+..+..+.+++.+++++.+.++++++++                                                                                               ",
+"+h+++h+h...++h...h+....++h...h+.+++++h...h+h+++h+++.+++h...h+h++jh+h...j+h+++h+.+++.+h...h+h+++++h...h+h+++h+h...h+++h+++j....++...++.+++.+h+++.+h...h+h...h++++                                                                                               ",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++                                                                                               ",
+"                                                                                                                                                                                                                                                               ",
+"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"                                                                                                                                                                                                                                                               ",
+"                                                                                                                                                                                                                                                               ",
+"                                                                                                                                                                                                                                                               ",
+"                                                                                                                                                                                                                                                               ",
+"                                                                                                                                                                                                                                                               ",
+"                                                                                                                                                                                                                                                               ",
+"                                                                                                                                                                                                                                                               ",
+"                                                                                                                                                                                                                                                               ",
+"                                                                                                                                                                                                                                                               ",
+"                                                                                                                                                                                                                                                               ",
+"                                                                                                                                                                                                                                                               ",
+"                                                                                                                                                                                                                                                               ",
+"                                                                                                                                                                                                                                                               "};
diff --git a/include/misc.h b/include/misc.h
new file mode 100644
index 0000000..8118007
--- /dev/null
+++ b/include/misc.h
@@ -0,0 +1,26 @@
+/* WMix 3.0 -- a mixer using the OSS mixer API.
+ * Copyright (C) 2000, 2001
+ *	Daniel Richard G. <skunk at mit.edu>,
+ *	timecop <timecop at japan.co.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+void		lr_to_vb	(float left, float right, float *volume, float *balance);
+void		vb_to_lr	(float volume, float balance, float *left, float *right);
+double		get_current_time(void);
+void		add_region	(int index, int x, int y, int width, int height);
+int		check_region	(int x, int y);
+void		config_read	(void);
diff --git a/include/mixer.h b/include/mixer.h
new file mode 100644
index 0000000..b5680d7
--- /dev/null
+++ b/include/mixer.h
@@ -0,0 +1,77 @@
+/* WMix 3.0 -- a mixer using the OSS mixer API.
+ * Copyright (C) 2000, 2001
+ *	Daniel Richard G. <skunk at mit.edu>,
+ *	timecop <timecop at japan.co.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* WMVolume mixer interface
+ *
+ * Some notes:
+ *
+ * - The volume argument is a floating-point value between 0 (no sound) and
+ *   1 (full sound) inclusively.
+ *
+ * - The balance argument is a floating-point value betwen -1 (full left)
+ *   and 1 (full right) inclusively. A value of 0 indicates centered sound.
+ *   If the device does not natively support a balance parameter, use the
+ *   lr_to_vb() and vb_to_lr() functions in misc.c.
+ *
+ * - The volume and balance arguments passed to these functions, given that
+ *   they are (usually 32-bit) floating-point values, may be capable of
+ *   specifying the audio parameters at a finer resolution than the
+ *   hardware itself. However, the mixer driver must not quantize the
+ *   volume and balance levels returned by the mixer_get_*() routines.
+ *
+ *   Example: Suppose the mixer device supports specifying the volume as an
+ *   integer in the range of 0 to 100. If mixer_set_volume() is called with
+ *   an argument of 0.7351, then the device will be set to a volume of 74,
+ *   yet subsequent calls to mixer_get_volume() (assuming mixer state has
+ *   not changed since) will return 0.7351. Only if the mixer state is
+ *   changed by another program (that, say, sets the volume to 51) is the
+ *   value returned by this interface quantized (in such a case returning
+ *   0.51).
+ *
+ *   The reason why this quantization must be avoided whenever possible is
+ *   that otherwise, a large number of minuscule increases to the volume
+ *   level will have no cumulative effect. Calling mixer_set_volume_rel()
+ *   ten thousand times with an argument of 0.0001 should successfully
+ *   increase the volume to its maximum, even if the device actually
+ *   supports only 64 discrete volume levels.
+ *
+ * - Muting must occur independently of the volume level.
+ */
+
+void		mixer_init			(const char *mixer_device, bool verbose);
+bool		mixer_is_changed		(void);
+int		mixer_get_channel_count		(void);
+int		mixer_get_channel		(void);
+const char *	mixer_get_channel_name		(void);
+const char *	mixer_get_short_name		(void);
+void		mixer_set_channel		(int channel);
+void		mixer_set_channel_rel		(int delta_channel);
+float		mixer_get_volume		(void);
+void		mixer_set_volume		(float volume);
+void		mixer_set_volume_rel		(float delta_volume);
+float		mixer_get_balance		(void);
+void		mixer_set_balance		(float balance);
+void		mixer_set_balance_rel		(float delta_balance);
+void		mixer_toggle_mute		(void);
+void		mixer_toggle_rec		(void);
+bool		mixer_is_muted			(void);
+bool		mixer_is_stereo			(void);
+bool		mixer_is_rec			(void);
+bool		mixer_can_rec			(void);
diff --git a/include/ui_x.h b/include/ui_x.h
new file mode 100644
index 0000000..34cab68
--- /dev/null
+++ b/include/ui_x.h
@@ -0,0 +1,40 @@
+/* WMix 3.0 -- a mixer using the OSS mixer API.
+ * Copyright (C) 2000, 2001
+ *	Daniel Richard G. <skunk at mit.edu>,
+ *	timecop <timecop at japan.co.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+void		dockapp_init	(Display *x_display);
+
+void		new_window	(char *name, int width, int height);
+
+void		new_osd		(int width, int height);
+void		update_osd	(float volume, bool up);
+void		map_osd		(void);
+void		unmap_osd	(void);
+bool		osd_mapped	(void);
+
+void		ui_update	(void);
+void		redraw_window	(void);
+
+int		blit_string	(const char *text);
+void		scroll_text	(int x, int y, int width, bool reset);
+void		set_cursor	(int type);
+void		knob_turn	(float delta);
+void		slider_move	(float delta);
+
+unsigned long	get_color	(Display *display, char *color_name);
diff --git a/misc.c b/misc.c
new file mode 100644
index 0000000..45f58a4
--- /dev/null
+++ b/misc.c
@@ -0,0 +1,163 @@
+/* WMix 3.0 -- a mixer using the OSS mixer API.
+ * Copyright (C) 2000, 2001
+ *	Daniel Richard G. <skunk at mit.edu>,
+ *	timecop <timecop at japan.co.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "include/common.h"
+#include "include/misc.h"
+
+typedef struct {
+    int enable;
+    int x;
+    int y;
+    int width;
+    int height;
+} MRegion;
+MRegion mr[16];
+
+extern Config config;
+
+/* Converts separate left and right channel volumes (each in [0, 1]) to
+ * volume and balance values. (Volume is in [0, 1], balance is in [-1, 1])
+ */
+void lr_to_vb(float left, float right, float *volume, float *balance)
+{
+    assert((left >= 0.0) && (right >= 0.0));
+
+    *volume = MAX(left, right);
+
+    if (left > right)
+	*balance = -1.0 + right / left;
+    else if (right > left)
+	*balance = 1.0 - left / right;
+    else
+	*balance = 0.0;
+}
+
+/* Performs the reverse calculation of lr_to_vb()
+ */
+void vb_to_lr(float volume, float balance, float *left, float *right)
+{
+/*    *left = volume; *right = volume; return; // XXX */
+
+    *left = volume * (1.0 - MAX(0.0, balance));
+    *right = volume * (1.0 + MIN(0.0, balance));
+}
+
+double get_current_time(void)
+{
+    struct timeval tv;
+    double t;
+
+    gettimeofday(&tv, NULL);
+
+    t = (double)tv.tv_sec;
+    t += (double)tv.tv_usec / 1.0e6;
+
+    return t;
+}
+
+void add_region(int index, int x, int y, int width, int height)
+{
+    mr[index].enable = 1;
+    mr[index].x = x;
+    mr[index].y = y;
+    mr[index].width = width;
+    mr[index].height = height;
+}
+
+int check_region(int x, int y)
+{
+    register int i;
+    bool found = false;
+
+    for (i = 0; i < 16 && !found; i++) {
+	if (mr[i].enable && x >= mr[i].x &&
+	    x <= mr[i].x + mr[i].width &&
+	    y >= mr[i].y && y <= mr[i].y + mr[i].height)
+	    found = true;
+    }
+    if (!found)
+	return -1;
+    return (i - 1);
+}
+
+void config_read(void)
+{
+    FILE *fp;
+    char buf[512];
+    char *ptr;
+
+    if (config.file == NULL)
+	return;
+
+    fp = fopen(config.file, "r");
+    if (!fp)
+	return;
+
+    while (fgets(buf, 512, fp)) {
+	if ((ptr = strstr(buf, "mousewheel="))) {
+	    ptr += 11;
+	    config.mousewheel = atoi(ptr);
+	}
+	if ((ptr = strstr(buf, "scrolltext="))) {
+	    ptr += 11;
+	    config.scrolltext = atoi(ptr);
+	}
+	if ((ptr = strstr(buf, "osd="))) {
+	    ptr += 4;
+	    config.osd = atoi(ptr);
+	}
+	if ((ptr = strstr(buf, "osdcolor="))) {
+	    char *end;
+	    ptr += 9;
+	    end = strchr(ptr, '\n');
+	    ptr[end - ptr] = '\0';
+	    if (config.osd_color)
+		free(config.osd_color);
+	    config.osd_color = strdup(ptr);
+	}
+	if ((ptr = strstr(buf, "wheelstep="))) {
+	    ptr += 10;
+	    /* detect old style config */
+	    if (atoi(ptr) > 1)
+		config.scrollstep = (float)atoi(ptr) / 100.0;
+	    else
+		config.scrollstep = atof(ptr);
+	}
+	if ((ptr = strstr(buf, "wheelbtn1="))) {
+	    ptr += 10;
+	    config.wheel_button_up = atoi(ptr);
+	}
+	if ((ptr = strstr(buf, "wheelbtn2="))) {
+	    ptr += 10;
+	    config.wheel_button_down = atoi(ptr);
+	}
+    }
+    fclose(fp);
+}
diff --git a/mixer-oss.c b/mixer-oss.c
new file mode 100644
index 0000000..51b13b2
--- /dev/null
+++ b/mixer-oss.c
@@ -0,0 +1,408 @@
+/* WMix 3.0 -- a mixer using the OSS mixer API.
+ * Copyright (C) 2000, 2001
+ *	Daniel Richard G. <skunk at mit.edu>,
+ *	timecop <timecop at japan.co.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+
+#include "include/common.h"
+#include "include/misc.h"
+#include "include/mixer.h"
+
+#define WMVOLUME_CHANNEL_NAMES \
+	"Master volume", \
+	"Bass", \
+	"Treble", \
+	"FM Synth volume", \
+	"PCM Wave volume", \
+	"PC Speaker", \
+	"Line In level", \
+	"Microphone level", \
+	"CD volume", \
+	"Recording monitor", \
+	"PCM Wave 2 volume", \
+	"Recording volume", \
+	"Input gain", \
+	"Output gain", \
+	"Line In 1", \
+	"Line In 2", \
+	"Line In 3", \
+	"Digital In 1", \
+	"Digital In 2", \
+	"Digital In 3", \
+	"Phone input", \
+	"Phone output", \
+	"Video volume", \
+	"Radio volume", \
+	"Monitor volume"
+
+#ifdef OSS_CHANNEL_NAMES
+#define CHANNEL_NAMES SOUND_DEVICE_LABELS
+#else
+#define CHANNEL_NAMES WMVOLUME_CHANNEL_NAMES
+#endif
+
+typedef struct {
+    const char *name;		/* name of channel */
+    const char *sname;		/* short name of the channel */
+    int dev;			/* channel device number */
+    int prev_dev_lr_volume;	/* last known left/right volume
+				 * (in device format) */
+    float volume;		/* volume, in [0, 1] */
+    float balance;		/* balance, in [-1, 1] */
+    bool can_record;		/* capable of recording? */
+    bool is_recording;		/* is it recording? */
+    bool is_stereo;		/* capable of stereo? */
+    bool is_muted;		/* is it muted? */
+} MixerChannel;
+
+static const char *channel_names[] = { CHANNEL_NAMES };
+static const char *short_names[] = SOUND_DEVICE_LABELS;
+
+static int mixer_fd;
+
+static MixerChannel mixer[SOUND_MIXER_NRDEVICES];
+static int n_channels = 0;
+static int cur_channel = 0;
+
+static int prev_modify_counter = -1;
+
+static bool get_mixer_state(void)
+{
+    struct mixer_info mixer_info;
+    int dev_lr_volume, dev_left_volume, dev_right_volume;
+    float left, right;
+    int srcmask;
+    int ch;
+
+    /* to really keep track of updates */
+    static MixerChannel oldmixer[SOUND_MIXER_NRDEVICES];
+
+    ioctl(mixer_fd, SOUND_MIXER_INFO, &mixer_info);
+
+    if (mixer_info.modify_counter == prev_modify_counter)
+	/*
+	 * Mixer state has not changed
+	 */
+	return false;
+
+    /* Mixer state was changed by another program, so we need
+     * to update. As OSS cannot tell us specifically which
+     * channels changed, we read all of them in.
+     *
+     * prev_modify_counter was initialized to -1, so this part
+     * is guaranteed to run the first time this routine is
+     * called.
+     */
+
+    if (ioctl(mixer_fd, SOUND_MIXER_READ_RECSRC, &srcmask) == -1) {
+	fprintf(stderr, "mixer read failed\n");
+	perror(NULL);
+	exit(EXIT_FAILURE);
+    }
+
+    for (ch = 0; ch < n_channels; ch++) {
+	if (ioctl(mixer_fd, MIXER_READ(mixer[ch].dev), &dev_lr_volume) ==
+	    -1) {
+	    fprintf(stderr, "mixer read failed\n");
+	    exit(EXIT_FAILURE);
+	}
+
+	if (dev_lr_volume != mixer[ch].prev_dev_lr_volume) {
+	    dev_left_volume = dev_lr_volume & 0xFF;
+	    dev_right_volume = dev_lr_volume >> 8;
+
+	    if ((dev_left_volume > 0) || (dev_right_volume > 0))
+		mixer[ch].is_muted = false;
+
+	    left = (float) dev_left_volume / 100.0;
+	    right = (float) dev_right_volume / 100.0;
+
+	    if (!mixer[ch].is_muted) {
+		if (mixer[ch].is_stereo)
+		    lr_to_vb(left,
+			     right, &mixer[ch].volume, &mixer[ch].balance);
+		else {
+		    mixer[ch].volume = left;
+		    mixer[ch].balance = 0.0;
+		}
+
+		mixer[ch].prev_dev_lr_volume = dev_lr_volume;
+	    }
+	}
+	mixer[ch].is_recording = ((1 << mixer[ch].dev) & srcmask) != 0;
+    }
+    prev_modify_counter = mixer_info.modify_counter;
+    /* check if this was due to OSS stupidity or if we really changed */
+    if (!memcmp(&mixer, &oldmixer, sizeof(mixer))) {
+	memcpy(&oldmixer, &mixer, sizeof(mixer));
+	return false;
+    }
+    memcpy(&oldmixer, &mixer, sizeof(mixer));
+    return true;
+}
+
+static void set_mixer_state(void)
+{
+    float left, right;
+    int dev_left_volume, dev_right_volume, dev_lr_volume;
+
+    if (mixer[cur_channel].is_muted) {
+	left = 0.0;
+	right = 0.0;
+    } else
+	vb_to_lr(mixer[cur_channel].volume,
+		 mixer[cur_channel].balance, &left, &right);
+
+    dev_left_volume = (int) (100.0 * left);
+    dev_right_volume = (int) (100.0 * right);
+    dev_lr_volume = (dev_right_volume << 8) | dev_left_volume;
+    ioctl(mixer_fd, MIXER_WRITE(mixer[cur_channel].dev), &dev_lr_volume);
+}
+
+static void get_record_state(void)
+{
+    int srcmask;
+    int ch;
+
+    if (ioctl(mixer_fd, SOUND_MIXER_READ_RECSRC, &srcmask) == -1) {
+	fprintf(stderr, "mixer read failed\n");
+	perror(NULL);
+	exit(EXIT_FAILURE);
+    }
+
+    for (ch = 0; ch < n_channels; ch++) {
+	mixer[ch].is_recording = ((1 << mixer[ch].dev) & srcmask) != 0;
+    }
+}
+
+static void set_record_state(void)
+{
+    int srcmask;
+
+    if (ioctl(mixer_fd, SOUND_MIXER_READ_RECSRC, &srcmask) == -1) {
+	fputs("error: recording source mask ioctl failed\n", stderr);
+	exit(EXIT_FAILURE);
+    }
+
+    if (((1 << mixer[cur_channel].dev) & srcmask) == 0)
+	srcmask |= (1 << mixer[cur_channel].dev);
+    else
+	srcmask &= ~(1 << mixer[cur_channel].dev);
+
+    if (ioctl(mixer_fd, SOUND_MIXER_WRITE_RECSRC, &srcmask) == -1) {
+	fputs("error: recording source mask ioctl failed\n", stderr);
+	exit(EXIT_FAILURE);
+    }
+}
+
+void mixer_init(const char *mixer_device, bool verbose)
+{
+    int devmask, srcmask, recmask, stmask;
+    struct mixer_info mixer_info;
+    int count;
+    int mask;
+
+    mixer_fd = open(mixer_device, O_RDWR);
+
+    if (mixer_fd == -1) {
+	fprintf(stderr, "error: cannot open mixer device %s\n",
+		mixer_device);
+	exit(EXIT_FAILURE);
+    }
+
+    if (ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) {
+	fputs("error: device mask ioctl failed\n", stderr);
+	exit(EXIT_FAILURE);
+    }
+
+    if (ioctl(mixer_fd, SOUND_MIXER_READ_RECSRC, &srcmask) == -1) {
+	fputs("error: recording source mask ioctl failed\n", stderr);
+	exit(EXIT_FAILURE);
+    }
+
+    if (ioctl(mixer_fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1) {
+	fputs("error: recording mask ioctl failed\n", stderr);
+	exit(EXIT_FAILURE);
+    }
+
+    if (ioctl(mixer_fd, SOUND_MIXER_READ_STEREODEVS, &stmask) == -1) {
+	fputs("error: stereo mask ioctl failed\n", stderr);
+	exit(EXIT_FAILURE);
+    }
+
+    if (ioctl(mixer_fd, SOUND_MIXER_INFO, &mixer_info) == -1) {
+	fputs("error: could not read mixer info\n", stderr);
+	exit(EXIT_FAILURE);
+    }
+
+    if (verbose) {
+	printf("%s (%s)\n", mixer_info.name, mixer_info.id);
+	puts("Supported channels:");
+    }
+    for (count = 0; count < SOUND_MIXER_NRDEVICES; count++) {
+	mask = 1 << count;
+	if (mask & devmask) {
+	    mixer[n_channels].name = channel_names[count];
+	    mixer[n_channels].sname = short_names[count];
+	    mixer[n_channels].dev = count;
+	    mixer[n_channels].prev_dev_lr_volume = -1;
+	    mixer[n_channels].can_record = (mask & recmask) != 0;
+	    mixer[n_channels].is_recording = (mask & srcmask) != 0;
+	    mixer[n_channels].is_stereo = (mask & stmask) != 0;
+	    mixer[n_channels].is_muted = false;
+	    ++n_channels;
+	    if (verbose)
+		printf("  %d: %s\n", n_channels, channel_names[count]);
+	}
+    }
+    get_mixer_state();
+}
+
+bool mixer_is_changed(void)
+{
+    return get_mixer_state();
+}
+
+int mixer_get_channel_count(void)
+{
+    return n_channels;
+}
+
+int mixer_get_channel(void)
+{
+    return cur_channel;
+}
+
+const char *mixer_get_channel_name(void)
+{
+    return mixer[cur_channel].name;
+}
+
+const char *mixer_get_short_name(void)
+{
+    return mixer[cur_channel].sname;
+}
+
+void mixer_set_channel(int channel)
+{
+    assert((channel >= 0) && (channel < n_channels));
+
+    cur_channel = channel;
+    get_record_state();
+}
+
+void mixer_set_channel_rel(int delta_channel)
+{
+    cur_channel = (cur_channel + delta_channel) % n_channels;
+    if (cur_channel < 0)
+	cur_channel += n_channels;
+    get_record_state();
+}
+
+float mixer_get_volume(void)
+{
+    get_mixer_state();
+    return mixer[cur_channel].volume;
+}
+
+void mixer_set_volume(float volume)
+{
+    assert((volume >= 0.0) && (volume <= 1.0));
+
+    mixer[cur_channel].volume = volume;
+    set_mixer_state();
+}
+
+void mixer_set_volume_rel(float delta_volume)
+{
+    mixer[cur_channel].volume += delta_volume;
+    mixer[cur_channel].volume = CLAMP(mixer[cur_channel].volume, 0.0, 1.0);
+    set_mixer_state();
+}
+
+float mixer_get_balance(void)
+{
+    get_mixer_state();
+    return mixer[cur_channel].balance;
+}
+
+void mixer_set_balance(float balance)
+{
+    assert((balance >= -1.0) && (balance <= 1.0));
+
+    if (mixer[cur_channel].is_stereo) {
+	mixer[cur_channel].balance = balance;
+	set_mixer_state();
+    }
+}
+
+void mixer_set_balance_rel(float delta_balance)
+{
+    if (mixer[cur_channel].is_stereo) {
+	mixer[cur_channel].balance += delta_balance;
+	mixer[cur_channel].balance =
+	    CLAMP(mixer[cur_channel].balance, -1.0, 1.0);
+	set_mixer_state();
+    }
+}
+
+void mixer_toggle_mute(void)
+{
+    mixer[cur_channel].is_muted = !mixer[cur_channel].is_muted;
+
+    set_mixer_state();
+}
+
+void mixer_toggle_rec(void)
+{
+    if (mixer[cur_channel].can_record) {
+	mixer[cur_channel].is_recording = !mixer[cur_channel].is_recording;
+	set_record_state();
+	get_record_state();
+    }
+}
+
+bool mixer_is_muted(void)
+{
+    return mixer[cur_channel].is_muted;
+}
+
+bool mixer_is_stereo(void)
+{
+    return mixer[cur_channel].is_stereo;
+}
+
+bool mixer_is_rec(void)
+{
+    return mixer[cur_channel].is_recording;
+}
+
+bool mixer_can_rec(void)
+{
+    return mixer[cur_channel].can_record;
+}
diff --git a/sample.wmixrc b/sample.wmixrc
new file mode 100644
index 0000000..a22fb9a
--- /dev/null
+++ b/sample.wmixrc
@@ -0,0 +1,16 @@
+# WMix configuration file
+
+# use mousewheel
+mousewheel=1
+# scroll the system messages
+scrolltext=1
+# display OSD
+osd=1
+# color of the OSD (from rgb.txt or #xxxxxx)
+osdcolor=green
+# which mousewheel button is "up"
+wheelbtn1=4
+# which mousewheel button is "down"
+wheelbtn2=5
+# the step for mousewheel adjustment (in percent, 3 or 0.03)
+wheelstep=3
diff --git a/ui_x.c b/ui_x.c
new file mode 100644
index 0000000..9d7c2fa
--- /dev/null
+++ b/ui_x.c
@@ -0,0 +1,604 @@
+/* WMix 3.0 -- a mixer using the OSS mixer API.
+ * Copyright (C) 2000, 2001
+ *	Daniel Richard G. <skunk at mit.edu>,
+ *	timecop <timecop at japan.co.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <math.h>
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/shape.h>
+#include <X11/xpm.h>
+#include <X11/cursorfont.h>
+
+#include "include/master.xpm"
+#include "include/led-on.xpm"
+#include "include/led-off.xpm"
+
+#include "include/common.h"
+#include "include/misc.h"
+#include "include/mixer.h"
+#include "include/ui_x.h"
+
+#ifndef PI
+#define PI M_PI
+#endif
+
+#define LED_POS_RADIUS 8
+#define KNOB_CENTER_X 49
+#define KNOB_CENTER_Y 48
+#define LED_WIDTH 6
+#define LED_HEIGHT 6
+
+typedef struct _Dockapp Dockapp;
+
+struct _Dockapp {
+    int width;
+    int height;
+    Pixmap pixmap;
+    Pixmap mask;
+    GC gc;
+    int ctlength;
+
+    Window osd;
+    GC osd_gc;
+    int osd_width;
+    bool osd_mapped;
+
+};
+
+extern Config config;
+
+static Pixmap led_on_pixmap;
+static Pixmap led_on_mask;
+static Pixmap led_off_pixmap;
+static Pixmap led_off_mask;
+
+#define copy_xpm_area(x, y, w, h, dx, dy) \
+    XCopyArea(display, dockapp.pixmap, dockapp.pixmap, dockapp.gc, \
+	    x, y, w, h, dx, dy)
+
+/* local prototypes */
+static Cursor create_null_cursor(Display *x_display);
+
+/* ui stuff */
+static void draw_stereo_led(void);
+static void draw_rec_led(void);
+static void draw_mute_led(void);
+static void draw_percent(void);
+static void draw_knob(float volume);
+static void draw_slider(float offset);
+
+/* global variables */
+static Dockapp dockapp;
+static Display *display;
+static Window win;
+static Window iconwin;
+static Cursor hand_cursor;
+static Cursor null_cursor;
+static Cursor norm_cursor;
+static Cursor bar_cursor;
+
+/* public methods */
+void dockapp_init(Display *x_display)
+{
+    display = x_display;
+}
+
+void redraw_window(void)
+{
+    XCopyArea(display, dockapp.pixmap, iconwin, dockapp.gc,
+	      0, 0, dockapp.width, dockapp.height, 0, 0);
+    XCopyArea(display, dockapp.pixmap, win, dockapp.gc,
+	      0, 0, dockapp.width, dockapp.height, 0, 0);
+}
+
+void ui_update(void)
+{
+    draw_stereo_led();
+    draw_rec_led();
+    draw_mute_led();
+    draw_knob(mixer_get_volume());
+    draw_slider(mixer_get_balance());
+    redraw_window();
+}
+
+void knob_turn(float delta)
+{
+    mixer_set_volume_rel(delta);
+    draw_knob(mixer_get_volume());
+    redraw_window();
+}
+
+void slider_move(float delta)
+{
+    mixer_set_balance_rel(delta);
+    draw_slider(mixer_get_balance());
+    redraw_window();
+}
+
+int blit_string(const char *text)
+{
+    register int i;
+    register int c;
+    register int k;
+
+    k = 0;
+    copy_xpm_area(0, 87, 256, 9, 0, 96);
+
+    for (i = 0; text[i] || i > 31; i++) {
+	c = toupper(text[i]);
+	if (c == '-') {
+	    copy_xpm_area(60, 67, 6, 8, k, 96);
+	    k += 6;
+	}
+	if (c == ' ') {
+	    copy_xpm_area(66, 67, 6, 8, k, 96);
+	    k += 6;
+	}
+	if (c == '.') {
+	    copy_xpm_area(72, 67, 6, 8, k, 96);
+	    k += 6;
+	}
+	if (c >= 'A' && c <= 'Z') {	/* letter */
+	    c = c - 'A';
+	    copy_xpm_area(c * 6, 77, 6, 8, k, 96);
+	    k += 6;
+	} else if (c >= '0' && c <= '9') {	/* number */
+	    c = c - '0';
+	    copy_xpm_area(c * 6, 67, 6, 8, k, 96);
+	    k += 6;
+	}
+    }
+    dockapp.ctlength = k;
+    return k;
+}
+
+void scroll_text(int x, int y, int width, bool reset)
+{
+    static int pos;
+    static int first;
+    static int stop;
+
+    /* no text scrolling at all */
+    if (!config.scrolltext) {
+	if (!reset)
+	    return;
+	copy_xpm_area(0, 96, 58, 9, x, y);
+	redraw_window();
+	return;
+    }
+
+    if (reset) {
+	pos = 0;
+	first = 0;
+	stop = 0;
+	copy_xpm_area(0, 87, width, 9, x, y);
+    }
+
+    if (stop) {
+	return;
+    }
+
+    if ((first == 0) && pos == 0) {
+	pos = width;
+	first = 1;
+    }
+
+    if (pos < -(dockapp.ctlength)) {
+	first = 1;
+	pos = width;
+	stop = 1;
+	return;
+    }
+    pos -= 2;
+
+    if (pos > 0) {
+	copy_xpm_area(0, 87, pos, 9, x, y);	/* clear */
+	copy_xpm_area(0, 96, width - pos, 9, x + pos, y);
+    } else {			/* don't need to clear, already in text */
+	copy_xpm_area(abs(pos), 96, width, 9, x, y);
+    }
+    redraw_window();
+    return;
+}
+
+void new_window(char *name, int width, int height)
+{
+    XpmAttributes attr;
+    Pixel fg, bg;
+    XGCValues gcval;
+    XSizeHints sizehints;
+    XClassHint classhint;
+    XWMHints wmhints;
+    XTextProperty wname;
+
+    dockapp.width = width;
+    dockapp.height = height;
+
+    sizehints.flags = USSize | USPosition;
+    sizehints.x = 0;
+    sizehints.y = 0;
+    sizehints.width = width;
+    sizehints.height = height;
+
+    fg = BlackPixel(display, DefaultScreen(display));
+    bg = WhitePixel(display, DefaultScreen(display));
+
+    win = XCreateSimpleWindow(display, DefaultRootWindow(display),
+			      sizehints.x, sizehints.y,
+			      sizehints.width, sizehints.height, 1, fg,
+			      bg);
+
+    iconwin = XCreateSimpleWindow(display, win, sizehints.x, sizehints.y,
+				  sizehints.width, sizehints.height, 1, fg,
+				  bg);
+
+    XSetWMNormalHints(display, win, &sizehints);
+    classhint.res_name = name;
+    classhint.res_class = name;
+    XSetClassHint(display, win, &classhint);
+
+#define INPUT_MASK \
+    ButtonPressMask \
+    | ExposureMask \
+    | ButtonReleaseMask \
+    | PointerMotionMask \
+    | LeaveWindowMask \
+    | StructureNotifyMask
+
+    XSelectInput(display, win, INPUT_MASK);
+    XSelectInput(display, iconwin, INPUT_MASK);
+
+#undef INPUT_MASk
+
+    XStringListToTextProperty(&name, 1, &wname);
+    XSetWMName(display, win, &wname);
+
+    gcval.foreground = fg;
+    gcval.background = bg;
+    gcval.graphics_exposures = 0;
+
+    dockapp.gc =
+	XCreateGC(display, win,
+		  GCForeground | GCBackground | GCGraphicsExposures,
+		  &gcval);
+
+    attr.exactColors = 0;
+    attr.alloc_close_colors = 1;
+    attr.closeness = 30000;
+    attr.valuemask = (XpmExactColors | XpmAllocCloseColors | XpmCloseness);
+    if ((XpmCreatePixmapFromData(display, DefaultRootWindow(display),
+				master_xpm, &dockapp.pixmap, &dockapp.mask,
+				&attr) != XpmSuccess) ||
+	    (XpmCreatePixmapFromData(display, DefaultRootWindow(display),
+				led_on_xpm, &led_on_pixmap, &led_on_mask,
+				&attr) != XpmSuccess) ||
+	    (XpmCreatePixmapFromData(display, DefaultRootWindow(display),
+				led_off_xpm, &led_off_pixmap, &led_off_mask,
+				&attr) != XpmSuccess)) {
+	fputs("Cannot allocate colors for the dockapp pixmaps!\n", stderr);
+	exit(EXIT_FAILURE);
+    }
+
+    XShapeCombineMask(display, win, ShapeBounding, 0, 0, dockapp.mask,
+		      ShapeSet);
+    XShapeCombineMask(display, iconwin, ShapeBounding, 0, 0, dockapp.mask,
+		      ShapeSet);
+
+    wmhints.initial_state = WithdrawnState;
+    wmhints.icon_window = iconwin;
+    wmhints.icon_x = sizehints.x;
+    wmhints.icon_y = sizehints.y;
+    wmhints.window_group = win;
+    wmhints.flags =
+	StateHint | IconWindowHint | IconPositionHint | WindowGroupHint;
+    XSetWMHints(display, win, &wmhints);
+
+    hand_cursor = XCreateFontCursor(display, XC_hand2);
+    norm_cursor = XCreateFontCursor(display, XC_left_ptr);
+    bar_cursor = XCreateFontCursor(display, XC_sb_up_arrow);
+    null_cursor = create_null_cursor(display);
+    
+    XMapWindow(display, win);
+}
+
+void new_osd(int width, int height)
+{
+    Window osd;
+    Pixel fg, bg;
+    XGCValues gcval;
+    GC gc;
+    XSizeHints sizehints;
+    XSetWindowAttributes xattributes;
+    int win_layer = 6;
+    XFontStruct *fs = NULL;
+
+    sizehints.flags = USSize | USPosition;
+    sizehints.x = (DisplayWidth(display, 0) - width) / 2;
+    sizehints.y = (DisplayHeight(display, 0) - 120);
+    sizehints.width = width;
+    sizehints.height = height;
+    xattributes.save_under = True;
+    xattributes.override_redirect = True;
+    xattributes.cursor = None;
+
+
+    fg = WhitePixel(display, DefaultScreen(display));
+    bg = BlackPixel(display, DefaultScreen(display));
+
+    osd = XCreateSimpleWindow(display, DefaultRootWindow(display),
+			      sizehints.x, sizehints.y, width, height,
+			      0, fg, bg);
+
+    XSetWMNormalHints(display, osd, &sizehints);
+    XChangeWindowAttributes(display, osd, CWSaveUnder | CWOverrideRedirect,
+			    &xattributes);
+    XStoreName(display, osd, "osd");
+    XSelectInput(display, osd, ExposureMask);
+
+    XChangeProperty(display, osd, XInternAtom(display, "_WIN_LAYER", False),
+	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&win_layer, 1);
+
+    gcval.foreground = get_color(display, config.osd_color);
+    gcval.background = bg;
+    gcval.graphics_exposures = 0;
+
+    /*
+     * -sony-fixed-medium-r-normal--24-170-100-100-c-120-iso8859-1 
+     * -misc-fixed-medium-r-normal--36-*-75-75-c-*-iso8859-* */
+
+    /* try our cool scaled 36pt fixed font */
+    fs = XLoadQueryFont(display,
+	    "-misc-fixed-medium-r-normal--36-*-75-75-c-*-iso8859-*");
+    
+    if (fs == NULL) {
+	/* they don't have it! */
+	/* try our next preferred font (100dpi sony) */
+	fprintf(stderr, "Trying alternate font\n");
+	fs = XLoadQueryFont(display,
+		"-sony-fixed-medium-r-normal--24-*-100-100-c-*-iso8859-*");
+	
+	/* they don't have the sony font either */
+	if (fs == NULL) {
+	    fprintf(stderr, "Trying \"fixed\" font\n");
+	    fs = XLoadQueryFont(display,
+		    "fixed");
+	    /* if they don't have the fixed font, we've got different kind
+	     * of problems */
+	    if (fs == NULL) {
+		fprintf(stderr, "Your X server is probably broken\n");
+		exit(1);
+	    }
+	}
+    }
+    
+    gc =
+	XCreateGC(display, osd,
+		  GCForeground | GCBackground | GCGraphicsExposures,
+		  &gcval);
+    XSetFont(display, gc, fs->fid);
+
+    dockapp.osd = osd;
+    dockapp.osd_gc = gc;
+    dockapp.osd_width = width;
+    dockapp.osd_mapped = false;
+}
+
+void update_osd(float volume, bool up)
+{
+    int i;
+    int foo;
+    static int bar;
+
+    if (config.osd) {
+	foo =
+	    (((dockapp.osd_width / 100) * (volume * 100)) / 20) + 1;
+
+	if ((foo != bar) || up) {
+	    XClearArea(display, dockapp.osd, ((bar - 1) * 20), 30,
+		       (foo * 20), 25, 1);
+	    for (i = 1; i < foo; i++)
+		XFillRectangle(display, dockapp.osd, dockapp.osd_gc,
+			       i * 20, 30, 5, 25);
+	}
+	bar = foo;
+    }
+}
+
+void unmap_osd(void)
+{
+    if (config.osd) {
+	XUnmapWindow(display, dockapp.osd);
+	XFlush(display);
+	dockapp.osd_mapped = false;
+    }
+}
+
+void map_osd(void)
+{
+    if (config.osd) {
+	XMapRaised(display, dockapp.osd);
+	XDrawString(display, dockapp.osd, dockapp.osd_gc, 1, 25,
+		mixer_get_channel_name(), strlen(mixer_get_channel_name()));
+	update_osd(mixer_get_volume(), true);
+	XFlush(display);
+	dockapp.osd_mapped = true;
+    }
+}
+
+bool osd_mapped(void)
+{
+    return dockapp.osd_mapped;
+}
+
+void set_cursor(int type)
+{
+    static int oldtype;
+
+    if (oldtype == type)
+	return;
+
+    switch (type) {
+	case NULL_CURSOR:
+	    XDefineCursor(display, win, null_cursor);
+	    XDefineCursor(display, iconwin, null_cursor);
+	    break;
+	case NORMAL_CURSOR:
+	    XDefineCursor(display, win, norm_cursor);
+	    XDefineCursor(display, iconwin, norm_cursor);
+	    break;
+	case HAND_CURSOR:
+	    XDefineCursor(display, win, hand_cursor);
+	    XDefineCursor(display, iconwin, hand_cursor);
+	    break;
+	case BAR_CURSOR:
+	    XDefineCursor(display, win, bar_cursor);
+	    XDefineCursor(display, iconwin, bar_cursor);
+	    break;
+    }
+    oldtype = type;
+}
+
+/* private */
+static void draw_stereo_led(void)
+{
+    if (mixer_is_stereo())	/* stereo capable */
+	copy_xpm_area(78, 0, 9, 7, 28, 14);	/* light up LCD */
+    else			/* mono channel */
+	copy_xpm_area(78, 7, 9, 7, 28, 14);	/* turn off LCD */
+}
+
+static void draw_rec_led(void)
+{
+    if (mixer_is_rec())		/* record enabled */
+	copy_xpm_area(65, 0, 13, 7, 4, 14);	/* Light up LCD */
+    else			/* record disabled */
+	copy_xpm_area(65, 7, 13, 7, 4, 14);	/* turn off LCD */
+}
+
+static void draw_mute_led(void)
+{
+    if (mixer_is_muted())	/* mute */
+	copy_xpm_area(65, 14, 20, 7, 39, 14);	/* light up LCD */
+    else			/* unmute */
+	copy_xpm_area(65, 21, 20, 7, 39, 14);	/* turn off LCD */
+}
+
+static void draw_percent(void)
+{
+    int volume = (int)(mixer_get_volume() * 100);
+
+    copy_xpm_area(0, 87, 18, 9, 41, 22);	/* clear percentage */
+    
+    if (volume < 100) {
+	if (volume > 10)
+	    copy_xpm_area((volume / 10) * 6, 67, 6, 9, 47, 22);
+	copy_xpm_area((volume % 10) * 6, 67, 6, 9, 53, 22);
+    } else {
+	copy_xpm_area(6, 67, 6, 9, 41, 22);
+	copy_xpm_area(0, 67, 6, 9, 47, 22);
+	copy_xpm_area(0, 67, 6, 9, 53, 22);
+    }
+}
+
+static void draw_knob(float volume)
+{
+    float bearing, led_x, led_y;
+    int led_topleft_x, led_topleft_y;
+    Pixmap led_pixmap, led_mask;
+
+    bearing = (1.25 * PI) - (1.5 * PI) * volume;
+
+    led_x = KNOB_CENTER_X + LED_POS_RADIUS * cos(bearing);
+    led_y = KNOB_CENTER_Y - LED_POS_RADIUS * sin(bearing);
+    
+    led_topleft_x = (int)(led_x - (LED_WIDTH / 2.0) + 0.5);
+    led_topleft_y = (int)(led_y - (LED_HEIGHT / 2.0) + 0.5);
+
+    /* clear previous knob picture */
+    copy_xpm_area(87, 0, 26, 26, 36, 35);
+
+    if (mixer_is_muted()) {
+	led_pixmap = led_off_pixmap;
+	led_mask = led_off_mask;
+    } else {
+	led_pixmap = led_on_pixmap;
+	led_mask = led_on_mask;
+    }
+    XCopyArea(display, led_pixmap, dockapp.pixmap, dockapp.gc,
+	    0, 0, LED_WIDTH, LED_HEIGHT, led_topleft_x, led_topleft_y);
+    draw_percent();
+}
+
+static void draw_slider(float offset)
+{
+    int x = (offset * 50) / 5;
+
+    copy_xpm_area(65, 45, 27, 20, 4, 40);	/* repair region. move */
+    copy_xpm_area(65, 29, 7, 15, 14 + x, 43);	/* slider */
+}
+
+static Cursor create_null_cursor(Display *x_display)
+{
+    Pixmap cursor_mask;
+    XGCValues gcval;
+    GC gc;
+    XColor dummy_color;
+    Cursor cursor;
+    
+    cursor_mask = XCreatePixmap(x_display, DefaultRootWindow(x_display), 1, 1, 1);
+    gcval.function = GXclear;
+    gc = XCreateGC(x_display, cursor_mask, GCFunction, &gcval);
+    XFillRectangle(x_display, cursor_mask, gc, 0, 0, 1, 1);
+    dummy_color.pixel = 0;
+    dummy_color.red = 0;
+    dummy_color.flags = 04;
+    cursor = XCreatePixmapCursor(x_display, cursor_mask, cursor_mask,
+		&dummy_color, &dummy_color, 0, 0);
+    XFreePixmap(x_display, cursor_mask);
+    XFreeGC(x_display, gc);
+    
+    return cursor;
+}
+
+unsigned long get_color(Display *display, char *color_name)
+{
+    XColor color;
+    XWindowAttributes winattr;
+
+    XGetWindowAttributes(display,
+	    RootWindow(display, DefaultScreen(display)), &winattr);
+
+    color.pixel = 0;
+    XParseColor(display, winattr.colormap, color_name, &color);
+
+    color.flags = DoRed | DoGreen | DoBlue;
+    XAllocColor(display, winattr.colormap, &color);
+
+    return color.pixel;
+}
diff --git a/wmix.1x.gz b/wmix.1x.gz
new file mode 100644
index 0000000..df0feb4
Binary files /dev/null and b/wmix.1x.gz differ
diff --git a/wmix.c b/wmix.c
new file mode 100644
index 0000000..eac439b
--- /dev/null
+++ b/wmix.c
@@ -0,0 +1,399 @@
+/* WMix 3.0 -- a mixer using the OSS mixer API.
+ * Copyright (C) 2000, 2001 timecop at japan.co.jp
+ * Mixer code in version 3.0 based on mixer api library by
+ * Daniel Richard G. <skunk at mit.edu>, which in turn was based on
+ * the mixer code in WMix 2.x releases.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include "include/common.h"
+#include "include/mixer.h"
+#include "include/misc.h"
+#include "include/ui_x.h"
+
+#define VERSION "3.0"
+
+static Display *display;
+static char *display_name = NULL;
+static char *mixer_device = NULL;
+static bool button_pressed = false;
+static bool slider_pressed = false;
+static double prev_button_press_time = 0.0;
+
+static float display_height;
+static float display_width;
+Config config;
+static int mouse_drag_home_x;
+static int mouse_drag_home_y;
+static int idle_loop;
+
+/* local stuff */
+static void parse_cli_options(int argc, char **argv);
+static void signal_catch(int sig);
+static void button_press_event(XButtonEvent *event);
+static void button_release_event(XButtonEvent *event);
+static void motion_event(XMotionEvent *event);
+
+#define HELP_TEXT \
+	"WMixer " VERSION " by timecop at japan.co.jp + skunk at mit.edu\n" \
+	"usage:\n" \
+	"  -d <dsp>  connect to remote X display\n" \
+	"  -f <file> parse this config [~/.wmixrc]\n" \
+	"  -m <dev>  mixer device [/dev/mixer]\n" \
+	"  -h        print this help\n" \
+
+static void parse_cli_options(int argc, char **argv)
+{
+    int opt;
+
+    while ((opt = getopt(argc, argv, "d:f:hm:")) != EOF) {
+	switch (opt) {
+	    case 'd':
+		if (optarg != NULL)
+		    display_name = strdup(optarg);
+		break;
+	    case 'm':
+		if (optarg != NULL)
+		    mixer_device = strdup(optarg);
+		break;
+	    case 'f':
+		if (optarg != NULL)
+		    if (config.file != NULL)
+			free(config.file);
+		    config.file = strdup(optarg);
+		break;
+	    case 'h':
+		fputs(HELP_TEXT, stdout);
+		exit(0);
+		break;
+	    default:
+		break;
+	}
+    }
+}
+
+int main(int argc, char **argv)
+{
+    XEvent event;
+    char *home;
+    char *pid;
+    FILE *fp;
+
+    memset(&config, 0, sizeof(config));
+
+    /* we can theoretically live without a config file */
+    home = getenv("HOME");
+    if (home) {
+	config.file = calloc(1, strlen(home) + 9);
+	sprintf(config.file, "%s/.wmixrc", home);
+    }
+
+    /* handle writing PID file, silently ignore if we can't do it */
+    pid = calloc(1, strlen(home) + 10);
+    sprintf(pid, "%s/.wmix.pid", home);
+    fp = fopen(pid, "w");
+    if (fp) {
+	fprintf(fp, "%d\n", getpid());
+	fclose(fp);
+    }
+    free(pid);
+
+    /* default values */
+    config.mousewheel = 1;
+    config.scrolltext = 1;
+    config.wheel_button_up = 4;
+    config.wheel_button_down = 5;
+    config.scrollstep = 0.03;
+    config.osd = 1;
+    config.osd_color = strdup("green");
+
+    parse_cli_options(argc, argv);
+    config_read();
+
+    if (mixer_device == NULL)
+	mixer_device = "/dev/mixer";
+
+    mixer_init(mixer_device, false);
+    mixer_set_channel(0);
+
+    if ((display = XOpenDisplay(display_name)) == NULL) {
+	fprintf(stderr, "Unable to open display \"%s\"\n", display_name);
+	return EXIT_FAILURE;
+    }
+    display_width = (float)DisplayWidth(display, DefaultScreen(display)) / 4.0;
+    display_height = (float)DisplayHeight(display, DefaultScreen(display)) / 2.0;
+
+    dockapp_init(display);
+    new_window("wmix", 64, 64);
+    new_osd(DisplayWidth(display, DefaultScreen(display)) - 200, 60);
+    blit_string("wmix 3.0");
+    scroll_text(3, 4, 57, true);
+    ui_update();
+
+    /* add click regions */
+    add_region(1, 37, 36, 25, 25);	/* knob */
+    add_region(2, 4, 42, 27, 15);	/* balancer */
+    add_region(3, 2, 26, 7, 10);	/* previous channel */
+    add_region(4, 10, 26, 7, 10);	/* next channel */
+    add_region(5, 39, 14, 20, 7);	/* mute toggle */
+    add_region(6, 4, 14, 13, 7);	/* rec toggle */
+    add_region(10, 3, 4, 56, 7);	/* re-scroll current channel name */
+
+    /* setup up/down signal handler */
+    signal(SIGUSR1, (void *) signal_catch);
+    signal(SIGUSR2, (void *) signal_catch);
+
+    while (true) {
+	if (button_pressed || slider_pressed || (XPending(display) > 0)) {
+	    XNextEvent(display, &event);
+	    switch (event.type) {
+		case Expose:
+		    redraw_window();
+		    break;
+		case ButtonPress:
+		    button_press_event(&event.xbutton);
+		    idle_loop = 0;
+		    break;
+		case ButtonRelease:
+		    button_release_event(&event.xbutton);
+		    idle_loop = 0;
+		    break;
+		case MotionNotify:
+		    /* process cursor change, or drag events */
+		    motion_event(&event.xmotion);
+		    idle_loop = 0;
+		    break;
+		case LeaveNotify:
+		    /* go back to standard cursor */
+		    if ((!button_pressed) && (!slider_pressed))
+			set_cursor(NORMAL_CURSOR);
+		    break;
+		case DestroyNotify:
+		    XCloseDisplay(display);
+		    return EXIT_SUCCESS;
+		default:
+		    break;
+	    }
+	} else {
+	    usleep(100000);
+	    scroll_text(3, 4, 57, false);
+	    /* rescroll message after some delay */
+	    if (idle_loop++ > 256) {
+		scroll_text(3, 4, 57, true);
+		idle_loop = 0;
+	    }
+	    /* get rid of OSD after a few seconds of idle */
+	    if ((idle_loop > 15) && osd_mapped() && !button_pressed) {
+		unmap_osd();
+		idle_loop = 0;
+	    }
+	    if (mixer_is_changed())
+		ui_update();
+	}
+    }
+    return EXIT_SUCCESS;
+}
+
+static void signal_catch(int sig)
+{
+    switch (sig) {
+	case SIGUSR1:
+	    mixer_set_volume_rel(config.scrollstep);
+	    if (!osd_mapped())
+		map_osd();
+	    if (osd_mapped())
+		update_osd(mixer_get_volume(), false);
+	    ui_update();
+	    idle_loop = 0;
+	    break;
+	case SIGUSR2:
+	    mixer_set_volume_rel(-config.scrollstep);
+	    if (!osd_mapped())
+		map_osd();
+	    if (osd_mapped())
+		update_osd(mixer_get_volume(), false);
+	    ui_update();
+	    idle_loop = 0;
+	    break;
+    }
+}
+
+static void button_press_event(XButtonEvent *event)
+{
+    double button_press_time = get_current_time();
+    int x = event->x;
+    int y = event->y;
+    bool double_click = false;
+
+    /* handle wheel scrolling to adjust volume */
+    if (config.mousewheel) {
+	if (event->button == config.wheel_button_up) {
+	    mixer_set_volume_rel(config.scrollstep);
+	    if (!osd_mapped())
+		map_osd();
+	    if (osd_mapped())
+		update_osd(mixer_get_volume(), false);
+	    ui_update();
+	    return;
+	}
+	if (event->button == config.wheel_button_down) {
+	    mixer_set_volume_rel(-config.scrollstep);
+	    if (!osd_mapped())
+		map_osd();
+	    if (osd_mapped())
+		update_osd(mixer_get_volume(), false);
+	    ui_update();
+	    return;
+	}
+    }
+
+    if ((button_press_time - prev_button_press_time) <= 0.5) {
+	double_click = true;
+	prev_button_press_time = 0.0;
+    } else
+	prev_button_press_time = button_press_time;
+
+    switch (check_region(x, y)) {
+	case 1:			/* on knob */
+	    button_pressed = true;
+	    slider_pressed = false;
+	    mouse_drag_home_x = x;
+	    mouse_drag_home_y = y;
+	    if (double_click) {
+		mixer_toggle_mute();
+		ui_update();
+	    }
+	    break;
+	case 2:			/* on slider */
+	    button_pressed = false;
+	    slider_pressed = true;
+	    mouse_drag_home_x = x;
+	    mouse_drag_home_y = y;
+	    if (double_click) {
+		mixer_set_balance(0.0);
+		ui_update();
+	    }
+	    break;
+	case 3:			/* previous channel */
+	    mixer_set_channel_rel(-1);
+	    blit_string(config.scrolltext ? mixer_get_channel_name() : mixer_get_short_name());
+	    scroll_text(3, 4, 57, true);
+	    unmap_osd();
+	    map_osd();
+	    ui_update();
+	    break;
+	case 4:			/* next channel */
+	    mixer_set_channel_rel(1);
+	    blit_string(config.scrolltext ? mixer_get_channel_name() : mixer_get_short_name());
+	    scroll_text(3, 4, 57, true);
+	    unmap_osd();
+	    map_osd();
+	    ui_update();
+	    break;
+	case 5:			/* toggle mute */
+	    mixer_toggle_mute();
+	    ui_update();
+	    break;
+	case 6:			/* toggle rec */
+	    mixer_toggle_rec();
+	    ui_update();
+	    break;
+	case 10:
+	    scroll_text(3, 4, 57, true);
+	    break;
+	default:
+	    printf("unknown region pressed\n");
+	    break;
+    }
+}
+
+static void button_release_event(XButtonEvent *event)
+{
+    int x = event->x;
+    int y = event->y;
+    int region;
+
+    region = check_region(x, y);
+
+    if (region == 1)
+	set_cursor(HAND_CURSOR);
+    
+    button_pressed = false;
+    slider_pressed = false;
+}
+
+static void motion_event(XMotionEvent *event)
+{
+    int x = event->x;
+    int y = event->y;
+    int region;
+
+    if ((x == mouse_drag_home_x) && (y == mouse_drag_home_y))
+	return;
+
+    region = check_region(x, y);
+
+    if (button_pressed) {
+	if (y != mouse_drag_home_y) {
+	    float delta;
+
+	    set_cursor(NULL_CURSOR);
+
+	    delta = (float)(mouse_drag_home_y - y) / display_height;
+	    knob_turn(delta);
+	    if (!osd_mapped())
+		map_osd();
+	    if (osd_mapped())
+		update_osd(mixer_get_volume(), false);
+	}
+	XWarpPointer(display, None, event->window, x, y, 0, 0,
+			mouse_drag_home_x, mouse_drag_home_y);
+	return;
+    }
+
+    if (slider_pressed) {
+	if (x != mouse_drag_home_x) {
+	    float delta;
+
+	    set_cursor(NULL_CURSOR);
+
+	    delta = (float)(x - mouse_drag_home_x) / display_width;
+	    slider_move(delta);
+	}
+	XWarpPointer(display, None, event->window, x, y, 0, 0,
+			mouse_drag_home_x, mouse_drag_home_y);
+	return;
+    }
+    
+    if (region == 1)
+	set_cursor(HAND_CURSOR);
+    else if (region == 2)
+	set_cursor(BAR_CURSOR);
+    else
+	set_cursor(NORMAL_CURSOR);
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-wmaker/wmix.git



More information about the Pkg-wmaker-commits mailing list