[Pkg-wmaker-commits] [wmix] 01/44: Strip off version numbers from dir name
Doug Torrance
dtorrance-guest at moszumanska.debian.org
Fri Sep 29 10:40:11 UTC 2017
This is an automated email from the git hooks/post-receive script.
dtorrance-guest pushed a commit to branch upstream
in repository wmix.
commit c98f967e075fac34e64ec8ddd2b0f7dd2f835563
Author: Carlos R. Mafra <crmafra at gmail.com>
Date: Sun Feb 12 22:50:31 2012 +0000
Strip off version numbers from dir name
---
AUTHORS | 21 ++
BUGS | 4 +
COPYING | 3 +
INSTALL | 11 +
Makefile | 18 ++
NEWS | 86 ++++++++
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 | 81 +++++++
include/ui_x.h | 40 ++++
misc.c | 163 ++++++++++++++
mixer-oss.c | 424 ++++++++++++++++++++++++++++++++++++
sample.wmixrc | 16 ++
ui_x.c | 605 ++++++++++++++++++++++++++++++++++++++++++++++++++++
wmix.1x.gz | Bin 0 -> 584 bytes
wmix.c | 419 ++++++++++++++++++++++++++++++++++++
20 files changed, 2263 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..bea62a5
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,86 @@
+3.1 NEWS:
+Fixed a 3 years old bug about volume level showing up as "0" when it's
+actually set to 10. Thank to all 20 or so people who e-mailed me about this.
+Changed struct mixer_info name to allow compiling wmix under 2.6.1+ kernels.
+Thanks Neil Burch <burch at cs.ualberta.ca> for the patch.
+Added a patch to enable exclusion of channels from display - allows you to
+remove mixer channels you never use. adds '-e <channame>' command line option
+which can be repeated any number of times. Thanks to Nicolas Descomps <nico_206 at noos.fr>.
+Added #include <string.h> since glibc finally fixed most of the includes since 2001.
+
+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..d5746ea
--- /dev/null
+++ b/include/mixer.h
@@ -0,0 +1,81 @@
+/* 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,
+ const char *exclude[]);
+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);
+bool is_exclude (const char *short_name,
+ const char *exclude[]);
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..6bdbe1e
--- /dev/null
+++ b/mixer-oss.c
@@ -0,0 +1,424 @@
+/* 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 <string.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 m_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, &m_info);
+
+ if (m_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 = m_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, const char * exclude[])
+{
+ int devmask, srcmask, recmask, stmask;
+ struct mixer_info m_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, &m_info) == -1) {
+ fputs("error: could not read mixer info\n", stderr);
+ exit(EXIT_FAILURE);
+ }
+
+ if (verbose) {
+ printf("%s (%s)\n", m_info.name, m_info.id);
+ puts("Supported channels:");
+ }
+ for (count = 0; count < SOUND_MIXER_NRDEVICES; count++) {
+ mask = 1 << count;
+ if ((mask & devmask) && (!is_exclude((short_names[count]),exclude))) {
+ 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 \t(%s)\n", n_channels,
+ channel_names[count],
+ short_names[count]);
+ } else if ((mask & devmask) && verbose)
+ printf(" x: %s \t(%s) - disabled\n", channel_names[count],
+ short_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;
+}
+
+bool is_exclude(const char *short_name, const char *exclude[])
+{
+ int count = 0;
+ while (count < SOUND_MIXER_NRDEVICES && exclude[count] != NULL){
+ if ( strcmp(short_name, exclude[count]) == 0 )
+ return true;
+ count++;
+ }
+ return false;
+}
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..cf85e1d
--- /dev/null
+++ b/ui_x.c
@@ -0,0 +1,605 @@
+/* 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 <string.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..1fbf9db
--- /dev/null
+++ b/wmix.c
@@ -0,0 +1,419 @@
+/* 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 <sys/soundcard.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;
+static bool verbose;
+static char *exclude[SOUND_MIXER_NRDEVICES];
+
+/* 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" \
+ " -v verbose -> id, long name, name\n" \
+ " -e <name> exclude channel, can be used many times\n" \
+
+static void parse_cli_options(int argc, char **argv)
+{
+ int opt;
+ int count_exclude = 0 ;
+ verbose = false ;
+
+ while ((opt = getopt(argc, argv, "d:f:hm:ve:")) != 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;
+ case 'v':
+ verbose = true;
+ break;
+ case 'e':
+ if (count_exclude < SOUND_MIXER_NRDEVICES) {
+ exclude[count_exclude] = strdup(optarg);
+ /* printf("exclude : %s\n", exclude[count_exclude]); */
+ count_exclude++;
+ } else
+ fprintf(stderr, "Warning: You can't exclude this many channels\n");
+ break;
+ default:
+ break;
+ }
+ }
+ exclude[count_exclude] = NULL ;
+}
+
+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, verbose, (const char **)exclude);
+ 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