Advice about the scripts included in this site: try to understand what the script does and to write your own version.  If you just cut and paste, you'll surely have to visit my site again ;-).  Remember, it's not free software but to learn what makes you free.

Basic Graphical Environment Configuration Tips

Avoiding Dependencies

Unfortunately, in 1987 I had to do the compulsory military service (In Argentina, where I'm from, it was abolished in 1994).  A pair of days before Christmas, four of us were on duty.  We had to stay four hours sitting in a room as reinforcement in case of emergency(!?).

In the calmly of the night the young officer in charge asked us, one by one, the typical 'What will you do if you win the Christmas lottery?'

At that time (I was nineteen) my companions and me thought we were just losing fourteen months of our lives.  Today, with half life experience on my back, I realize it was just one among the lot of arbitrary stupid things society makes us to do.  Now I don't see a big difference between barracks, school, neighborhood or family.  I've suffered worse abuses from teachers, neighbors, friends, relatives than from sergeants or captains.  Violence is spontaneous, natural, explicit.  Despotism is, the most of the times, subliminal, innocent.

And people support these abuses because they don't dare to find out to what extent they really depend on all that stuff.

With the same confidence the young version of me added one more "life of luxury" to the others' descriptions I know exactly what I'd answer to the officer if I were able to go back to that instant of my life: "I'd buy a little house in a quiet place, far from all towns, and refrain myself from squandering".

A Window Manager Is Enough

Between others you can use FVWM, Openbox or JWM.

Manual vs Automatic

The whole freedesktop.org initiative points in one direction that's to give people the desktop experience they're used to, as they were educated by the two big known multinationals.  Their initiative is perfectly understandable (especially from the marketing point of view) and it'll be acceptable too while it wouldn't assume absolutely everyone wants to adopt that way, forcing those who know that commodities are not only unneeded but in some cases counterproductive to waste a lot of time in trying to workaround them.

Years of use taught me to consider the contents of ~/.config no less temporal than the ~/.cache or /tmp.  Most of the WYSIWYG applications that store their crap there will surely crash in the next upgrade because of incompatibilities with its own default "auto" loaded settings.  I find wiser and more practical to keep my precious manually edited configuration files out of that directory.  Not so long ago I used to remove that whole directory any time some GTK or Qt application started to behave badly as you surely know is advisable to do with ~/.mozilla when firefox starts to get crazy.  Well, the last time I did that if I hadn't backup my precious ~/.fonts.conf I'd magically lose it.  Why?  It seems freedesktop.org people decided to "deprecate" it and put it into a ~/.config subdirectory.  If you dare to overlook the warnings, the file is silently moved to the new path and a soft link is created on its place.

Fonts

Suppose the "packager" of your favorite distribution thinks bitmap fonts are "ugly" (as he probably think about vaginas).  If you don't share his taste you'll find out that to get fontconfig doing what you want isn't trivial.  The first step is learning how to edit your per user configuration file.  More late you'll find out your settings won't always overwrite the system wide ones.  You can fix this by assigning 50-users.conf the highest number as well as playing with the rest of links in /etc/fonts/conf.d, but all these customizations are overwritten by a system upgrade.

To circumvent these "conveniences" my advise is once you've learned how to implement what you want in your fonts.conf file (not before!), set this variable in your environment:

export FONTCONFIG_FILE="/wherever/you/fuc..ng/want/fonts.conf"

In that way only your settings will be read.  You'll be able to migrate effectively your configuration to any unix-like system by just copying this file. And put it wherever you want as the owner of your files you supposedly are.

Now, create your fonts.conf.  Probably you won't like my settings but they'll be useful as an example:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>

<!-- Check where fonts are stored in your system and put here the paths -->
<dir>/usr/share/fonts</dir>
<dir>~/.fonts</dir>
<cachedir>~/.fontconfig</cachedir>

<!-- LCD filter (lcddefault, lcdlight, lcdlegacy or lcdnone) -->
<match target="font">
<edit mode="assign" name="lcdfilter"><const>lcddefault</const></edit>
</match>

<!-- Default values (optimal for DejaVu) -->
<match target="font">
<edit mode="assign" name="rgba"><const>none</const></edit>
<edit mode="assign" name="hinting"><bool>true</bool></edit>
<edit mode="assign" name="hintstyle"><const>hintslight</const></edit>
<edit mode="assign" name="antialias"><bool>true</bool></edit>
<edit mode="assign" name="autohint"><bool>true</bool></edit>
<edit mode="assign" name="lcdfilter"><const>lcddefault</const></edit>
</match>

<!-- Workaround for Qt -->
<match target="font">
<test name="family" qual="any"><string>Helvetica</string></test>
<edit name="antialias" mode="assign"><bool>false</bool></edit>
<edit name="autohint" mode="assign"><bool>false</bool></edit>
<edit name="hinting" mode="assign"><bool>true</bool></edit>
<edit name="hintstyle" mode="assign"><const>hintfull</const></edit>
</match>

<!-- Replace Helvetica for Arial -->
<!--
<alias binding="same">
<family>Helvetica</family><prefer><family>Arial</family></prefer>
</alias>
-->

<!-- Don't use antialias and autohint for legacy MS fonts -->
<match target="font">
<test name="family" qual="any"><string>Arial</string></test>
<edit name="antialias" mode="assign"><bool>false</bool></edit>
<edit name="autohint" mode="assign"><bool>false</bool></edit>
<edit name="hinting" mode="assign"><bool>true</bool></edit>
<edit name="hintstyle" mode="assign"><const>hintfull</const></edit>
</match>

<match target="font">
<test name="family" qual="any"><string>Verdana</string></test>
<edit name="antialias" mode="assign"><bool>false</bool></edit>
<edit name="autohint" mode="assign"><bool>false</bool></edit>
<edit name="hinting" mode="assign"><bool>true</bool></edit>
<edit name="hintstyle" mode="assign"><const>hintfull</const></edit>
</match>

<match target="font">
<test name="family" qual="any"><string>Times New Roman</string></test>
<edit name="antialias" mode="assign"><bool>false</bool></edit>
<edit name="autohint" mode="assign"><bool>false</bool></edit>
<edit name="hinting" mode="assign"><bool>true</bool></edit>
<edit name="hintstyle" mode="assign"><const>hintfull</const></edit>
</match>

<match target="font">
<test name="family" qual="any"><string>Tahoma</string></test>
<edit name="antialias" mode="assign"><bool>false</bool></edit>
<edit name="autohint" mode="assign"><bool>false</bool></edit>
<edit name="hinting" mode="assign"><bool>true</bool></edit>
<edit name="hintstyle" mode="assign"><const>hintfull</const></edit>
</match>

<match target="font">
<test name="family" qual="any"><string>Courier New</string></test>
<edit name="antialias" mode="assign"><bool>false</bool></edit>
<edit name="autohint" mode="assign"><bool>false</bool></edit>
<edit name="hinting" mode="assign"><bool>true</bool></edit>
<edit name="hintstyle" mode="assign"><const>hintfull</const></edit>
</match>

<match target="font">
<test name="family" qual="any"><string>Georgia</string></test>
<edit name="antialias" mode="assign"><bool>false</bool></edit>
<edit name="autohint" mode="assign"><bool>false</bool></edit>
<edit name="hinting" mode="assign"><bool>true</bool></edit>
<edit name="hintstyle" mode="assign"><const>hintfull</const></edit>
</match>

<match target="font">
<test name="family" qual="any"><string>Trebuchet MS</string></test>
<edit name="antialias" mode="assign"><bool>false</bool></edit>
<edit name="autohint" mode="assign"><bool>false</bool></edit>
<edit name="hinting" mode="assign"><bool>true</bool></edit>
<edit name="hintstyle" mode="assign"><const>hintfull</const></edit>
</match>

<match target="font">
<test name="family" qual="any"><string>Impact</string></test>
<edit name="antialias" mode="assign"><bool>false</bool></edit>
<edit name="autohint" mode="assign"><bool>false</bool></edit>
<edit name="hinting" mode="assign"><bool>true</bool></edit>
<edit name="hintstyle" mode="assign"><const>hintfull</const></edit>
</match>

<!-- Aliases taken from conf.d/60-latin.conf -->

<alias>
<family>serif</family>
<prefer>
<family>Bitstream Vera Serif</family>
<family>DejaVu Serif</family>
<family>Times New Roman</family>
<family>Thorndale AMT</family>
<family>Luxi Serif</family>
<family>Nimbus Roman No9 L</family>
<family>Times</family>
</prefer>
</alias>
<alias>
<family>sans-serif</family>
<prefer>
<family>Bitstream Vera Sans</family>
<family>DejaVu Sans</family>
<family>Verdana</family>
<family>Arial</family>
<family>Albany AMT</family>
<family>Luxi Sans</family>
<family>Nimbus Sans L</family>
<family>Helvetica</family>
<family>Lucida Sans Unicode</family>
<family>BPG Glaho International</family> <!-- lat,cyr,arab,geor -->
<family>Tahoma</family> <!-- lat,cyr,greek,heb,arab,thai -->
</prefer>
</alias>
<alias>
<family>monospace</family>
<prefer>
<family>Bitstream Vera Sans Mono</family>
<family>DejaVu Sans Mono</family>
<family>Inconsolata</family>
<family>Andale Mono</family>
<family>Courier New</family>
<family>Cumberland AMT</family>
<family>Luxi Mono</family>
<family>Nimbus Mono L</family>
<family>Courier</family>
</prefer>
</alias>
<!-- Fantasy faces -->
<alias>
<family>fantasy</family>
<prefer>
<family>Impact</family>
<family>Copperplate Gothic Std</family>
<family>Cooper Std</family>
<family>Bauhaus Std</family>
</prefer>
</alias>
<!-- Cursive faces -->
<alias>
<family>cursive</family>
<prefer>
<family>ITC Zapf Chancery Std</family>
<family>Zapfino</family>
<family>Comic Sans MS</family>
</prefer>
</alias>

</fontconfig>

It just rests to run fc-cache -fv as your normal user to load the configuration.

Some applications don't read fonts.conf.  Add the following to ~/.Xdefaults

Xft.antialias: 1
Xft.hinting: 1
Xft.hintstyle: hintslight
Xft.lcdfilter: lcddefault
Xft.rgba: rgb
Xft.dpi: 96

My Documents, My Pictures, My Videos

One more workaround against the "forced" automation.

Those of us who know we don't need desktop environments for idiots find annoying applications take decisions for us, like Firefox creating "Desktop" and "Downloads" directories without asking.  If you want to get rid of this feature you can edit manually ~/.config/user-dirs.dirs.  Or to "automate" the workaround:

#!/bin/sh
# Workaround freedesktop.org settings

sed -i "s#\$HOME/Desktop#$HOME#" ~/.config/user-dirs.dirs
sed -i "s#\$HOME/Documents#$HOME#" ~/.config/user-dirs.dirs
sed -i "s#\$HOME/Downloads#$HOME#" ~/.config/user-dirs.dirs
sed -i "s#\$HOME/Templates#$HOME#" ~/.config/user-dirs.dirs
sed -i "s#\$HOME/Music#$HOME#" ~/.config/user-dirs.dirs
sed -i "s#\$HOME/Public#$HOME#" ~/.config/user-dirs.dirs
sed -i "s#\$HOME/Videos#$HOME#" ~/.config/user-dirs.dirs
sed -i "s#\$HOME/Pictures#$HOME#" ~/.config/user-dirs.dirs

xdg-user-dirs-update

Unified Keybindings

It doesn't care if emacs, vi or whatever while having for goal an unified set.  Taking in care 'unified' doesn't mean 'exactly' the same behavior in all applications, in some cases 'different' (or, more suitable said, 'complementary') key bindings avoid problems between applications that have to live together.

The file ~/.gtkrc-2.0 was also moved to ~/.config.  As I explained above I prefer to avoid that directory:

export GTK2_RC_FILES="$HOME/.gtkrc-2.0"

Then add this to your ~/.gtkrc-2.0.

# Generic Unix key bindings
binding "gtk-unix-text-entry"
{
	bind "<ctrl>u" { "delete-from-cursor" (paragraph-ends, -1) }
	bind "<ctrl>h" { "delete-from-cursor" (chars, -1) }
	bind "<ctrl>w" { "delete-from-cursor" (word-ends, -1) }
	bind "<ctrl>b" { "move-cursor" (paragraph-ends, -1, 0) }
	bind "<ctrl>e" { "move-cursor" (paragraph-ends, 1, 0) }
}
class "GtkEntry" binding "gtk-unix-text-entry"

# Vi key bindings (as far as possible)
binding "gtk-vi-tree-view"
{
	bind "j" { "move-cursor" (display-lines, 1) }
	bind "k" { "move-cursor" (display-lines, -1) }
	bind "l" { "move-cursor" (logical-positions, 1) }
	bind "h" { "move-cursor" (logical-positions, -1) }
	bind "slash" { "start-interactive-search" () }
}
binding "gtk-vi-menu"
{
	bind "j" { "move-current" (next) }
	bind "k" { "move-current" (prev) }
	bind "l" { "move-current" (child) }
	bind "h" { "move-current" (parent) }
}
class "GtkTreeView" binding "gtk-vi-tree-view"
class "GtkMenuShell" binding "gtk-vi-menu"

# Emacs key bindings
#binding "gtk-emacs-text-entry"
#{
#	bind "<ctrl>b" { "move-cursor" (logical-positions, -1, 0) }
#	bind "<ctrl>f" { "move-cursor" (logical-positions, 1, 0) }
#
#	bind "<alt>b" { "move-cursor" (words, -1, 0) }
#	bind "<alt>f" { "move-cursor" (words, 1, 0) }
#
#	bind "<ctrl>a" { "move-cursor" (paragraph-ends, -1, 0) }
#	bind "<ctrl>e" { "move-cursor" (paragraph-ends, 1, 0) }
#
#	bind "<ctrl>d" { "delete-from-cursor" (chars, 1) }
#	bind "<ctrl>BackSpace"	{ "delete-from-cursor" (chars, -1) }
#	bind "<alt>d" { "delete-from-cursor" (word-ends, 1) }
#	bind "<alt>BackSpace" { "delete-from-cursor" (word-ends, -1) }
#	bind "<ctrl>k" { "delete-from-cursor" (paragraph-ends, 1) }
#	bind "<ctrl>space" { "entry.set_position" () }
#}
#binding "gtk-emacs-text-view"
#{
#	bind "<ctrl>p" { "move-cursor" (display-lines, -1, 0) }
#	bind "<ctrl>n" { "move-cursor" (display-lines, 1, 0) }
#	bind "<ctrl>space" { "set-anchor" () }
#}
#binding "gtk-emacs-tree-view"
#{
#	bind "<ctrl>s" { "start-interactive-search" () }
#	bind "<ctrl>f" { "move-cursor" (logical-positions, 1) }
#	bind "<ctrl>b" { "move-cursor" (logical-positions, -1) }
#}
#binding "gtk-emacs-menu"
#{
#	bind "<ctrl>n" { "move-current" (next) }
#	bind "<ctrl>p" { "move-current" (prev) }
#	bind "<ctrl>f" { "move-current" (child) }
#	bind "<ctrl>b" { "move-current" (parent) }
#}
#class "GtkEntry" binding "gtk-emacs-text-entry"
#class "GtkTextView" binding "gtk-emacs-text-view"
#class "GtkTreeView" binding "gtk-emacs-tree-view"
#class "GtkMenuShell" binding "gtk-emacs-menu"

Comfortably navigating a PDF file:

# ~/.xpdfrc

bind ctrl-+ any zoomIn
bind ctrl-- any zoomOut

#continuousView yes

# Vi keybindings
bind h any scrollLeft(16)
bind l any scrollRight(16)
bind k any scrollUp(16)
bind j any scrollDown(16)
bind ctrl-y any scrollUp(16)
bind ctrl-e any scrollDown(16)
bind ctrl-u any scrollUpPrevPage(100)
bind ctrl-d any scrollDownNextPage(100)
bind ctrl-b any scrollUpPrevPage(800)
bind ctrl-f any scrollDownNextPage(800)
bind g any gotoPage(1)
bind G any gotoLastPage
bind / any find
bind f any toggleFullScreenMode

# Emacs keybindings
#bind ctrl-b any scrollLeft(16)
#bind ctrl-f any scrollRight(16)
#bind ctrl-p any scrollUp(16)
#bind ctrl-n any scrollDown(16)
#bind alt-v any scrollUpPrevPage(900)
#bind ctrl-v  any scrollDownNextPage(900)
#bind alt-< any gotoPage(1)
#bind alt-> any gotoLastPage
#bind ctrl-s any find

urlCommand "firefox %s"

# End of ~/.xpdfrc

Let's Go

Using startx you can run the X server as a child of your user shell process having more control over it.

# ~/.xinitrc

# Background
xsetroot -solid rgb:5/5/5

# Start Apps
xterm -geometry +10+10 &

# Start Window Manager
exec twm

An example of TWM config:

# ~/.twmrc -- Walter A. Iglesias

# General options
ButtonIndent 0
NoGrabServer
NoRaiseOnMove
NoRaiseOnResize
BorderWidth 2
DecorateTransients
ConstrainedMoveTime 0
RandomPlacement
TitlePadding 6
OpaqueMove
WarpUnmapped
NoStackMode
NoSaveUnders
RestartPreviousState
AutoRelativeResize
DontMoveOff
ShowIconManager
IconifyByUnmapping
UsePPosition "on"

# Adjust the following to your screen resolution (you may rest the
# necessary pixels for avoiding maximized windows to cover the Icon
# Manager)
MaxWindowSize "1024x714"
IconManagerGeometry "=1024x26+0-0" 6

# Fonts
TitleFont	"-*-fixed-medium-r-normal-*-13-*-*-*-*-*-*"
ResizeFont	"-*-fixed-medium-r-normal-*-13-*-*-*-*-*-*"
MenuFont	"-*-fixed-medium-r-normal-*-13-*-*-*-*-*-*"
IconFont	"-*-fixed-medium-r-normal-*-13-*-*-*-*-*-*"
IconManagerFont	"-*-fixed-medium-r-normal-*-13-*-*-*-*-*-*"

# Colors
Color
{
	BorderColor		"gray34"
	MenuBorderColor		"gray34"
	IconBorderColor		"gray34"

	DefaultBackground	"gray34"
	TitleBackground		"gray34"
	MenuBackground		"gray34"
	IconBackground		"gray34"
	IconManagerBackground	"gray34"
	MenuTitleBackground	"white"

	BorderTileBackground	"gray34"
	BorderTileForeground	"gray34"

	DefaultForeground	"white"
	TitleForeground		"white"
	MenuForeground		"white"
	IconForeground		"white"
	IconManagerForeground	"white"
	MenuTitleForeground	"gray34"

	IconManagerHighlight	"green"
	MenuShadowColor		"white"
}

# Don't draw title bar to these apps
NoTitle { "TWM Icon Manager" "XClock" "xosview" "xbiff" "xload" }

# Don't show these apps at icon manager
IconManagerDontShow { "XClock" "xosview" "xbiff" "xload" }

DefaultFunction f.nop

# Defining functions
MoveDelta 7
Function "move-or-raiselower"	{ f.move f.deltastop f.raiselower }
Function "move-or-iconify"	{ f.move f.deltastop f.iconify }
Function "resize-or-fullzoom"	{ f.resize f.deltastop f.fullzoom }
Function "deiconify-n-raise"	{ f.deiconify f.raise }
Function "forwiconmgr-n-raise"	{ f.forwiconmgr f.lower }
Function "backiconmgr-n-raise"	{ f.backiconmgr f.lower }
Function "warpto-n-cycle" {
	f.warpto "TWM Icon Manager"
	f.function "forwiconmgr-n-raise"
}
Function "warpto-n-backcycle" {
	f.warpto "TWM Icon Manager"
	f.function "backiconmgr-n-raise"
}

# Mouse click
Button1 = : root : f.menu "main"
Button3 = : root : f.menu "windowops"
Button2 = : root : f.menu "TwmWindows"

Button1 = : t|f : f.function "move-or-raiselower"
Button3 = : title : f.menu "windowops"

Button1 = m : w : f.function "move-or-raiselower"
Button3 = m : w : f.resize

Button1 = : iconmgr : f.function "deiconify-n-raise"
Button3 = : iconmgr : f.iconify

# Redmont title bar buttons
NoDefaults
RightTitleButton ":dot" = f.iconify
RightTitleButton ":resize" = f.function "resize-or-fullzoom"
RightTitleButton ":xlogo" = f.delete

# Key bindings
"Tab"	= m	: all : f.function "warpto-n-cycle"
"Tab"	= m | s	: all : f.function "warpto-n-backcycle"
"F11"	= m 	: all : f.fullzoom
"F12"	= m 	: all : f.hzoom
"F10"	= m 	: all : f.zoom
"F8"	= m 	: all : f.iconify
"F4"	= m 	: all : f.delete

# programs
"u"	= mod4 : all : !"/usr/bin/urxvt &"
"n"	= mod4 : all : !"/usr/bin/firefox &"

# Menu
menu "main"
{
	"Menu"			f.title
	"XTerm (Win+u)"		! "xterm &"
	"Firefox (Win+n)"	! "firefox &"
	"Mutt (Win+m)"		! "xterm -T \"Mutt\" -e sh -c \"/usr/bin/mutt\" &"
	""			f.nop

	"Show Icon bar"		f.showiconmgr
	"Hide Icon bar"		f.hideiconmgr

	""			f.nop
	"Exit"			f.menu "Quit-Verify"
}

menu "Icon bar"
{
	"Show"			f.showiconmgr
	"Hide"			f.hideiconmgr
}

menu "Quit-Verify"
{
	"Restart TWM"		f.restart
	"Exit"			f.quit
}

menu "windowops"
{
	"Window Ops"		f.title
	"Maximize (Alt+F11)"	f.fullzoom
	"H. Maximize (Alt+F12)"	f.hzoom
	"V. Maximize (Alt+F10)"	f.zoom
	"Iconify (Alt+F3)"	f.iconify
	"Resize"		f.resize
	"Move"			f.move
	"Raise"			f.raise
	"Lower"			f.lower
	"Identify"		f.identify
	""			f.nop
	"Focus"			f.focus
	"Unfocus"		f.unfocus
	""			f.nop
	"Close (Alt+F4)"	f.delete
	"Kill"			f.destroy
}

# End ~/.twmrc

Run Dialog Using BASH

You know what's installed in your machine, don't you?  What's the menu for?  Copy the following script to your ~/bin directory, change the execution bit and bind it to a Alt+F2 or whatever keystroke.  It's convenient to configure your window manager to keep "run" in the top layer.

#!/bin/sh
# ~/bin/run - Run Dialog using XTerm and BASH
# Walter Alejandro Iglesias (Last modif. Tue Jul 14 21:19:59 CEST 2015)

[ ! "$DISPLAY" ] && echo "X is not running" && exit 1

# Customize
name=run	# Use this name to set the properties in your window manager
width=480	# Width in pixels
font=fixed
background=Black
foreground=White
geometry=80x1+20+20

# If one instance is running then exit
num=`ps axo ucmd | grep run | wc -l`
[ $num -gt 2 ] && exit 1

grep '! Run' ~/.Xdefaults >/dev/null || {
	echo ""						>> ~/.Xdefaults
	echo "! Run"					>> ~/.Xdefaults
	echo -n "run.VT100*translations: #override"	>> ~/.Xdefaults
	echo " <Key>Return: string(\" &\\nexit\\n\")"	>> ~/.Xdefaults
	echo "run.VT100*cursorUnderLine: on"		>> ~/.Xdefaults
	echo "run.VT100*cursorBlink: yes"		>> ~/.Xdefaults
}

if [ ! -e ~/.runrc ] ; then
	echo '#!/bin/sh'				>> ~/.runrc
	echo '# ~/.runrc'				>> ~/.runrc
	echo ''						>> ~/.runrc
	echo 'HISTFILE=~/.run_history'			>> ~/.runrc
	echo 'HISTCONTROL=ignorespace:erasedups'	>> ~/.runrc
	echo 'HISTIGNORE=exit'				>> ~/.runrc
	echo 'shopt -s histappend'			>> ~/.runrc
	echo 'bind "TAB: menu-complete"'		>> ~/.runrc
	echo 'bind "set show-all-if-ambiguous off"'	>> ~/.runrc
	echo 'PS1="> "'					>> ~/.runrc
fi

xterm	-name $name		\
	-title $name		\
	-geom $geometry		\
	-fn $font		\
	-bg $background		\
	-fg $foreground		\
	-e bash --rcfile ~/.runrc

exit 0

Control audio volume from any window manager

Copy the following script to $HOME/bin and make it executable:

#!/bin/sh
# ~/bin/volume.sh - To be called from window managers key bindings

usage="Usage: `basename $0` [up|down|mute]"
amount=10

case `uname -s` in
OpenBSD)
	up="mixerctl -q outputs.master=+$amount"
	down="mixerctl -q outputs.master=-$amount"
	mute="mixerctl -q outputs.master.mute=toggle"
;;
Linux)
	up="amixer sset Master,0 $amount+ >/dev/null 2>&1"
	down="amixer sset Master,0 $amount- >/dev/null 2>&1"
	mute="amixer sset Master,0 toggle >/dev/null 2>&1"
esac

case $1 in
	up) $up ;;
	down) $down ;;
	mute) $mute ;;
	*) echo $usage; exit 1
esac

Now you can call it from any window manager.  For example you can add to Fvwm configuration files:

Mouse 4 A 4 Exec exec volume.sh up
Mouse 5 A 4 Exec exec volume.sh down
Key k A 4 Exec exec volume.sh up
Key j A 4 Exec exec volume.sh down
Key m A 4 Exec exec volume.sh mute

And holding the Win key you can raise or lower the audio volume with j o k (vi keys) or your mouse scroll wheel.  The 'm' key mutes the sound.