A Look at the DWM Window Manager

Tiling window managers can be quite handy. Although I generally use either fluxbox or openbox some of the small tiling window managers can be useful, especially on netbooks.

For those unfamiliar with them, a tiling window manager takes each window and sorts them into their own space. On some of them, including dwm, this is done dynamically, that is, as you open a new window, it will automatically be sized to fit. Looking at some screenshots might help. (The link goes to suckless.org's screenshots--they're the ones who created dwm).

As of dwm-6.2 the default config.def.h file has changed enough so that you may have to re-edit your custom config.h. The changes are minor, but enough to break an existing config. Depending upon what you need from dwm, though, you can probably choose to ignore upgrading it without losing very much. Much of this article is written about older versions, but it should work with 6.2 with small changes that should be evident if you look at the default config.def.h file

Some folks get put off by the fact that to configure dwm, one has to manually edit the included config.h file and recompile. This really isn't terribly difficult, even if one doesn't know C, and the recompiling only takes a few seconds. Some distributions, for example, Fedora, have a nice wrapper script that will do it for you. If your distribution doesn't have such a script, you're probably better off downloading it and compiling it yourself. It has relatively few dependencies.

One reason to download it rather than install it from your distribution's package manager is that, as mentioned, to customize it, you might want to edit config.h. If your distro installs it as a binary, you might not have that config.h file readily available.

However, the default keybindings are fairly reasonable. A good cheat sheet can be found at here, at github.

As stated above, Fedora, at least, is an exception to this rule. Your distribution might also have its version of a wrapper script.

This isn't going to be a detailed tutorial, but hopefully, it will make it a little easier for you to use and customize dwm.

Once installed, add to your $HOME/.xinitrc
exec dwm 

and it should start when you run startx.

For those of you who start with graphical login, there should be a way to add it to your login menu, but, as I don't use a gui login, I don't know what it is. I would try googling add dwm to gdm or kdm or whatever login gui that you use. An excellent ArchLinux wiki article gives some tips.

Regardless, we'll assume that you've managed to log into dwm. You will see nothing but a black screen with a gray bar at the top. In the upper right, there will be a small indicator, with dwm and the version, e.g., dwm-5.9-1. The left side will have numbers 1-9, for each tag. A tag is similar to a workspace.

Many people have little scripts to give a bit more inforation in the status bar (the right side, that has dwm and version). I use
while true; do xsetroot -name "$(date +"%F %R" )";sleep 1m;done &

