I'm going to take you step-by-step through the process of configuring Xmonad, setting up a status bar with xmobar, setting up a tray with trayer, and making it all play nicely together. I use this exact configuration on everything from a 1024x600 Eee PC to a 1920x1200 24" workstation, and it works well on all of them.

Preliminaries

First you'll want to install xmonad. You can find instructions for that on xmonad.org. I'm going to assume xmonad 0.8 here.

This guide will work for any operating system. I happen to use Debian, so when I talk about installing software, I can give you Debian commands. But you can run xmonad all over the place; just substitute the appropriate commands for your system.

This installs xmonad itself, everything you need to configure it, and dwm-tools, which provides the Mod-P launching feature. If you're not on sid, consult the xmonad download site -- note that there are etch binaries there, too. If you're downloading the etch binaries, you'll need the etch ghc6 and libghc6-*-dev*.deb binaries as well.

Set up xmonad in your .xsession as directed in the xmonad guided tour. You should have xmonad up and running before continuing.

Customizing xmonad

So the first thing you will want to do is customize xmonad. Make a directory called ~/.xmonad, and in there, create a file named xmonad.hs. We'll start off with importing some of the utility modules we will use:

Next, a basic configuration -- which is the same as the default -- is this:

main=doxmonad$defaultConfig

Over at the how to write a config file page -- which you should go read right now -- there are instructions for testing your config file. You should be able to save the above file, with the import lines plus the other two, and validate it with ghci, then press Mod-q to load it up. Another way to validate your xmonad.hs is to simply run `xmonad --recompile' in a terminal. You'll see errors if it's bad, and nothing if it's good.

Now how about something real? Replace the lines starting with main with:

(Disclaimer: The author in reality does not use this 'comma first' style. However, since it is the style used in xmonad, xmonad-contrib and most all xmonad.hs files, he has kindly consented to allow your friendly wiki gardeners to convert the original Haskell from 'comma last' style. This should make copying pieces from other configs a bit easier.)

Also, ghc sees tab characters as eight spaces, so to prevent confusion ensure your editor produces eight space tabs or expands tabs to spaces when editing haskell files. XMonad convention is to always expand tabs and (mostly) indent by four space increments.
Wikibooks has a great explanation of layout rules and indentation in haskell.

What this does is take the default configuration (defaultConfig) and modify two aspects of it -- the manageHook and layoutHook. This particular recipe comes from the Xmonad FAQ and adds the support we need for a status bar and dock.

I'll show my completed file at the end of this page, but for now let's add a few additional things in. By default, the Mod key is Alt, which is also used in Emacs. Sometimes Emacs and xmonad want to use the same key for different actions. Rather than remap every common key, I just change Mod to be the Windows key that's between Ctrl and Alt. So I add this line after layoutHook:

,modMask=mod4Mask-- Rebind Mod to the Windows key

The two dashes are a comment to the end of the line.

I also want to bind Mod-Shift-z to lock my screen with the screensaver, control-PrintScreen to take a snapshot of one window, and Printscreen to take a snapshot of the entire screen. My config file, starting with main, now looks like:

You can find the names for keys in the haskell-X11 source package in the files Graphics/X11/Types.hsc and Graphics.X11.ExtraTypes.hsc.

To support screen capture, run apt-get install scrot. I will cover setting up the screensaver later in this tutorial.

Did you notice the 0 in the xK_Print line? The first part of the (0, xK_Print) tuple states what modifier keys (ctrl, alt, etc.) have to be held down for a pattern to match. For the PrintScreen key, we don't need anything to be held down, and the zero indicates that. The 'sleep' before running the 'scrot -s' command is to leave time for keys to be released before scrot -s tries to grab the keyboard.

Installing xmobar

Next, it's time to get started with xmobar, the status bar. You can use xmobar or dzen2. dzen2 probably has a few more features, but xmobar has lower resource utilization and is considerably easier and more reliable to set up. I found it does more than everything I need, and recommend it.

You can download xmobar from its homepage, linked to above. That page also has compilation and installation instructions. As of Sept. 17, 2008, I also maintain a Darcs tree. Most patches have been merged back as of xmobar 0.9.1, but a couple patches that shorten the number formatting are unapplied. You can download my tree and merge with the official tree with:

If you darcs, apt-get install darcs or get it from your distribution or darcs.net.

Configuring xmonad to use xmobar

So, let's talk a little bit about how xmonad and xmobar fit together. You can piece them together in several different ways.

xmobar accepts input on its stdin, which it can display at an arbitrary position on the screen. It can also easily display other information. We want xmonad to send xmobar the stuff that I have at the upper left of my screenshot: information about available workspaces, current layout, and window manager.

We could, then, have xmonad write this stuff to its stdout, and write xmonad | xmobar in ~/.xsession. But it's more elegant and useful, in my opinion, to have xmonad fire up xmobar itself. This is pretty simple. Our main part of xmonad.hs will now look like:

-- make sure to edit paths to xmobar and .xmobarrc to match your system.-- If xmobar is in your $PATH, and its config is in ~/.xmobarrc you don't-- need the xmobar path or config file, use: xmproc <- spawnPipe "xmobar"main=doxmproc<-spawnPipe"/path/to/xmobarbinary /home/jgoerzen/.xmobarrc"xmonad$defaultConfig{manageHook=manageDocks<+>manageHookdefaultConfig,layoutHook=avoidStruts$layoutHookdefaultConfig,logHook=dynamicLogWithPP$xmobarPP{ppOutput=hPutStrLnxmproc,ppTitle=xmobarColor"green""".shorten50},modMask=mod4Mask-- Rebind Mod to the Windows key}`additionalKeys`[((mod4Mask.|.shiftMask,xK_z),spawn"xscreensaver-command -lock"),((controlMask,xK_Print),spawn"sleep 0.2; scrot -s"),((0,xK_Print),spawn"scrot")]

We've added a line right after "main". We fire up xmobar, and pass to it a command-line argument giving the path to its config file -- we will create this file in a minute. Then we also define a logHook. We tell xmonad that the way we get output to xmobar is with the command hPutStrLn xmproc -- this transmits the data via a pipe to xmobar. We also tell it that we want to put the first 50 characters of the window title in the title area.

When using hPutStrLn in your logHook make sure to add StdinReader to your xmobarrc commands and template as described below or xmonad may freeze when the unread pipe fills up.

Save this and test it with ghci.

Configuring xmobar

Now, before this will work, we have to configure xmobar. Here's a slightly simplified version of what I use, which is mostly similar to the sample you can find in the xmobar source package.

NoteWith xmobar-version > 0.9.2 , i.e. from darcs, fields you leave out and will be replaced by the default, and the order doesn't matter.

First, I set the font to use for my bar, as well as the colors. The position is documented well on the xmobar home page. This says to put my bar in the upper left of the screen, and make it consume 90% of the width of the screen.

In the commands list you, well, define commands. The commands are the pieces that generate the content that is available to display, which will later be combined together in the template. I define a weather widget, a CPU widget, memory and swap widgets, a clock, and of course the data from xmonad via the StdinReader.

The template then combines them together. Stuff to be left-justified goes before the } character, things to be centered after it, and things to be right justified after {. I have nothing centered so there is nothing in between them.

Warning: even though the config file uses Haskell syntax, it doesn't accept comments, allow you to change the field order, or drop fields. This is because xmobar uses reads to parse it.

Save that to ~/.xmobarrc. Now you should be able to press Mod-q and have xmonad load up xmobar, and have it work.

Replace both occurrences of EGPF with your choice of ICAO weather stations. There is a list of ICAO station codes maintained at ucar.edu. You can of course monitor more than one if you like, see the xmobar home page for more details.

Adding Keyboard LED Indication

My 9.1" Eee doesn't have capslock or numlock lights. Before xmonad, I had an applet to do that, but that's not very efficient now. If you want this, read this section, otherwise skip it.

This uses xsetroot to set my background color. It can also use images; see its manpage for more.

Then we fire up trayer, the icon tray. The options tell it to go on the top right, with a default width of 10% of the screen (to nicely match up with the status bar, which we set to a width of 90% of the screen). We give it a color and a height.

Then we fire up gajim, the screensaver daemon, and if installed, the network manager applet and the power manager.

Finally, we start xmonad.

Mission accomplished!

Final Touches

There may be some programs that you don't want xmonad to tile. The classic example is Gimp. It pops up all sorts of new windows all the time, and they work best at defined sizes. It makes sense for xmonad to ignore them. Over at the general tips page, there are suggestions on how to accomplish this. The xmonad FAQ has instructions on using xprop to find the class (or other properties) of your window.

We are going to compose a list like so:

myManageHook=composeAll[className=?"Gimp"-->doFloat]

I also don't want xmonad to tile the VNC viewer, because I want to manage its size myself. Very well; I can add it:

importXMonadimportXMonad.Hooks.DynamicLogimportXMonad.Hooks.ManageDocksimportXMonad.Util.Run(spawnPipe)importXMonad.Util.EZConfig(additionalKeys)importSystem.IOmyManageHook=composeAll[className=?"Gimp"-->doFloat,className=?"Vncviewer"-->doFloat]main=doxmproc<-spawnPipe"/path/to/xmobarbinary /home/jgoerzen/.xmobarrc"xmonad$defaultConfig{manageHook=manageDocks<+>myManageHook-- make sure to include myManageHook definition from above<+>manageHookdefaultConfig,layoutHook=avoidStruts$layoutHookdefaultConfig,logHook=dynamicLogWithPP$xmobarPP{ppOutput=hPutStrLnxmproc,ppTitle=xmobarColor"green""".shorten50},modMask=mod4Mask-- Rebind Mod to the Windows key}`additionalKeys`[((mod4Mask.|.shiftMask,xK_z),spawn"xscreensaver-command -lock"),((controlMask,xK_Print),spawn"sleep 0.2; scrot -s"),((0,xK_Print),spawn"scrot")]

Tips on daily use

Here are a few things that occurred to me as I was learning xmonad.

Minimizing Windows

xmonad doesn't have a "minimize" feature. So I've designated workspace 9 for this purpose. When I want to hide a window, I Mod-Shift-9 it. That makes it go away. When I want it back, it's Mod-9, the Mod-j or Mod-k to select it, then Mod-Shift-1 Mod-1 or whatnot to zip it back and go back. It works surprisingly well.

Use of workspaces

I used to use KDE, and I used workspaces there too. But with the ability to so easily zip windows around to different workspaces, and to instantaneously change between them using only the keyboard, it's a lot easier to use.

Also, I find myself not wanting to have quite as many windows open on a given desktop at once. I generally have desktop 1 be for shells, 2 for email/IM, 3 for web, and 9 for music/minimized stuff. But I'm just learning this so far, and may find a better way. Don't hesitate to try different ways of organizing and use what works for you.