Personal tools

Xmonad/xmonad development tutorial

From HaskellWiki

< Xmonad
Revision as of 03:46, 20 February 2008 by Byorgey (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

So, you've got an idea for a great extension to xmonad, and you think other people will enjoy using it, too. The only problem is, you've never written an xmonad extension before, "darcs" sounds scary, and you're not sure what the proper procedures are. Well, never fear --- this tutorial will have you writing xmonad extensions in no time!

As a running example, let's suppose you want to develop an extension to close all the windows on the current workspace with a single key combination. We'll walk through the entire process of creating, testing, and submitting such an extension. I encourage you to follow along, either by copying and pasting the code shown here, or you can just use it as a guideline for making your own extension.

Before we begin, here are some other resources which may be useful to you during this process:


1 Getting started

1.1 Getting the latest darcs sources

The first step in creating an xmonad extension is to get the latest development sources using darcs. darcs is a distributed revision control system, written in Haskell, which is used to track the source code for xmonad and the xmonad-contrib extension library. If you already have the darcs sources, you can skip this section.

First, of course, you will have to install darcs itself; it probably already exists as a package for your operating system's package manager. Otherwise, you can grab a tarball from

Next, create a directory where you will store the xmonad repositories.

[brent@xenophon:~/haskell]$ mkdir xmonad-dev-tut
[brent@xenophon:~/haskell]$ cd xmonad-dev-tut/

Now, use 'darcs get' to create new directories and download the current repositories into them:

[brent@xenophon:~/haskell/xmonad-dev-tut]$ darcs get
Copying patch 941 of 941... done!
Applying patch 941 of 941... done.
Finished getting.
[brent@xenophon:~/haskell/xmonad-dev-tut]$ darcs get
Copying patch 1292 of 1292... done!
Applying patch 1292 of 1292... done.
Finished getting.
[brent@xenophon:~/haskell/xmonad-dev-tut]$ ls
xmonad  XMonadContrib

Be patient, it might take a while to download the entire repositories the first time. After the initial download, however, subsequent pulls should be much faster, since darcs will only need to download any new patches.

Congratulations, you now have the latest xmonad sources!

1.2 Staying up-to-date

To keep your local repositories up-to-date, you should periodically run the command 'darcs pull' in each directory in order to download any new patches, especially before starting any new development. Darcs is generally good at merging patches, but it's best not to make it work any harder than necessary.

1.3 Building

To build the latest development versions of xmonad and xmonad-contrib, just issue the usual incantations:

[brent@xenophon:~/haskell/xmonad-dev-tut]$ cd xmonad/
[brent@xenophon:~/haskell/xmonad-dev-tut/xmonad]$ runhaskell Setup configure --prefix=$HOME --user && runhaskell Setup build && runhaskell Setup install
Configuring xmonad-0.6...
Preprocessing library xmonad-0.6...
Preprocessing executables for xmonad-0.6...
Building xmonad-0.6...
...blah blah...
Writing new package config file... done.
[brent@xenophon:~/haskell/xmonad-dev-tut/xmonad]$ cd ../XMonadContrib/
[brent@xenophon:~/haskell/xmonad-dev-tut/XMonadContrib]$ runhaskell Setup configure --prefix=$HOME --user && runhaskell Setup build && runhaskell Setup install
Configuring xmonad-contrib-0.6...
Preprocessing library xmonad-contrib-0.6...
Building xmonad-contrib-0.6...
...blah blah BLAH...
Writing new package config file... done.

Now restart xmonad. Note, if you are upgrading from a previous version of xmonad, you may need to first 'unregister' the old packages with ghc. Type 'ghc-pkg list xmonad' and 'ghc-pkg list xmonad-contrib' at a prompt; if multiple versions are listed you need to unregister all but the newest. For example:

[brent@xenophon:~/haskell/xmonad-dev-tut]$ ghc-pkg unregister xmonad-0.5 --user
Saving old package config file... done.
Writing new package config file... done.
[brent@xenophon:~/haskell/xmonad-dev-tut]$ ghc-pkg unregister xmonad-contrib-0.5 --user
Saving old package config file... done.
Writing new package config file... done.

1.4 An important note!

If you pull new patches into the xmonad repository or make changes to it, and rebuild the xmonad library, you should do a clean build of the xmonad-contrib library afterwards ('runhaskell Setup clean && runhaskell Setup configure --prefix=... && ... build && ... install'). Otherwise, the Cabal build process for xmonad-contrib may not notice that the xmonad library (which xmonad-contrib depends on) has changed, and xmonad-contrib will not get rebuilt properly, leading to possible crashes at runtime. Annoying, but necessary.

2 Creating a module

2.1 Adding the module file

So, let's add that module! We'll call it XMonad.Actions.KillAll, so it needs to go in the XMonad/Actions/ subdirectory of the XMonadContrib repository:

[brent@xenophon:~/haskell/xmonad-dev-tut/XMonadContrib]$ $EDITOR XMonad/Actions/KillAll.hs &

To avoid flame wars, I won't reveal my favorite editor here. =)

Now we've got... a nice, blank module! What now? Well, let's begin by adding some standard header stuff. In practice you can just open up some other module and copy and paste, changing the stuff that's appropriate to change.

-- |
-- Module      :  XMonad.Actions.KillAll
-- Copyright   :  (c) 2008 Brent Yorgey
-- License     :  BSD3-style (see LICENSE)
-- Maintainer  :  Brent Yorgey <>
-- Stability   :  unstable
-- Portability :  unportable
-- Provides an action to kill all the windows on a workspace.
module XMonad.Actions.KillAll where

OK, that's a good start. Now let's go to a prompt and see what we've done so far:

[brent@xenophon:~/haskell/xmonad-dev-tut/XMonadContrib]$ darcs whatsnew
No changes!

Huh? No changes? That's because we haven't added KillAll.hs to the repository yet, so darcs ignores it. Let's do that now:

[brent@xenophon:~/haskell/xmonad-dev-tut/XMonadContrib]$ darcs add XMonad/Actions/KillAll.hs 
[brent@xenophon:~/haskell/xmonad-dev-tut/XMonadContrib]$ darcs whatsnew
addfile ./XMonad/Actions/KillAll.hs
hunk ./XMonad/Actions/KillAll.hs 1
+-- |
+-- Module      :  XMonad.Actions.KillAll

Ah, much better! In general, the 'darcs whatsnew' command shows any changes that are currently unrecorded.