This changes my status bar to something like 2014-04-22 11:01 (if I'm writing it on April 22 at 11:01 AM). You can run the script as a script in itself (putting #!/bin/sh at the top), or put it in your xinitrc before starting dwm--that is, the exec dwm should be the last line in ~/.xinitrc. In FreeBSD, it doesn't seem to recognize the 1m so use 60 (for seconds) instead.

If using default keybindings (we'll cover this in a minute), hit alt+shift+return and you'll get a terminal that takes up the entire screen. Hit alt+shift+return again, and you'll get a second terminal, with the screen now divided into a left and right half. Do it a third time and the right half of the screen will now have a top and a bottom terminal. Do it a 4th time, and the right half of the screen will now have three terminals, stacked top to bottom. This is known, oddly enough, as stacking. (Note that "stacking", is also a term for window managers like flxubox and open box, that stack rather than tile windows)

Note that in Fedora's version, they have changed the mod key from Mod1, alt, to Mod4, the Windows key, between Ctrl and Alt on most US keyboards. So, if you're on Fedora and installed dwm with yum install dwm, substitute the Windows key for alt in all of these shortcuts. The left hand terminal is the master area, the right side is used for stacking. The man page gives the various default keyboard shortcuts. Alt+h will decrease the size of the master area. Alt+l will enlarge it. The keyboard shortcuts are familiar to vi users, l moving a vi cursor to the right and h to the left. (That's a lower case L, not the numeral one.) Alt+j and Alt+k move focus to the window below and above respectively.

Now hit alt+p and you'll see the handy dmenu on the top. Type in a few letters of an application, for example, opera, the web browser.

A new application, whether opened by terminal command or using dmenu, should open in the master area. If I want that application to have more screen, I can use the alt-l shortcut mentioned previously. If I want it to take the full screen, I can hit alt+m, as in monocle layout, meaning that the one window will take the entire workspace. Another use for monocle view might be if you've opened up a bunch of windows. A particular window might be too small to be useful. To fix that, I can hit alt+m and it's maximized to use the whole screen. To return to tile view, I hit alt+t.

There is also alt+f, which will change the focused window to floating. A floating window can be resized with alt+button3 (right mouse button). (I'd always thought button 3 is the middle button, but I'm going by the config.h file--dwm, at least, seems to consider it the right button, rather than the middle one). or moved with alt+button1 (left mouse button). A default config.h file will toggle floating with alt+button2 (middle button). In my experience, if you set an app to always be floating in config.h, then resize it with alt+button3, close dwm and reopen it, the window will stay that size. This can be useful if you use something that you like to keep at a certain size. For example, I like to use gbuffy to watch mail. If I open it in dwm, it will open in the master window. If I resize it, then close dwm, then reopen dwm, it remains the size that I want. To set an app to always be floating, you have to figure out its class name and then put it in config.h. If you look at the listing for gimp, which is set to always be floating, you can see the syntax, which is also listed above the entries--that is the class, instance, title and so on. So, in this case, it would read

 { "Gbuffy",     NULL,       NULL,       0,            True,        -1 },

You get the class name by using the xprop program. Use the command
xprop |grep WM_CLASS

It will give back two names, often the same, e.g., with the old mlterm, it would give back mlterm, mlterm. However, for gbuffy, it will give back
WM_CLASS(STRING) = "gbuffy," "Gbuffy"

The first listing, all lower case, is the name, the second the class. I cover this a bit more in my openbox page. I want gbuffy to always be opened in a floating window, so I add the lines I listed above (the "Gbuffy", NULL and so on) into config.h. Then, as always when making changes, I rerun make install in my dwm directory. (Unless using the Fedora wrapper script mentioned above.)

If, while in dwm, one chooses floating mode, the next window opened will float rather than be tiled. For example, if I have two terminals open, then hit alt+f, the next terminal I open will be floating--that is, rather than a second window opening in the right hand stack, it will be a typical 80x24 terminal appearing in the upper left. The status bar will indicate the mode that you're in--by default, if in floating mode, after th 1-9 listing tags, you'll see
><>:

If in typical tile mode it will show []=. If using monocular mode it will be a bracket with a number, showing how many things are open. For example, if all I have three terminals and nothing else running, it will show [3]. If one uses horizontal stacking (the special patch allowing that is covered below) then it will show [TTT]. This can be changed, if desired, in config.h in the section static const Layouts. The comment indicates that a symbol indicates a function. It then defines [] as tile and so on.

If I want to make a window larger, but don't want to switch to monocular format, hitting alt+return will swap the window with the master area. So, if I have 6 windows on screen, and then open another, but it's too small to see what I need to, I can use the alt-return keybinding. Then, if it's still not big enough, I can enlarge the master area with alt+l. Firefox is a special case. In the config.h file, it is specified that firefox will open full screen in its own tag. To oversimplify a bit, a tag can be thought of as a virtual desktop, or workspace. If we have our screen with the 6 or so windows that we made above, and want a new fresh desktop, we can hit alt+2 which will take us to tag 2. This will give us the same black screen we saw when we first logged in. You can move a window to a new tag by, while focused on it, hitting shift+alt+2, for example, which will move it to tag number 2.

Going back to firefox, the config file has it set to open in tag 9. Sometimes, one will see on a forum that someone is opening firefox in dwm and it's not opening. What's actually happening is that they're in workspace 1, call firefox with dmenu and not realize that it's open on tag 9. When you are in one tag, e.g., 1 and something is open in another tag, you'll see a little square next to that tag. So, in this case, you'd see a little square next to tag 9 in the status bar at the top. So, if in tag 1, I have a terminal running, then start firefox, either with a terminal command or using dmenu, and then go to tag 2 with alt+2, I'll see a little square next to both the 1 and the 9 on the status bar. If, while in tag 9, I have several windows open in that tag, firefox will be tiled, just as opera would be in our earlier example.

The latest version can be gotten with
git clone http://git.suckless.org/dwm

If you get a RPC error, then instead of http use
git clone git://git.suckless.org/dwm and

that should work. It has added a nice feature (that was apparently there long ago, but dropped) to add and decrease clients in the master area. So if, for example, you have one large terminal in the master area, and 3 smaller ones in the stacking area (the right half of the screen), hitting alt+i will move one of the three on the right to the left, so you would now have two on the left and two on the right. With alt+l and alt+h you can addjust the sizes of the two areas.

For me personally, this is a big plus--it gives me the ability to have 6 or 8 equally sized terminals on screen. If I then want to open a browser, I can go to another workspace, or open the browser and hit alt+m to let it have full focus.

To build dwm you need xlib, libXft, and xinerama header files. In CentOS, this can be gotten with
yum install libX11-devel libXft-devel libXinerama-devel

You will also, need the basic development tools. At an absolute minimum, I install gcc, gcc-c++, make, automake and kernel-devel.

Once downloaded, if you got the the tarball, untar with with the usual tar xvf dwm-<version>.tar.gz. Then cd into the newly created directory. Copy config.def.h to config.h, and you can edit it if you wish.

There is a howto on the Debian forums that goes through config.h line by line. It explains how you can edit the keybindings, for example, changing the Mod1 to Mod4 to make the Windows key, rather than Alt, your mod key. It also explains how to open a program on a particular tag (workspace). Rather than duplicate that, I'll just point out a few that I find handy.

For example, I like to start chromium browser with Mod+g. So, underneath the line where it defines the terminal command, I put
static const char *chromiumcmd[]  = { "chromium", NULL };

You will see the pattern in config.h. Their default is the st terminal, so the line they use to define the command for it is
static const char *termcmd[]  = { "st", NULL };

(If you prefer another terminal, such as urxvt, you can change uxterm to urxvt)

If you want specific options, then each option is added inside double quotes with a comma. For example, I want to open mutt, the mail client, with a keyboard shortcut. So, I'll define it in here.
static const char *muttcmd[]  = { "uxterm","-e","mutt", NULL };

I could add other options if I wish, with each option and its description enclosed separately in double quotes and comma. In the example below, I change the terminal that I'll use for mutt to have a background of burlywood.
static const char *muttcmd[]  = { "uxterm","-bg","burlywood","-e","mutt", NULL };

Next in the file are the key commands. For example, one of their defaults is
{ MODKEY|ShiftMask,             XK_Return, spawn,          {.v = termcmd } },

This means the mod key, alt by default, that can be changed as mentioned and shift. Then, the XK_Return is the key combo to open a terminal. The termcmd was defined above as a static constant. You don't have to know C to figure it out once you get the pattern.

If you don't want to use alt+shift, you could edit that line to read

{ MODKEY,             XK_Return, spawn,          {.v = termcmd } },

For my key binding for chromium, I use
 { MODKEY,                       XK_g, spawn,          {.v = chromiumcmd} },   

In the same way, I'll make the modkey and m my shortcut for the muttcmd that I defined earlier.
 { MODKEY,                       XK_m, spawn,          {.v = muttcmd} },   

(Note that if running a command, you would use spawn. If you look at the default config.h, you will see that spawn is only used when running an actual program, such as terminal or dmenu.)

Many dwm users contribute patches. I find the moveresize patch pretty handy. It allows you to move and resize windows with keystrokes. The patch's author has some reasonable defaults.

According to the page, one just puts the code in dwm.c below the line reading
#include "config.h"

Then add the keybindings to config.h. However, on ArchLinux and FreeBSD, at least, I found that it was also necessary to add
static void moveresize(const Arg *arg); 

in an earlier part of the file, where there is a long list of function declarations. (You can tell the part because it begins with a comment reading function declarations. If you're not familiar with C, a comment doesn't begin with a #. It begins with /* and ends with */. In the case of dwm.c, it starts around line 157 at time of writing.

At any rate you'll see a long list of things like static void, static Monitor, etc., one per line. Add that static void movereisze line there, as well as the lines you are instructed to add on the patch's page.

To do this in FreeBSD, first edit your config.h file. If you don't have one, first run in /usr/ports/x11-wm/dwm,
make extract

You will then see a work directory which contains, among other things, a dwm-<version> directory. You will have a config.def.h in there. Copy it somewhere,and edit it.

To apply the patch in FreeBSD, put it in /usr/ports/x11-wm/dwm/files and call it patch-dwm.c

Although I use packages for most things, for dwm, I use the port. To use my custom config.h file, I can cd into /usr/ports/x11-wm/dwm and run
make DWM_CONF='/home/scottro/config.h' install clean

While on the BSDs, on OpenBSD, the way to use a custom config.h and/or a patch is to use the ports tree. The OpenBSD faq has a section on using ports. In a nutshell, you fetch a ports tarball for your version, then extract it to /usr. Once you've done that, as described in this daemonforums post Before doing it, I take the moveresize patch, rename it to patch-moveresize and put it into the /usr/ports/x11/dwm/patches directory. Because it has lines like a/config.def.h b/config.def.h during make patch (see below) it will ask me which file to patch for both config.def.h and dwm.c I'm lazy so I find it easier to do it this way and just enter the file when asked, rather than editing the diff file. Anyway, after renaming the diff to patch-moveresize and putting it in the patches directory I run
cd /usr/ports/x11/dwm
make patch
cd `make show=WRKDIST`
(At this point, I finish editing config.def.h to add my custom
commands).
$ cd -
$ make install

In case it's not clear, those are backticks (above a tilde on most US keyboards), not single quotes for the make show=WRKDIST.

As of April, 2022, OpenBSD's dwm is at version 6.2.

I emailed the patch's author about needing to declare the function, but, although I've seen at least one other mention of it on the Arch forums, the author wasn't able to duplicate the problem. They also were kind enough to tell me that the reason they did it as they do, rather than an actual patch file, is to allow people to customize it. For example, you will see that the author puts a note that if you want to automatically make a client floating, rather than tiled, when resizing, replace the if statement that ends with return with togglefloating(NULL). However, this article was first written around 2012, and nowadays, they have diff files that can be downloaded. For FreeBSD, I usually take the latest and rename it as as patch-dwm.c, and as mentioned, in the BSD's put it where it's needed, as explained above, and in Linux, just patch dwm.c before compliling. (Note that the latest version at time of writing also has a patch for config.h, but I don't use that part as I have my own config.h already edited to use the patch.)

Judging from screenshots, another popular patch is the bottom stack patch. It changes the layout from a master window on the left and stacked windows in the right half of the screen to the master on top, and then stacking on the bottom, left to right. (If you look at the link, it's illustrated.) It also includes what its author calls bstackhoriz which just has rows of windows from top to bottom--again, illustrated on the page. For those who prefer, here's a screenshot of a #!Crunchbang user with a typical bstack layout.

The main patches page has instructions for patching. One can use the standard patch command
cd dwm
patch -p1 < ~/Downloads/dwm-6.0-bstack.diff

One thing about the patch that its page doesn't mention is that it alters config.def.h, not config.h. So, if you have a config.h file, apply the patch, then run the usual sudo make install, it won't have any effect. The easiest way is to probably copy your config.def.h file to a backup, then copy your config.h file to config.def.h, then copy it back. It sounds more confusing than it actually is.
cd dwm
cp config.def.h config.def.h.orig
cp config.h config.def.h
patch -p1 < ~/Downloads/dwm-6.0-backstack.diff
sudo make install 
cp config.def.h.orig config.def.h

I've left out one part. To get it to work, you have to add a keyboard entry or two in your config.h, so actually, after patching, but before running make install, one would do some additional editing. If you look at your config.h file, you'll see the entries where one changes between monocle and floating mode.
 { 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} },

You'll note that hitting the mod key and space will toggle the layouts. That's what the {0} is. I don't know enough about C to have it completely figured out, but the toggling seems to only work between some layouts--that is, it won't cycle completely through tile, float, and backstack. The user can investigate.

Once the patch is applied, add one or two more keyboard bindings for layout 3 and 4. Pick ones, obviously, that aren't already assigned. For example, I use modkey plus n for bottom stacking, because the more obvious modkey b is already used by default, toggling the bar at top on and off. I don't bother adding a key for bottom horizontal stacking, because, in later versions one gets the same effect with effect with modkey i, adding master windows. So, under the XK_m entry, I add
 { MODKEY,                       XK_n,      setlayout,      {.v = &layouts[3]} },

to my config.h before running make install. Now, I can change between standard tiled layout (using modkey and t), with one large window on the left and a few smaller ones on the right, or bottom stacking, with one large window on top and a few smaller ones beneath it, left to right. When using this method, using modkey l and modkey k increase and decrease the height, rather than width, of the master window.

In FreeBSD, you would generate and modify your config.h, place the patch file in /usr/ports/x11-wm/dwm/files then, assuming your user name was john and your custom config.h is in your home directory, cd into /usr/ports/x11-wm/dwm and run
make DWM_CONF='/home/john/config.h' install clean

One can define a floating window's position in config.h, I think, but as the only things that I need in a particular position are various terminals, I'll do that in ~/.Xdefaults. For example, I've mentioned my muttcmd that I bind to Modkey_m. Suppose I want mutt to open, as a floating window in the middle of my screen. First, I'll use a different terminal for mutt so that the terminal I use in termcmd continues to open in tiled mode. If I use urxvt as my usual terminal, perhaps I'll use uxterm for mutt. Then, I can define uxterm to always float by adding it to the tagging section of config.h, in the same way I've described with gbuffy.
 { "UXTerm",     NULL,       NULL,       0,            True,        -1 },

Now, in ~/.Xdefaults, I can define its geometry, including where I want it to open.
UXTerm*geometry:80x24+600-400

The above example puts it approximately in the middle of a screen using 1920x1080 resolution on a 24 inch monitor. I explain that a bit in my mutt article. If you're interested, just search for geometry in the article. It's in the section on configuring getmail.

I'm sure there's a way to define geometry, including position, in config.h as well, but I'm familar with doing it in .Xdefaults--and this way, it can stay consistent with different window managers.

Speaking, sort of, of geometry, I've found that dwm works more or less out of the box with multiple monitors using xinerama. On FreeBSD, where I have a workstation with 4 monitors, dwm opens 4 separate backgrounds. I can move windows between monitors, but if, while focused in a particular monitor, I begin to use mod+j to move amongst windows, it will only move around the windows in that monitor. As the man page states (it uses the term "screen" to refer to a monitor), mod+. or mod+, (a period and comma, respectively) will move focus to the next monitor. If I run dmenu, it appears in the first (upper left in my case) monitor, but the invoked program will open in whichever monitor has focus at that time.

The dwm manager is popular amongst Arch and #!Crunchbang users. The #!Crunchbang forums were pretty friendly to new users, but I doubt it's there any longer as the distribution is discontinued.

The Arch forums have a long and often useful, thread on dwm. Those familiar with Arch are probably familiar with the excellence of its wiki, which as mentioned above, also has a page about dwm.