frosch03.de/posts/2013-11-02-XMonadConfigure
Configuration
After one has successfully installed the Xmonad window manager, the next task is to take a look at the configuration. Over all you can start to use an unconfigured Xmonad. But the main advantages of this window manager comes from a nice configuration.
A Xmonad is configured in the xmonad.hs
file. This file is typically
located within your home directory in: ~/.xmonad/xmonad.hs
.
As you can see, the configuration is a Haskell file. This means, that you configure your Xmonad in the language Haskell. So you have all the power of Haskell at your and, to define your configuration.
My whole xmonad.hs
configuration can be found
on
github.com/frosch03.
What parts can you configure
The configuration of a Xmonad is done within the module, that contains
the main
function. Within main
, the window manager is started with
the xmonad
function. That function is supplied with a data structure,
called defaultConfig
. Within Haskell a data structure can be altered,
while passing it to a function, in the following way:
{% highlight haskell %} xmonad $ defaultConfig { workspaces = myWorkspaces } {% endhighlight %}
This tells us, that the function xmonad
is called with o parameter
called defaultConfig
, where the value of workspaces
is overwritten
by the value of myWorkspaces
.
According to this, the following sections describe parts of the
defaultConfig
data structure, that are used to overwrite the default
one, while xmonad
is called.
The virtual Desktops
First of all, there are virtual desktops. A virtual desktop, in the xmonad sense, is a subset of the running applications. These applications are shown in the context of their desktop. One application is always mapped to exactly one desktop.
It is usual not to use more than 9 desktops in xmonad. This is due to the fact, that each desktop is accessed by a shortcut and it might be a good idea to use a combination of modifiers together with a number key. But after all it is up to you to use as many desktops as you wish.
To setup your desktops you define a list of strings. Each string stands for a desktop. One way is to use just numbers for your desktop's names.
{% highlight haskell %} myWorkspaces = ["1", "2", "3", "4", "5", "6", "7", "8", "9"] {% endhighlight %}
But because we have the whole power of Haskell to configure Xmonad, one can write this as:
{% highlight haskell %} myWorkspaces = map show [1..9] {% endhighlight %}
I like to give names to my desktops. These names remind me, what kind of applications i can expect at these desktops. So my desktop configuration would look something like this:
{% highlight haskell %} myWorkspaces = [ "console" , "code" , "web" , "im" , "mail" , "skype" , "gimp" , "musi" , "irc" ] {% endhighlight %}
The mappings of Applications onto Desktops
So tiling window managers are famous for automatically organizing your windows, right? This includes routing applications onto specific desktops. The configuration of the routing is described within this section.
First of all, different applications need a way to differentiate between
them. For my configuration, i go mainly with the X11 properties
WM_CLASS
and _NET_WM_NAME
. The XMonad/ManageHook.hs
exports the
className
function, that matches WM_CLASS
, and also the title
function, that matches _NET_WM_NAME
.
If you have a look into XMonad/ManageHook.hs
, you can find more
functions to match window properties.
In order to route the applications to desktops, i like to define various
lists of strings. These strings are used to match similar applications,
that i like to go onto the same desktop. For example, i use multiple
applications for instant messaging like Pidgin or Empathy. They go
into the comApps
list:
{% highlight haskell %} webApps = ["Firefox", "Google-chrome", "Chromium"] comApps = ["Pidgin", "jabber", "Jabber", "Empathy"] mailApps = ["OUTLOOK.EXE", "Wine", "mutt", "mail"] gimpApp = ["Gimp", "gimp"] skypeApp = ["Skype", "skype", "MainWindow"] ircApps = ["workies", "irc"] {% endhighlight %}
These are the strings i use, to route most of my applications. In the next step, you see, how this lists help to do the actual routing.
The actual routing is attached to the myManagedHook
label, which is
composed from a list of list of atomic routes.
{% highlight haskell %} myManagedHook = composeAll . concat $ [ [ className =? c –> doFloat | c <- floatClass ] , [ title =? t –> doFloat | t <- floatTitle ] , [ title =? x –> doF (S.shift "1") | x <- hacking] , [ title =? x –> doF (S.shift "code") | x <- coding] , [ className =? x –> doF (S.shift "web") | x <- webApps ] , [ className =? x –> doF (S.shift "im") | x <- comApps ] , [ title =? x –> doF (S.shift "im") | x <- comApps ] , [ className =? x –> doF (S.shift "mail") | x <- mailApps ] , [ title =? x –> doF (S.shift "mail") | x <- mailApps ] , [ className =? x –> doF (S.shift "gimp") | x <- gimpApp ] , [ className =? x –> doF (S.shift "skype") | x <- skypeApp ] , [ title =? x –> doF (S.shift "irc") | x <- ircApps ] , [ isFullscreen –> doFullFloat] ] {% endhighlight %}
This syntax may look a little confusing, but what it actually say's
(corresponding to the irc-line) is: For each string inside ircApps
,
if that string equals the title of the application, shift
the
application onto the desktop called irc
.
Later, while calling the xmonad
function, the manageHook
value will
be overwritten with this definition. (Actually, the manageHook
property will be extended with this definitions)
The Layouts
Routing applications to different desktops was the first automation step. In this section, the second step is described, namely how to align multiple applications within that desktop. In this section, that will describe some of the existing layouts. There are many more layouts defined in the xmonad-contrib library. For inspiration you might want to have a look to other XMonad configurations. Also you can risk a look into the xmonad-contrib sources. There are all the layouts defined, but as usual, the documentation could be better.
A layout definition for a desktop starts with the function call
onWorkspaces
. There are two parameters to supply, the list of the
names of desktops, where the layout should apply, and the actual layout
itself.
{% highlight haskell %} onWorkspaces ["1"] ( Full ||| (Tall 1 (1/2) (3/100)) ||| Mirror (Tall 1 (1/2) (3/100)) ) {% endhighlight %}
This translates to the following. The desktop with name 1
aligns its
applications with one of the three supplied layouts. The layout Full
simply shows an application in full screen. The (Tall 1 (1/2) (3/100))
layout splits the screen vertically into two half's ((1/2)), separated
by gaps 3% ((3/100)). With Mirror
the supplied layout is modified,
such that it splits the screen horizontally.
Multiple layouts are combined with the (|||)
operator.
The result of such a onWorkspaces
definition is again something, that
can be supplied to another onWorkspaces
definition. Therefore the
layout definition of a XMonad is a long concatenation of single layout
statements. My whole layout definition is bound to the myLayout
name.
Note the where
clause with all the local definitions.
{% highlight haskell %} myLayout = smartBorders $ avoidStruts $ onWorkspaces ["1"] (Full ||| tiled ||| Mirror tiled) $ onWorkspaces ["code"] (codeColumn ||| codeRow) $ onWorkspaces ["web"] (Full ||| (gaps [(L,250), (R,250)] $ Full)) $ onWorkspaces ["im"] (imLayout gridLayout
imLayout Full | imLayout Circle) $ onWorkspaces ["mail"] (Full) $ |
onWorkspaces ["skype"] (skypeLayout) $ onWorkspaces ["gimp"] (gimp) $
onWorkspaces ["musi"] (Circle) $ tiled ||| Mirror tiled ||| Full |||
Circle where tiled = Tall nmaster delta ratio nmaster = 1 ratio = 1/2
delta = 3/100 ratio' = 1/3 delta' = 0 imLayout l = reflectHoriz
(pidginLayout l) pidginLayout l = withIM (18/100) (Role "buddy\_list") l
skypeLayout = reflectHoriz $ (withIM (1%6) (Role "MainWindow") Grid)
codeColumn
= named "Column Code" $ Tall nmaster delta' ratio' codeRow
= named "Row Code" $ Mirror $ reflectHoriz $ Tall nmaster delta' ratio'
gridLayout
= spacing 8 $ Grid
gimp = withIM (0.11) (Role "gimp-toolbox") $ reflectHoriz $ withIM
(0.15) (Role "gimp-dock") Full {% endhighlight %}
This shows, that on the code
desktop there are just two different
variations of the tiled
layout. On the code
desktop, the split is at
(1/3), either horizontally or vertically, of the screen. The idea behind
this layout is, that the smaller window can hold a interpreter window,
like the ghci. Within the bigger window, an editor with the
corresponding source file has place.
For the web
desktop, i used earlier only a full screen layout:
{% highlight haskell %} onWorkspaces ["web"] (Full) {% endhighlight %}
The actual layout, shown earlier, showed a Full
layout and also a
gaps
layout. This gaps
layout inserts a spacer of 250 pixels on the
left and the right. I think, that a browser that is smaller, can help
reading text on pages with no line break.
For the im
desktop a layout is used, that places a specific window on
one side. withIM (18/100) (Role "buddy_list")
points out, that a
window with the role buddy_list
gets a column of 18% of the screen
width. Usually this is on the left side, but the call to reflectHoriz
switches that to the right. The rest of the screen is managed with
either gridLayout
, Full
, or Circle
. So on the im
desktop, there
are three layouts to choose from.
The other desktops have similar layouts or just a single one.
The Key Bindings
Another important thing for XMonad to configure, are the keybindings. They are important, because XMonad is mostly controlled via the keyboard. There are keybindings for opening a terminal, moving windows within the layout, shifting them to other desktops, or switching the actual one. Here are keybindings cheetshets for the default key bindings.
Additionally one can change this key bindings or add new ones. The following lists my key bindings, which i add to the default ones.
{% highlight haskell %} myKeys x = M.fromList $ [ ((modMask x, xK\_p), shellPrompt myXPConfig) , ((modMask x, xK\_y), scratchpadSpawnActionTerminal "urxvt") , ((modMask x, xK\_m), windows shiftMaster) , ((modMask x .|. shiftMask, xK\_m), manPrompt myXPConfig) , ((modMask x .|. shiftMask, xK\_s), sshPrompt myXPConfig) , ((modMask x .|. shiftMask, xK\_f), focusUrgent) , ((modMask x .|. shiftMask, xK\_j), windows swapDown) , ((modMask x .|. shiftMask, xK\_k), windows swapUp) , ((modMask x .|. shiftMask, xK\_Return), spawn "/usr/bin/urxvt")
, ( (modMask x .|. controlMask, xK_l) , spawn "gnome-screensaver-command --lock" ) , ( (modMask x .|. controlMask, xK_a) , spawn "/home/frosch03/whatIsThisPlace" ) , ( (0, xK_XF86ScreenSaver) , spawn "gnome-screensaver-command --lock" ) , ( (0, xK_XF86AudioRaiseVolume) , spawn "/home/frosch03/.xmonad/volUp.sh" ) , ( (0, xK_XF86AudioLowerVolume) , spawn "/home/frosch03/.xmonad/volDn.sh" ) ] newKeys x = myKeys x `M.union` keys defaultConfig x
{% endhighlight %}
One can choose the modifiers out of:
modMask
corresponds to your modifier key (for me that is the
windows-key)
shiftMask
is your shift key
controlMask
corresponds to ctrl
They are combined with the (.|.)
operator. A key binding is defined
with a tuple. The first element is also a tuple from the modifiers in
the front, and the key in the second. The second element is the command,
that is issued, if the key is pressed.
The last line shows, that this configurations are additional. As you can
see, my definition is bound to the identifier myKeys
. The union of my
keys and the default keys (keys defaultConfig
) is labeled
newKeys
, which will be used later to overwrite the default
configuration.
The xmonad call within main
Last but not least, here is the call to xmonad
within the main
function. This call looks like the following:
{% highlight haskell %} xmonad $ withUrgencyHook NoUrgencyHook $
defaultConfig { borderWidth = 1 , normalBorderColor = "#333333" ,
focusedBorderColor = "#999999" , modMask = mod4Mask , keys = newKeys ,
workspaces = myWorkspaces , manageHook = manageDocks <+> manageHook
defaultConfig <+> myManagedHook , layoutHook = myLayout , logHook = (
dynamicLogWithPP $ myDzen2PP { ppOutput = \x -> writeFile "/tmp/dmpipe1"
$ "1" + x + "\n"} ) } {% endhighlight %}
The additional call to withUrgencyHook
is for my information bar. That
configuration will be covered in another article. Also the line
logHook
is for displaying the bar.
Beside that, the width of the border is set to 1 pixel via
borderWidth
. A color is specified for the focused window with
focusedBorderColor
, and another color for the normal windows border
via normalBorderColor
. The modifier key modMask
is set to the
mod4Mask
, which in my case corresponds to the windows key.
Then the different configurations, defined earlier, are placed. The
key's newKeys
is already the combination of the default keys, and the
newly defined myKeys
. Therefore keys
is just overwritten with
newKeys
. The same is true for myWorkspaces
, and myLayout
. For the
manageHooks
, the defined myManakgeHook
gets combined with the
default one, and the manageDocks
.
Finally
And there you have it, that are the basic building blocks of a XMonad configuration. You can compile your configuration by:
{% highlight bash %} xmonad –recompile {% endhighlight %}
If that doesn't throws any errors, the running XMonad can be restarted by:
{% highlight bash %} xmonad –restart {% endhighlight %}
That's it, for the basic configuration part. In another post, i will describe, how to build a information bar with dzen2.
Again, you can find my whole configuration file on github.com/frosch03.