This howto is aimed at anyone from low-intermediate level upwards. You will need to be familiar with the terminal, root vs normal user commands and the basic Debian file-system structure (but the less you know now, the more you learn as you go, right?

This howto also assumes that you are familiar with using a window manager, such as openbox, as opposed to a Desktop Environment like Gnome, KDE or XFCE.
Substitute 'apt-get' for 'aptitude' if you prefer.
Any commands with a # at the start means “as root”.
Overview:
Part 1: Preparation
Part 2: Configuration
Part 3: Installation
* Note: This howto will discuss two ways of installing dwm: the generic, quick-and-dirty “make install” way, and the ‘proper’ APT/Debian way. Both ways have their ups and downs; as always, it’s your system, just be aware of your choices.
***************************************************************
Part 1: Preparation
Before we start, let’s get the dependencies: (they are probably already on your system if you’re using X anyway)
- Code: Select all
# aptitude install libc6 libx11-6 libxinerama1
We’ll need the build-dependencies, and also make and gcc:
- Code: Select all
# aptitude build-dep dwm
# aptitude install make gcc
Also, we’ll want an application launcher like dmenu, although you could use gmrun or similar. dmenu is part of this package:
- Code: Select all
# aptitude install suckless-tools
The default font for the status bar is Terminus, so if you want that you'll need this package:
- Code: Select all
# aptitude install xfonts-terminus
dwm can be installed as a normal package, but that would kind of defeat the purpose as you wouldn’t be able to customise it

So, make a directory to put the dwm source into, for example:
- Code: Select all
mkdir ~/Build
Then move into that directory, download the source, then move into the new dwm directory:
- Code: Select all
cd ~/Build
apt-get source dwm
cd dwm_6.0 # (or whatever it is)
Have a look at what you have now; check out the README in particular.
But what we really want to do is customise this baby, so...
*****************************************************************
Part 2: Configuration
Copy the default config file to config.h then open it.
Use whatever editor you prefer to modify config.h, but I would recommend using one that allows syntax highlighting to help you understand what you’re doing

- Code: Select all
cp config.def.h config.h
vim config.h
It’s pretty well commented, but we'll take a look through it bit by bit:
- Code: Select all
/* See LICENSE file for copyright and license details. */
/* appearance */
static const char font[] = "-*-terminus-medium-r-*-*-16-*-*-*-*-*-*-*";
static const char normbordercolor[] = "#cccccc";
static const char normbgcolor[] = "#cccccc";
static const char normfgcolor[] = "#000000";
static const char selbordercolor[] = "#0066ff";
static const char selbgcolor[] = "#0066ff";
static const char selfgcolor[] = "#ffffff";
static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int snap = 32; /* snap pixel */
static const Bool showbar = True; /* False means no bar */
static const Bool topbar = True; /* False means bottom bar */
You can define the font the way you would for an xterm. The bottom two lines define whether it will be hidden and where it will be. Easy huh?
The selected window has a one-pixel border by default, of color #0066ff (red, I think)
The background and foreground colors refer to the status bar.
- Code: Select all
/* tagging */
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
static const Rule rules[] = {
/* class instance title tags mask isfloating monitor */
{ "Gimp", NULL, NULL, 0, True, -1 },
{ "Firefox", NULL, NULL, 1 << 8, False, -1 },
};
dwm has multiple desktops, but they’re called tags and work slightly differently. By default they are numbered 1-9, but you can rename them however you want, just put the names in quotes.
The rules define how certain windows get treated, e.g. Gimp is always floating (not tiled or monocle/fullscreen) as it doesn't work properly otherwise.
The “tags mask” defines which tag the window appears on. Basically read the “<<“ as “+”, so “1 << 8” means Firefox will be on tag 9.
0 means any tag.
- Code: Select all
/* layout(s) */
static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
static const Bool resizehints = True; /* True means respect size hints in tiled resizals */
static const Layout layouts[] = {
/* symbol arrange function */
{ "[]=", tile }, /* first entry is default */
{ "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
};
dwm has three layouts: tiling, floating (normal 'Windows' mode) and monocle (fullscreen). Here you can set the default layout, and the funky little symbol that will appear in the status bar.
Above that you can select the amount of the screen that the master window takes up.
- Code: Select all
/* key definitions */
#define MODKEY Mod1Mask
#define TAGKEYS(KEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
{ MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} },
By default the modifier key is Alt (“Mod1Mask”). You could change it to Super/Windows by using "Mod4Mask" if you preferred.
The TAGKEYS set what happens if you press the number for a tag along with a modifier, so Alt-3 will take you to tag 3, while Alt-Shift-3 will tag (attach) a window to tag 3.
- Code: Select all
/* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
I think this part speaks for itself

Now we get to the guts of it, the commands:
- Code: Select all
/* commands */
static const char *dmenucmd[] = { "dmenu_run", "-fn", font, "-nb", normbgcolor, "-nf", normfgcolor, "-sb", selbgcolor, "-sf", selfgcolor, NULL };
static const char *termcmd[] = { "uxterm", NULL };
static Key keys[] = {
/* modifier key function argument */
{ MODKEY, XK_p, spawn, {.v = dmenucmd } },
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
{ MODKEY, XK_b, togglebar, {0} },
{ MODKEY, XK_j, focusstack, {.i = +1 } },
{ MODKEY, XK_k, focusstack, {.i = -1 } },
{ MODKEY, XK_h, setmfact, {.f = -0.05} },
{ MODKEY, XK_l, setmfact, {.f = +0.05} },
{ MODKEY, XK_Return, zoom, {0} },
{ MODKEY, XK_Tab, view, {0} },
{ MODKEY|ShiftMask, XK_c, killclient, {0} },
{ MODKEY, XK_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, XK_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, XK_m, setlayout, {.v = &layouts[2]} },
{ MODKEY, XK_space, setlayout, {0} },
{ MODKEY|ShiftMask, XK_space, togglefloating, {0} },
{ MODKEY, XK_0, view, {.ui = ~0 } },
{ MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } },
{ MODKEY, XK_comma, focusmon, {.i = -1 } },
{ MODKEY, XK_period, focusmon, {.i = +1 } },
{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
TAGKEYS( XK_1, 0)
TAGKEYS( XK_2, 1)
TAGKEYS( XK_3, 2)
TAGKEYS( XK_4, 3)
TAGKEYS( XK_5, 4)
TAGKEYS( XK_6, 5)
TAGKEYS( XK_7, 6)
TAGKEYS( XK_8, 7)
TAGKEYS( XK_9, 8)
{ MODKEY|ShiftMask, XK_q, quit, {0} },
};
The first two commands in the long list spawn commands; note how those commands are defined above that, with each separate part of the command in quotes and with NULL at the end. So any commands you wanted to add would need to be defined under ‘commands’, then called in the ‘keys’ section.
XK_p means the button ’p’, etc.
If instead of ‘MODKEY’ you put ‘0’, that would mean ‘no modifier’.
‘focusstack’ means next or previous window, depending on the +1 or -1
‘setmfact’ means set the size of the master window in tiling mode
‘zoom’ is move a window to the master window in tiling mode
‘setlayout’ chooses a layout based on the order defined earlier, starting numbering from 0
The TAGKEYS select the tags - note that the tags as far as dwm is concerned number from 0-8.
Lastly, Alt-Shift-q quits dwm and takes you back to a tty.
- Code: Select all
/* button definitions */
/* click can be ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
static Button buttons[] = {
/* click event mask button function argument */
{ ClkLtSymbol, 0, Button1, setlayout, {0} },
{ ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
{ ClkWinTitle, 0, Button2, zoom, {0} },
{ ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
{ ClkClientWin, MODKEY, Button1, movemouse, {0} },
{ ClkClientWin, MODKEY, Button2, togglefloating, {0} },
{ ClkClientWin, MODKEY, Button3, resizemouse, {0} },
{ ClkTagBar, 0, Button1, view, {0} },
{ ClkTagBar, 0, Button3, toggleview, {0} },
{ ClkTagBar, MODKEY, Button1, tag, {0} },
{ ClkTagBar, MODKEY, Button3, toggletag, {0} },
};
Here you define what happens when you click in various places, with and without the mod keys.
‘ClkLtSymbol’ means ‘click layout symbol’.
**********************************************************
As you can see, there are a lot of options. The default config basically chucks them all in so you can use what you need and ditch what you don’t.
I have found that I don’t need most of the options, so here is my config.h for reference after I took to it with a knife and added a couple of tricks:
- Code: Select all
/* See LICENSE file for copyright and license details. */
/* appearance */
static const char font[] = "7x14";
static const char normbordercolor[] = "#000000";
static const char normbgcolor[] = "#cccccc";
static const char normfgcolor[] = "#000000";
static const char selbordercolor[] = "#cccccc";
static const char selbgcolor[] = "#0066ff";
static const char selfgcolor[] = "#ffffff";
static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int snap = 32; /* snap pixel */
static const Bool showbar = True; /* False means no bar */
static const Bool topbar = True; /* False means bottom bar */
Different font; selected window border is white.
- Code: Select all
/* tagging */
static const char *tags[] = { "Here", "There" };
static const Rule rules[] = {
/* class instance title tags mask isfloating monitor */
{ NULL, NULL, NULL, 0, False, -1 },
};
Two tags, “Here” and "There"; I don’t tend to run many windows at once. No rules as I haven’t found I needed any (but you still need to keep this line in here with ‘NULL’)
- Code: Select all
/* layout(s) */
static const float mfact = 0.5; /* factor of master area size [0.05..0.95] */
static const Bool resizehints = False; /* True means respect size hints in tiled resizals */
static const Layout layouts[] = {
/* symbol arrange function */
{ "[ ]", monocle }, /* first entry is default */
{ "[]=", tile },
};
Monocle (fullscreen) mode by default, and if I switch to tiling it’s usually with two windows so I split the screen 50:50
‘resizehints = false’ so terminal windows don’t leave odd gaps around the edges.
- Code: Select all
/* key definitions */
#define MODKEY Mod1Mask
#define TAGKEYS(KEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
{ MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} },
#include <X11/XF86keysym.h>
/* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
Note the line added in here (‘#include <X11/XF86keysym.h>’) - this means you can refer to your XF86 keys in the next section.
- Code: Select all
/* commands */
static const char *dmenucmd[] = { "dmenu_run", "-fn", font, "-nb", normbgcolor, "-nf", normfgcolor, "-sb", selbgcolor, "-sf", selfgcolor, NULL };
static const char *termcmd[] = { "xterm", "-rv", "-fn", font, NULL };
static const char *chromiumcmd[] = { "chromium-browser", "--proxy-server=172.31.232.250:3128", NULL };
static const char *play[] = { "mocp", "-G", NULL };
static const char *stop[] = { "mocp", "-x", NULL };
static const char *prev[] = { "mocp", "-r", NULL };
static const char *next[] = { "mocp", "-f", NULL };
static const char *mute[] = { "amixer", "-q", "set", "Master", "toggle", NULL };
static const char *volumedown[] = { "amixer", "-q", "set", "Master", "2%-", "unmute", NULL };
static const char *volumeup[] = { "amixer", "-q", "set", "Master", "2%+", "unmute", NULL };
static const char *eject[] = { "eject", NULL };
Most apps I run from dmenu, but if you need to use something regularly with command line options, like chromium with a proxy server, you can define it here to call with a keybind.
Also you need to define what command each of your XF86 keys will execute.
- Code: Select all
static Key keys[] = {
/* modifier key function argument */
{ 0, XF86XK_Launch1, spawn, {.v = dmenucmd } },
{ 0, XK_F6, spawn, {.v = termcmd } },
{ 0, XK_F7, spawn, {.v = chromiumcmd } },
{ 0, XF86XK_AudioPlay, spawn, {.v = play}},
{ 0, XF86XK_AudioStop, spawn, {.v = stop}},
{ 0, XF86XK_AudioPrev, spawn, {.v = prev}},
{ 0, XF86XK_AudioNext, spawn, {.v = next}},
{ 0, XF86XK_AudioMute, spawn, {.v = mute}},
{ 0, XF86XK_AudioLowerVolume, spawn, {.v = volumedown}},
{ 0, XF86XK_AudioRaiseVolume, spawn, {.v = volumeup}},
{ 0, XF86XK_Eject, spawn, {.v = eject}},
{ MODKEY, XK_Tab, focusstack, {.i = +1 } },
{ MODKEY, XK_space, setlayout, {0} },
{ MODKEY, XK_z, zoom, {0} },
{ MODKEY, XK_q, killclient, {0} },
{ MODKEY|ControlMask, XK_q, quit, {0} },
TAGKEYS( XK_comma, 0)
TAGKEYS( XK_period, 1)
};
I’ve tried to stick to keybinds that don’t interfere with other apps, e.g. Alt-p is used to select ‘print’ on a print dialog page, so I've avoided that.
The XF86 key "Launch1" for dmenu is actually the Super/Windows key, which I remap in ~/.xinitrc (see near the end of this howto)
Alt-space simply toggles between monocle and tiling modes, as they are the only modes I specified earlier.
Alt-comma and alt-period are for "Here" and "There" tags.
- Code: Select all
static Button buttons[] = {
/* click event mask button function argument */
{ ClkLtSymbol, 0, Button1, setlayout, {0} },
{ ClkWinTitle, 0, Button1, spawn, {.v = dmenucmd } },
{ ClkWinTitle, 0, Button3, spawn, {.v = termcmd } },
{ ClkTagBar, 0, Button1, view, {0} },
{ ClkTagBar, ControlMask, Button1, toggleview, {0} },
{ ClkTagBar, 0, Button3, tag, {0} },
{ ClkTagBar, ControlMask, Button3, toggletag, {0} },
};
Click on the layout symbol to toggle tiling mode; click the status bar text and dmenu starts; right-click to get a terminal.
Click on a tag name to view it; right-click on a tag name to send a window to that tag.
So that’s mine; go to town, or just try the default first, whatever. When you're done there, it’s time for…
*******************************************************************
Part 3: Installation
a) The generic quick-and-dirty method
This is the method from the README file. It will put dwm into /usr/local/bin which is good for two reasons: that directory is empty by default in Debian, and it is already in your $PATH
The disadvantage of this method is that APT will not be aware of its existence, so you have been warned.
The advantage is that it is, well, quick:
- Code: Select all
# make clean install
If you plan to tinker with your config.h a bit, each time you just need to run this command from the ~/Build/dwm_6.0 directory, then exit dwm (Alt-Ctrl-q in my case) and ‘startx’ again.
b) The ‘proper’ APT/Debian way
Once you have it set up how you want, you can make a .deb and install it properly.
This is straightforward too; just run this to make the .deb (from in the ~/Build/dwm_6.0 directory):
- Code: Select all
dpkg-buildpackage -us -uc
This will spit out lots of output; the options given mean it shouldn't prompt you about unsigned files.
When it finishes you should find your .deb in the parent directory, so cd up one level and install like so (as root):
- Code: Select all
cd ..
# dpkg -i dwm*.deb
Done! Now it’s official.
Don’t forget to uninstall the file at /usr/local/bin/dwm (if you used the quick-and-dirty method) like this:
- Code: Select all
cd ~/Build/dwm_6.0
# make uninstall
You might also want to run
- Code: Select all
# aptitude hold dwm
so your version doesn't get overwritten by an update if you're running testing or sid.
**************************
You need to make (or alter) the executable file ~/.xinitrc to start dwm
The last line in it should be
- Code: Select all
exec [...] dwm
Here’s mine as an example; :
- Code: Select all
#!/bin/sh
. ~/.fehbg # set wallpaper
unclutter -root & # hide cursor when unused
# Turn Super/Windows key into XF86Launch1
xmodmap -e "keycode 133 = XF86Launch1"
# Turn CapsLock into Control
xmodmap -e "clear lock" -e "keycode 66 = Control_R" -e "add Control = Control_R"
# Show memory use, volume %, battery % and time in status bar
while xsetroot -name "$(free -m | awk '/cache:/ { print $3"MB" }') Vol:$(amixer get Master | tail -1 | awk '{ print $5 }' | tr -d '[]') Batt:$(acpi | awk '{ print $4 }' | tr -d ',') $(date +%R)"
do
sleep 1
done &
# Launch system-wide stuff first, then dwm...
exec ck-launch-session dbus-launch dwm
Don’t forget to make it executable:
- Code: Select all
chmod 755 ~/.xinitrc
Then just run
- Code: Select all
startx
to get the whole show started.
**************************
Some sources:
http://dwm.suckless.org/tutorial
http://dwm.suckless.org/customisation/
http://www.xsnake.net/howto/dwm/dwm-eng.php
http://wiki.archlinux.org/index.php/Dwm
http://lubutu.com/rant/dwm-faq
You can also extend dwm with patches to add features like new layouts, such as b.stack (horizontal stacking, good for non-widescreens); see http://dwm.suckless.org/patches
Have fun!
Any errors in this howto are mine, and if you point them out I can fix them

(Edited many times for clarity and freshness)