I'll guide you through the different steps I took to install a working desktop environment in Debian using xmonad, conky and dzen2.
The final result of this configuration should look like this:
Installing the prerequisites
The first thing you should do is install the Haskell compiler and interpreter (I'm assuming you're using Debian Sid). To do that, use your beloved aptitude package manager:
To install Xmonad I recommend using the Haskell package manager cabal:
After cabal is installed, you'll have to update the package list by running cabal update. However, when doing this, cabal might tell you that there is newer version of the package manager to be installed. Follow the on screen instructions and upgrade to the latest cabal:
This will install the latest cabal into your home directory. From now on, make sure that /home/<your_username>/.cabal/bin is on the path to be using this newly installed cabal build. Run cabal update again to have the package list updated.
Installing Xmonad
Installing xmonad is as easy as running cabal install xmonad. This will try to download, compile and install xmonad and all the prerequisites. However, you may have some compile errors. This is because to compile Xmonad you need to have some development libraries installed. Typically you'll need libx11-dev, libxinerama-dev and libxext-dev, among others. To install those dependencies just look for them in the apt respositories and install them using aptitude or apt-get:
After installing xmonad, is recommended you also install xmonad-contrib, which includes some extra functionalities to Xmonad tant we will find useful later. Note that to install xmonad-contrib you'll need to install the xft development libraries, too:
Ok, with this steps you already have a fully functional xmonad version installed and ready to be run. We will need to add some eye candy to it now, however, to make it more usable. If you want to have it a try now, though, to see if everything works as expected, just edit your ~/.xsession file to make it look like this:
And now just start your X with startx (or make your display manager to use the default session). You should see... nothing. If everything worked fine, an empty X screen will be shown. Type Alt+Shift+Enter and an xterm should appear occupying the entire screen.
Installing conky and dzen2
Again, you can install both those packages from source or via the package manager. I'll show you how to install the latest versions from source.
Installing conky
Download the source tarball (at the time of the writing 1.8.1 was the latest version) from sourceforge and decompress it wherever you want:
Now run the typical commands to configure and install the package:
Note that you might need to install additional development libraries to configure and compile conky with all its bells and whistles, like libxdamage-dev, libmpd-dev and others. Do so with aptitude.
Installing dzen2
Download the latest source tarball (0.8.5 at the time of writing) from the official home page and uncompress it wherever you want:
And then just compile and install it (have a look at the README if you want to add xft and xinerama support to it):
The funny part begins: configuring xmonad and putting it all together
First of all, let me clarify some concepts. We will use xmonad as the Window Manager, dzen2 as the status bar and conky as a system data provider. Some people prefer to use xmobar instead of dzen2 + conky.
First of all, let me give credits to the resources I've used to craft xmonad the way I wanted:
kveras at Github gave me the overall skeleton of the configuration. Make sure to check his configs repository to have a look at his configs.
Haskell Wiki xmonad Config Archive, which provided some insights.
Xmonad configuration is done via a file in the .xmonad user folder called xmonad.hs. This file is Haskell code, and having a slight understanding of the Haskell syntax is recommended, although I managed to make this one without really having much Haskell knowledge.
Basically when you configure xmonad what you're really doing is compiling it with the options you pass to it, via that configuration file. There are several things (AFAIK) that can be configured:
The default terminal
The workspaces
The keybindings
The mod key
The Layout Hook
The Manage Hook
The Log Hook
Window border options
The way everything is tied together is done when calling the main method of the configuration file:
In that main method you use the defaultConfig and pass a list of the different parameters you want to customize. I'll explain briefly every configuration aspect.
The default terminal
xmonad has a special function that is intended to be used to spawn a terminal. As a daily Linux programmer this is probably very useful. With the terminal parameter, then, you can specify the program that xmonad will launch when this action is invoked. Assigning this variable to the program name (assuming it's in the path) is enough for it to work:
The workspaces
xmonad allows you to group your windows into workspaces. The concept of workspace is very similar to the virtual desktops we have in Gnome, or the Spaces we have in Mac OS X. Each window will belong to a workspace, each workspace has a name and it's own configuration, and one and only one workspace can be active (and shown) on one screen at the same time.
In the workspaces variable, we'll tell xmonad our default list of workspaces we want to work with, in a form of a string list. In my configuration I've preceded every workspace name with a number:
The keybindings
Key binding is a crucial part of the configuration. One of the goals of xmonad is being able to do all window management without having to use the mouse, so your hands are always on the keyboard. Choosing a good key layout is then very important, and having a look at all the things you can do in xmonad is important, too, so you can check what you think it may be more useful to you. The key binding configuration is a bit strange in haskell, and I don't entirely understand it myself, but I'll try to guide you through the basics. Basically you have 3 modifiers: the windows key, ctrl, shift and alt (in fact I think you can deifne any key as a valid modifier, but those are the most used). You then configure pair of key-action, telling xmonad what should it do when certain keys are pressed. Those pairs have this syntax:
The first part tells xmonad the key press, while the second the action to be performed. The key press is expressed in the form of modifier-key. The modifiers can be chained, so you can assign some action when more than a modifier is pressed. In the example, we have modMask .|. shiftMask, xK_Return. This line means press the modKey and the shift key, and then the return key. As you can see, the operator to chain modifiers is .|.. When you want a key binding without modifier, you put a 0 instead. For instance, to bind a key to print the screen, you could use something like this: ((0, xK_Print), spawn "scrot -e 'mv $f ~/screenshots/'"). This command tells xmonad to run the scrot program when the Print Screen key is pressed.
Some xmonad actions require a special attention: those used to manipulate the windows and the workspaces. I'll tell you the most used ones (at least in the default configurations).
sendMessage nextLayout is used to tell xmonad to switch to the next layout in the current workspace
setLayout $ XMonad.layoutHook conf will reset the current layout to the default one of the current workspace
sendMessage ToggleStruts is used to switch between struts mode on and off. If I'm not mistaken, struts are margins put between windows (which by default are not shown)
windows W.focusDown sets the focused window to the next window in the stack, allowing you to navigate the focus through different windows
windows W.focusUp does the same as above but in the reverse order
windows W.swapDown will swap the current window to the space occupied by the next window on the stack, useful to move windows between the different positions on the workspace
windows W.swapUp, again, does the same as above in the reverse order
windows W.swapMaster is very useful: it will put the focused window into the main space of the current layout (which is usually bigger than the other frames)
sendMessage Shrink is used to shrink the size of the main frame size
sendMessage Expand, on the other side, is used to expand the main frame size
sendMessage (IncMasterN number) will increase the number of panes of the master pane by the given number (which can be a positive or negative integer)
nextWS will switch to the next workspace
prevWS will switch to the previous workspace
shiftToNext will move the focused windo to the next workspace
shiftToPrev will move the focused window to the previous workspace
W.shift workspace will put the focused window to the selected workspace
W.view workspace will put the selected workspace as the workspace of the screen
Here's how the entire keys variable is assigned:
The mod key
The mod key is the default key xmonad uses to prepend all the keybindings (usually). Some users configure it to alt, others to ctrl, others to the windows key. It's up to you. In my config I have it configured to the windows key:
The Layout Hook
The layout hook is used to manage the layouts of the workspaces. xmonad is very configurable regarding the layouts you can use. By default, the windows are tiled by using half the screen (vertically) for the main frame, and equally sized horizontal windows for the other frames, but you can set it up however you want.
The syntax to configure the layouts is simple, you concatenate which layout you want to use for each workspace, leaving the last one as the default one:
What's not so easy is to configure those variables we used (customLayout, gimpLayout, imLayout, etc.), but you get the idea: you can configure each workspace to have its own layout, which is very useful. Here's my customLayout variable so you can see how a layout is configured:
What I'm doing here is using avoidstruts to have a layout without margins between windows, and then passing a list of the different available layouts for the workspace, separated by the ||| operator. This means that the default layout will be tiled, then Mirror tiled, etc. In this case, what Mirror does is swap the type of split, from vertical to horizontal. After that, I tell the config what I mean by tiled, in this case ResizableTall 1 (2/200) (1/2) []. What this means is to use the layout ResizableTall with 1 master window, the size of each resizing step, the width of the master panel and the slaves window height. You can check the full documentation for ResizableTall in the XMonad.Layout.ResizableTile documentation page.
The same way we've used ResizableTall for this customlayout, lots of extensions exist in xmonad that do all sorts of layout positioning.
The Manage Hook
The manage hook handles how new windows are positioned by default. xmonad allows you to put some windows into their own workspace, or in its own mode. This way, for example, you can have all Firefox windows to open in the "2:web" workspace, or have special program windows not to be tiled, but floated instead.
While I still don't fully understand all the configuration options, this is a typical line to configure a manage hook:
What we're doing with this line is tell xmonad that all windows which has a classname included in the myDev list must be shifted to the "1:main" workspace. I don't know all the criteria you can use to identify a window, but the most used ones are the className and the role. You can check those X properties using the program xprop and clicking on the desired window.
Here's my whole manage hook configuration:
The Log Hook
The log hook is what xmonad uses to tell us information about itself. Usually this hook is used in conjunction with other modules such as xmobar or, in my case, dzen2. It tells us info about the status of the different workspaces, selected windows, etcetera. When configuring this hook, you usually pipe its result to the pipe you want to use to show the info.
The log hook is also very customizable, you can select colors and icons to be shown. The documentation page for dynamicLogWithPP shows you all the options. In my configuration, because I use dzen2 as a status bar, I also use some helpers for the colors:
As you can see, I can set the colors for the different workspace statuses, and some icons to be able to know which is the current workspace layout. The last line is very important, as it's the line that outputs the results to wherever we choose.
dzen2 and conky
As I told you at the beginning, I'm using dzen2 as a status bar, and conky to get some fancy system information. To configure this, we have to tell xmonad to spawn the dzen2 and conky processes at the beginning. This can be done inside the main method, and here's how it looks like in my configuration:
Notice that I use two different bars. I'll use the left one for xmonad info and the right one for system info. What's most important here are the coordinates and size of the bars, and also the configuration file for conky. After spawning the processes, in the main method we tell xmonad to use the dzenLeftBar pipe to output the log info.
The right bar is piped to use the conky output, as configured in the .conky_dzen configuration. Conky has lots of options, covered in the Conky Objects documentation page. Here's my config:
The important things here are the background line and the out_to_x line, which tell conky to run in the background and not to output the results directly to the X. You can then configure the TEXT variable to display whatever variables you want.
Putting it all together
After all the work is done, it's time to finally put it all together. Here's the final configuration file that I currently use and that I've been showing you in this post:
Copy this into your .xmonad folder, assuming you have everything needed, and you should see a desktop similar to the one shown on the image.