C
Back - Home

Richard Riley's web site (inprogress)

The XMonad Window Manager

Table of Contents

XMonad

Overview

I have recently switched to using the superb XMonad Window Manager which is a tiling Window Manager written using the functional language Haskell.

XMonad provides the following advantages over metacity (Gnome's default Window Manager)

  • Uses much less resources
  • programmable using Haskell
  • Much easier "keyboard only" navigation (although mouse still recommended for screenshots etc).
  • Xinerama multi display integration

Haskell

Haskell is a real challenge for someone more familiar with procedural imperatives language programming, but some good tutorials exist and I can heartily recommend the Real World Haskell tutorial.

Haskell Emacs Integration

A haskell mode exists : editing Haskell with Emacs One thing that niggled me was the poor integration of Haskell help in Emacs which encompassed XMonad and XMonad-Contrib. Thanks to the Hayoo guys for giving me the right URL signature with which I added a bit of eLisp. You can integrate Hayoo with emacs as described here: Integrating Hayoo with Emacs with Browe Apropos Url.

Alternatively the following snippets give you something to start with:

(defun rgr/hayoo()  
  (interactive)
  (let* ((default (region-or-word-at-point))
         (term (read-string (format "Hayoo for the following phrase (%s): "
                                    default))))
    (let ((term (if (zerop(length term)) default term)))
      (browse-url (format "http://holumbus.fh-wedel.de/hayoo/results/hayoo.html?query=%s&start" term)))))


(define-key haskell-mode-map (kbd "<f3>") (lambda()(interactive)(rgr/hayoo))) 

My XMonad Configuration

I had used XDM and xsession. I moved back to GDM and Gnome : XMonad and Gnome

Too many things simply didnt work properly without "real" Gnome. I got tired of chasing down what.

Here is my root user's .Xdefaults which make uxvrt have a red tinge when I am in the shell as root:

*background: darkslategrey
*font: -adobe-courier-medium-r-normal-*-12-100-*-*-m-*-iso8859-1
*foreground: white
urxvt*transparent:true
urxvt*font: -adobe-courier-medium-r-normal-*-12-100-*-*-m-*-iso8859-1
urxvt*tintColor:red
urxvt*saveLines:12000
urxvt*scrollBar:true
urxvt*scrollBar_right:false
urxvt*scrollstyle:rxvt

Here is a screenshot

My .xsession file

Note that I dont use this anymore. I now use gnome. See my .gnomerx below

#!/bin/bash

# if [ -f $HOME/.bash_env ]; then
#     source $HOME/.bash_env
# fi

# source $HOME/bin/xmod

# export WINDOW_MANAGER=xmonad

# pidof xcompmgr || xcompmgr -c -C -F &
# pidof emacs || emacs --daemon &
# gnome-session &

# exec xmonad



My .gnomerc

if [ -f ~/.bash_env ]; then
    . ~/.bash_env
fi

if [ -f ~/.xmodmaprc ]; then
    xmodmap ~/.xmodmaprc
fi



My .screenrc

This is nice. Using screen in conjunction with urxvt and xmonad. See the xmonad.hs for how it launches/hides the screen session.

# For a complete list of available commands, see http://bit.ly/jLtj
# Added to by Richard G. Riley <rgr@richardriley.net>

# Message to display in the status line when activity is detected in a
# monitored window.
activity              "activity in %n (%t) [%w:%s]~"

# Detach session on hangup instead of terminating screen completely.
autodetach            on              # default: on

# When a bell character is sent to a background window, screen displays a
# notification in the message line. The notification message can be re-defined
# by this command.
bell_msg              "bell     in %n (%t) [%w:%s]~"

# This command controls the display of the window captions. Normally a caption
# is only used if more than one window is shown on the display.
caption               always          "%{= kw}%?%-Lw%?%{+b kw}%n*%t%f %?(%u)%?%{= kw}%?%+Lw%?"

# Select line break behavior for copying.
crlf                  off             # default: off

# Select default utmp logging behavior.
#deflogin              off             # default: on

# Set default lines of scrollback.
defscrollback         300            # default: 100

# If set to 'on', screen will append to the 'hardcopy.n' files created by the
# command hardcopy; otherwise, these files are overwritten each time.
hardcopy_append       off              # default: off

# This command configures the use and emulation of the terminal's hardstatus
# line. The type 'lastline' will reserve the last line of the display for the
# hardstatus. Prepending the word 'always' will force screen to use the type
# even if the terminal supports a hardstatus line.
hardstatus            alwayslastline  "%{+b kr}[ %H ] %{ky} Load: %l %-=%{kb} %c  %Y.%m.%d"
msgwait               15

# Set message displayed on pow_detach (when HUP is sent to screen's parent
# process).
pow_detach_msg        "BYE"

# Set the default program for new windows.
shell                 bash

# Default timeout to trigger an inactivity notify.
silencewait           30              # default: 30

# Change text highlighting. See http://bit.ly/11RDGZ
# sorendition           gK

# Do NOT display copyright notice on startup.
startup_message       off             # default: on

# Set $TERM for new windows. I have more luck with 'linux' than Terminal's
# default 'xterm-color' (^H problems). Comment out to use the default.
# term   gnome-256color
# term                  xterm-color
# term rxvt-256color
# term xterm-256color
# term screen-256color
term xterm-256color

# Tweak termcap, terminfo, and termcapinfo  entries for best performance.
termcap               linux           'AF=\E[3%dm:AB=\E[4%dm'
termcap               xterm-color     'AF=\E[3%dm:AB=\E[4%dm'
terminfo              linux           'AF=\E[3%p1%dm:AB=\E[4%p1%dm'
terminfo              xterm-color     'AF=\E[3%p1%dm:AB=\E[4%p1%dm'

# Allow xterm / Terminal scrollbars to access the scrollback buffer. This
# enables the behavior you'd expect, instead of losing the content that scrolls
# out of the window.
termcapinfo           linux           ti@:te@
termcapinfo           xterm-color     ti@:te@

# Use visual bell instead of audio bell.
vbell                 off              # default: ???

# Message to be displayed when the visual bell is triggered.
vbell_msg             " *beep* "

# screen -t EMACS 1 edit -nw
# screen -t RSS 2  myrss
# screen -t EXIM$ 8 ssh richardriley.net /home/shamrock/bin/maillog
screen -t BASH 0 bash

.xmodmaprc : keyboard modifying using xmodmap

remove Lock = Caps_Lock
add Mod4 = Caps_Lock
remove Mod4 = Super_R
remove control = Super_R
add Mod3 = Super_R

my .Xdefaults

*foreground: white
*background: black
*cursorColor:green

#include "/home/shamrock/.emacs.d/.xEmacs"

XTerm*backarrowKey: false 
XTerm*cursorBlink: true
XTerm*metaSendsEscape:true
XTerm*eightBitInput: False
XTerm*modifier: meta
XTerm*termName: xterm-256color

XTerm*faceName: Inconsolata
XTerm*faceSize: 10

XTerm:*font: "-*-lucida sans typewriter-medium-*-*-*-12-*-*-*-*-*-*-*"
Emacs:*font: "-*-lucida sans typewriter-medium-*-*-*-12-*-*-*-*-*-*-*"

XTerm*background: #3f3f3f
XTerm*foreground: white




My xmonad.hs file

Particularly nice is the use of urxvt for scratchpad and default terminal which in turn connects to a screen session.

{-# LANGUAGE ScopedTypeVariables #-}

-----------------------------------------
-- xmonad.hs
-- Richard G. Riley <rgr@richardriley.net>
-- Thanks to the #xmonad chaps
-- on freednode IRC for help and advice.
-------------------------------------------

import XMonad
import XMonad.Core

import Data.List

import qualified XMonad.StackSet as W

import Control.OldException
import Control.Monad
import DBus
import DBus.Connection
import DBus.Message
import XMonad
import XMonad.Config.Gnome
import XMonad.Hooks.DynamicLog


import XMonad.Actions.GridSelect 
import XMonad.Actions.MouseResize
import XMonad.Actions.CycleWS
import XMonad.Actions.CycleWindows  (rotFocusedUp, rotFocusedDown)
import XMonad.Actions.RotSlaves

import XMonad.Actions.PerWorkspaceKeys
import qualified XMonad.Actions.Submap as SM
import qualified XMonad.Actions.Search as S
import XMonad.Actions.WindowBringer
import XMonad.Actions.WindowGo
import qualified Data.Map as M
import XMonad.Hooks.DynamicLog
import XMonad.Hooks.ManageDocks
import XMonad.Hooks.ManageHelpers
import XMonad.Hooks.SetWMName
import XMonad.Layout.Circle
import XMonad.Layout.DragPane
import XMonad.Layout.IM
import XMonad.Layout.Grid
import XMonad.Layout.NoBorders
import XMonad.Layout.PerWorkspace
import XMonad.Layout.Monitor
import XMonad.Layout.MultiToggle
import XMonad.Layout.MultiToggle.Instances
import XMonad.Layout.Reflect
import XMonad.Layout.SimpleFloat
import XMonad.Layout.WindowArranger
import XMonad.Prompt
import XMonad.Prompt.AppLauncher as AL
import XMonad.Prompt.Shell
import XMonad.Prompt.Ssh
import XMonad.Util.Run
import qualified XMonad.StackSet as Window
import XMonad.Util.EZConfig(additionalKeys)
import XMonad.Util.Scratchpad
import XMonad.Util.NamedScratchpad

import System.Posix.Unistd (getSystemID, nodeName)
import System.Environment (getEnvironment)



import Graphics.X11.Xlib

myWorkSpaces    = ["Desktop", "TV" ,"gimp"]
myManageHook =  composeAll . concat $
                               [ 
                               [fmap(i `isPrefixOf`) resource --> doIgnore  | i <- myIgnores]
                               ,[fmap(f `isPrefixOf`) resource --> doCenterFloat   | f <- myFloats]
                               ,[ 
                                className =? "Gimp"  --> doShift "gimp"
                               , className =? "MPlayer" --> doShift "TV"
                               , className =? "MPlayer" --> (ask >>= doF . W.sink)
                               ,isDialog  --> doCenterFloat
                               ,isFullscreen  --> doFullFloat
                                ] 
                               ]

    where
      myFloats        = ["Gnome-system-monitor","Vncviewer", "Steam", "xvidcap", "Xvidcap", "recordMyDesktop","screensaver","gnome-panel","ktorrent","Ddd","GPicker","gpicker", "barrybackup","pinentry-gtk-2","recordMyDesktop"]
      myIgnores       = ["ddd", "bsh-Interpreter","Do","gnubiff","xsane"]
      role = stringProperty "WM_WINDOW_ROLE"



myLayout =
    onWorkspace "gimp" (avoidStruts $ mouseResize $ windowArrange $ withIM 0.25 (Role "gimp-toolbox") Full)  $
    onWorkspace "TV"   (smartBorders $ avoidStruts ( Full ||| simpleFloat)) $
    onWorkspace "VM"   (smartBorders Full) $
    avoidStruts $ mouseResize $ windowArrange $ mkToggle (single REFLECTY) $ mkToggle (single REFLECTX) $ mkToggle (single MIRROR) $ mkToggle(NOBORDERS??FULL??SMARTBORDERS??EOT) $ (Tall 1 (3/100) (1/2) ||| dragPane Horizontal 0.1 0.5||| Grid ||| Circle )

myFocusFollowsMouse :: Bool
myFocusFollowsMouse = False


-- myTerminal = "urxvt -name scratchpad -e screen -q -S rgr -d -R"
-- myTerminal = "xterm -e myscreen"
myTerminal = "xterm -name scratchpad -e screen -q -S rgr -d -R"
-- myTerminal = "gnome-terminal"

-- myFadeLogHook :: X ()
-- myFadeLogHook = fadeInactiveLogHook fadeAmount
--       where fadeAmount = 0.5

scratchpads = [

 -- run htop in xterm, find it by title, use default floating window placement

     NS "iceweasel" "iceweasel -a icepop" (className =? "Iceweasel") nonFloating ,


     NS "emacs" "edit" (className =? "Emacs") nonFloating ,

     NS "unison" "unison-gtk" (className =? "unison-gtk") nonFloating ,

 -- 1/6 of screen width from the left, 1/6 of screen height
 -- from the top, 2/3 of screen width by 2/3 of screen height

     NS "htop" "xterm -name htop -e htop" (title =? "htop") 
         (customFloating $ W.RationalRect (1/3) (1/8) (2/3) (1/2)) ,

     NS "newsbeuter" "xterm -e myrss" (title =? "myrss")
         (customFloating $ W.RationalRect (1/2) (0) (1/2) (1/4)) 


 ] where role = stringProperty "WM_WINDOW_ROLE"


isLightHost :: String -> Bool
isLightHost hostname = hostname `elem` ["X30","PUB","Hermes"]

-- This retry is really awkward, but sometimes DBus won't let us get our
-- name unless we retry a couple times.
getWellKnownName :: Connection -> IO ()
getWellKnownName dbus = tryGetName `catchDyn` (\ (DBus.Error _ _) ->
                                                getWellKnownName dbus)
    where
      tryGetName = do
        namereq <- newMethodCall serviceDBus pathDBus interfaceDBus "RequestName"
        addArgs namereq [String "org.xmonad.Log", Word32 5]
        sendWithReplyAndBlock dbus namereq 0
        return ()

main = withConnection Session $ \ dbus -> do 

  putStrLn "Getting well-known name."
  getWellKnownName dbus
  putStrLn "Got name, starting XMonad."

  host   <- fmap  nodeName getSystemID

  xmonad myConfig
             { logHook    = dynamicLogWithPP $ defaultPP {
                   ppOutput   = \ str -> do
                     let str'  = "<span font=\"Terminus 9 Bold\">" ++ str ++ 
                                 "</span>"
                         str'' = sanitize str'
                     msg <- newSignal "/org/xmonad/Log" "org.xmonad.Log" 
                                "Update"
                     addArgs msg [String str'']
                     -- If the send fails, ignore it.
                     send dbus msg 0 `catchDyn`
                       (\ (DBus.Error _name _msg) ->
                         return 0)
                     return ()
                 , ppTitle    = pangoColor "#003366" . shorten 50
                 , ppCurrent  = pangoColor "#006666" . wrap "[" "]"
                 , ppVisible  = pangoColor "#663366" . wrap "(" ")"
                 , ppHidden   = wrap " " " "
                 , ppUrgent   = pangoColor "red"
                 }

             }

myConfig = gnomeConfig {
             manageHook =  namedScratchpadManageHook scratchpads <+> manageDocks <+> myManageHook
           ,layoutHook = myLayout
           , startupHook = startupHook gnomeConfig >> setWMName "LG3D"
           , workspaces= myWorkSpaces
           , modMask = mod4Mask
           , focusFollowsMouse = False
           , terminal =  myTerminal
           } `additionalKeys` [
        -- scrot is a nicely configurable screen shot utility
        ((0, xK_Print), spawn "scrot -s -d 5 -q50 'shot-%Y%m%d-%H.%M.%S.png' -e 'mv $f ~/tmp/'")
           ,((modMask myConfig , xK_Print ), spawn "sleep 0.2;scrot -d5 -q50 -s 'shot-%Y%m%d-%H.%M.%S.png' -e 'eog $f'")
           ,((modMask myConfig, xK_b), bringMenu)
           ,((modMask myConfig, xK_g), goToSelected defaultGSConfig)
        
           ,((modMask myConfig.|. shiftMask, xK_s), SM.submap $ searchEngineMap S.selectSearch)
           ,((modMask myConfig, xK_s), SM.submap $ searchEngineMap $ S.promptSearch  defaultXPConfig)
        
           ,((modMask myConfig, xK_p), shellPrompt defaultXPConfig)

           , ((modMask myConfig , xK_F1), AL.launchApp defaultXPConfig "gnome-terminal -x info " )
--           ,((modMask myConfig , xK_F1), manPrompt defaultXPConfig)

        
           ,((modMask myConfig .|. shiftMask, xK_o), spawn "ooffice -calc")
           ,((mod3Mask, xK_space), spawn "gnome-do")
           ,((modMask myConfig .|. shiftMask, xK_g), spawn "gimp")
           ,((modMask myConfig .|. shiftMask, xK_n), spawn "pcmanfm")
           ,((modMask myConfig .|. shiftMask, xK_v), bindOn[("TV",spawn "iceweasel -P tv"),("",spawn "iceweasel -a icepop -P default")])
           ,((modMask myConfig .|. shiftMask, xK_i),  namedScratchpadAction scratchpads "iceweasel")
           ,((modMask myConfig .|. shiftMask, xK_u),  namedScratchpadAction scratchpads "unison")
           ,((modMask myConfig .|. shiftMask, xK_p),  namedScratchpadAction scratchpads "htop")
           ,((modMask myConfig .|. shiftMask, xK_m),  spawn "ssh hermes xterm -e htop")
           ,((modMask myConfig .|. shiftMask, xK_e),  namedScratchpadAction scratchpads "emacs")
           ,((modMask myConfig .|. shiftMask, xK_r),  namedScratchpadAction scratchpads "newsbeuter")
           ,((modMask myConfig .|. shiftMask, xK_s),  spawn "su-to-root -X -c /usr/sbin/synaptic")


        
           ,((modMask myConfig .|. controlMask, xK_x), spawn "gxmessage -wrap -bg black -fg green `xprop | grep WM_`") -- query X resources
        
           ,((modMask myConfig .|. shiftMask, xK_t), scratchpadSpawnActionCustom myTerminal)
        
     --     ,((modMask myConfig .|. shiftMask, xK_t), scratchpadSpawnActionCustom "gnome-terminal --disable-factory --name scratchpad")
        
           ,((modMask myConfig .|. shiftMask, xK_h), sshPrompt defaultXPConfig)
        
           -- workspace management
                                                                                            
           ,((modMask myConfig, xK_Down),  nextWS)
           ,((modMask myConfig, xK_j),  rotSlavesDown)
           ,((modMask myConfig, xK_k),  rotSlavesUp)
           ,((modMask myConfig, xK_u),  rotFocusedDown)
           ,((modMask myConfig, xK_i),  rotFocusedUp)
           ,((modMask myConfig, xK_Up),    prevWS)
           ,((modMask myConfig .|. controlMask, xK_Up),  shiftToNext)
           ,((modMask myConfig .|. controlMask, xK_Down),    shiftToPrev)
           ,((modMask myConfig, xK_Right), nextScreen)
           ,((modMask myConfig, xK_Left),  prevScreen)
           ,((modMask myConfig .|. controlMask, xK_Right), shiftNextScreen)
           ,((modMask myConfig .|. controlMask, xK_Left),  shiftPrevScreen)
           ,((modMask myConfig .|. controlMask, xK_z),     toggleWS)
           ,((modMask myConfig ,xK_x), sendMessage $ Toggle MIRROR)
           ,((modMask myConfig ,xK_f), sendMessage $ Toggle FULL)
           ,((modMask myConfig ,xK_y), sendMessage $ Toggle REFLECTX)
           ,((modMask myConfig ,xK_s), sendMessage $ Toggle SMARTBORDERS)
           ,((modMask myConfig ,xK_b), sendMessage $ Toggle NOBORDERS)
           -- ,((modMask myConfig, xK_x), sendMessage (Toggle "Circle"))
        
        
           -- session management
           ,((modMask myConfig .|. controlMask, xK_f), withFocused float)
           ,((modMask myConfig .|. controlMask, xK_Delete), spawn "xkill")
           ,((modMask myConfig .|. shiftMask,   xK_Delete), kill)
           ,((modMask myConfig .|. controlMask, xK_t), spawn "~/bin/myrootterm") -- root terminal
           ,((modMask myConfig .|. controlMask, xK_s), spawn "gksu poweroff")
           ,((modMask myConfig .|. controlMask, xK_r), spawn "gksu reboot")
           ,((modMask myConfig .|. controlMask, xK_l), spawn "gnome-screensaver-command --lock")
           ,((modMask myConfig .|. controlMask, xK_v), spawn "gnome-volume-control")
           ,((modMask myConfig .|. controlMask, xK_o), spawn "sleep 0.5; xset dpms force off")
        
           -- , ((modMask myConfig .|. shiftMask,                 xK_space), layoutScreens 2 (TwoPane 0.5 0.5))
           -- , ((modMask myConfig .|. controlMask .|. shiftMask, xK_space), rescreen)
        
        
        
           -- multi monitor stuff
           ,((modMask myConfig .|. controlMask, xK_1), spawn "xrandr -s 0")
           ,((modMask myConfig .|. controlMask, xK_2), spawn "xrandr -s 1")
           ,((modMask myConfig .|. controlMask, xK_3), spawn "xrandr -s 2")
        
           ] 

searchEngineMap method = M.fromList
                         [((0, xK_g), method S.google)
                         ,((0, xK_H), method S.hoogle)
                         ,((0, xK_w), method S.wikipedia)
                         ,((0, xK_i), method S.imdb)
                         ,((0, xK_a), method S.amazon)
                         ,((0, xK_m), method S.maps)
                         ,((0, xK_y), method S.youtube)
                         ,((0, xK_h), method hayoo)
                         ]
    where
      hayoo = S.searchEngine "Hayoo"  "http://holumbus.fh-wedel.de/hayoo/hayoo.html?query="

pangoColor :: String -> String -> String
pangoColor fg = wrap left right
 where
  left  = "<span foreground=\"" ++ fg ++ "\">"
  right = "</span>"

sanitize :: String -> String
sanitize [] = []
sanitize (x:rest) | fromEnum x > 127 = "&#" ++ show (fromEnum x) ++ "; " ++
                                       sanitize rest
                  | otherwise        = x : sanitize rest




My xmobar.conf file

Config {
       font     =  "xft:Times mono-10:size=10:antialias=true"
       , bgColor  = "#cc812a"
       , fgColor  = "darkblue"
       , position = Static { xpos = 800 , ypos = 977, width = 1280, height = 21 }
       , lowerOnStart = False
       , commands = [
        Run Network "eth0" [] 10
       , Run Cpu [] 10
       , Run Memory [] 10
       , Run Swap [] 10
       , Run Date "%a %b %_d %H:%M" "date" 10
       , Run StdinReader
       , Run CoreTemp [] 40
       , Run Com "mailcount" ["richardriley.net"] "mailcount" 600
       , Run Mail [("INBOX","/home/shamrock/hermes/Maildir/new"),("INBOX.rgr","/home/shamrock/hermes/Maildir/new")]
       ]
       , sepChar  = "%"
       , alignSep = "}{"
       , template = "<fc=#FFFFFF>%StdinReader%</fc>}{%coretemp%|%cpu%|%memory%|%eth0%|<fc=gold>%date%</fc>|<fc=green>(%mailcount%)</fc>"
       }

My mailcount script for xmobar IMAP email count

#!/bin/bash
# script for use with xmobar
# Richard Riley - rileyrgdev AT gmail DOT com
if [ "$1" = "" ]; then
   echo "needs a hostname. Exit."
   exit
fi
host=$1
newmails=$(ssh $host "ls ~/Maildir/new | wc -l")
if [ "$newmails" -eq "0" ]
then
        echo "0"
else
        echo "**<fc=white>Email:$newmails</fc>**"
fi

Date: 2009-12-08 17:33:39 CET

HTML generated by org-mode 6.33trans in emacs 23

Hosted On Debian LinuxServed Using Apache2