XMonad Modules

This page is to serve as both an apology and an announcement. I've recently modularized my xmonad.hs. I'm sorry.

This is no longer true. I've since gone through a bit of a config cleanse, deciding it makes my life easier to live closer to defaults and not carry around a lot of extra configuration or features (that I don't actively use).

As part of this cleanse, I've stripped my config back down to a very lean xmonad.hs that can easily live within the confines of a single file.

Who cares?

I know of at least one person who stops by my site on a regular basis to update his xmonad.hs to match the latest version of mine. I've also seen, on a few occasions, someone mention that they use brisbin33's xmonad config when discussing an issue on the forums or in IRC. True, for all I know, there could be only three people using some form of my config -- but to them, I'm sorry.

Anyone who blindly updates to my most recent xmonad.hs may get hit with the following error:

      Could not find module `ScratchPadKeys':
         Use -v to see a list of the files searched for.

  Failed, modules loaded: none.

That's because I've offloaded some of the more module-ish chunks of my config into, well, modules.


I noticed, when browsing the XMonad source (I know, shut-up), that the default recompile command includes the option -ilib this tells ghc to include source files in ./lib. It was a light-bulb moment.

I had gathered some pretty sophisticated code in my little xmonad.hs: custom data types and instances, reusable utilities, etc. Why not put them in their own files and import them into a nice clean config as I would with any normal contrib module?

So, if you're following my xmonad.hs, please continue to do so. Just be advised you'll need a few files in lib if you want to use the functionality they offer.

published on 31 Aug 2010, tagged with dzen haskell website xmonad

Haskell RSS Reader

I've been looking for a good Haskell project for a while now. The language is just awesome, and I've been getting more and more comfortable with it lately thanks to reading Real World Haskell. I even got the opportunity to write some haskell for a project at work (I'm a consultant on a Microsoft product, crazy).

I wanted something challenging but doable; something to keep me interested but still stretch my abilities. I had made some smaller utilities to manage the pages on my site, so I was getting familiar with parsing XML using some haskell libraries as well as starting to wrap my head around the IO Monad a bit more. Well, I just completed (what I think is) a slick little RSS reader using just haskell and dzen.

For those that don't know, RSS feeds are basically just site headlines; a very simple XML page that lists items, each item containing a title, description, and link.

So my reader would read in a listing of feed urls, put together all of the RSS items from each url, and then display them using dzen.

I put it in the upper right of my left monitor, configured to look like part of my existing dzen status bars.

The title text remains static and is clickable (opens the url of the feed item), and the description text is a ticker text that rolls by right-to-left one character at a time.


First, you would have to download RssReader.hs and Dzen.hs from my old xmonad library and place them in a directory along side a file called rssreader.hs. This file would serve the same purpose xmonad.hs does for XMonad: it would be both a configuration file and the main application itself, gluing together imported functions into a runnable main.

Here's an example:

import Dzen
import RssReader

-- this is it, the whole application in one line!

main :: IO ()
main = spawnDzen dzenConf >>= spawnReader readerConf

-- and the configuration part...

-- set a width and some text formatting
readerConf :: ReaderConf
readerConf = defaultReaderConf
  { titleFormat = dzenFG "#909090"
  , descrFormat = shorten 200 
  , tickerWidth = 150 

    -- some helpers
    dzenFG c s  = concat ["^fg(", c, ")", s, "^fg()"]
    shorten n s = if length s > n then (take n s) ++ "..." else s

-- start with the default dzen and override some things
dzenConf :: DzenConf
dzenConf = defaultDzen
  { x_position  = Just $ Percent 60 -- start 60% across screen 0
  , width       = Just $ Percent 40 -- and span the other 40%
  , font        = Just "Verdana-8"  -- if you have an xft-capable dzen                                                        
  , fg_color    = Just "#606060"
  , bg_color    = Just "#303030"

Once that's all set, you can run ghc --make -o rssreader rssreader.hs inside this directory to create an executable which you can run standalone.


The following packages would be required either from Hackage or your distribution's package manager:

Hackage Arch linux
http extra/haskell-http
tagsoup aur/haskell-tagsoup

Known Issues

Some unprintable characters seem to still come through. I try to clean the strings as much as possible, but I still see boxes in dzen from time to time.

The rssreader and the spawned dzen are not tied together process-wise. This means that you can kill rssreader and a frozen dzen remains, or you can quit the dzen and rssreader will be left as a zombie.

published on 15 Aug 2010, tagged with dzen haskell xmonad

Status Bars in XMonad

One of the trickiest things for a new user of XMonad is adding a statusbar. This is mainly because xmonad's statusbar support is so transparent and extensible, that any documentation for setting it up could be completely different from any other. Do you want a dynamicLog? A customLog? xmobar? dzen? One bar? Two?

Here I'll outline my method. Two separate dzen2 bars aligned to look like one bar across the top of your screen. The left fed by an xmonad dynamicLogWithPP to show workspaces (with coloring and urgencyHooks), the current layout and the current window title. The right fed by conky to show music, system stats and of course the time.

Many thanks go to moljac and lifeafter2am on the Arch forums. They offered their xmonad.hs's to me and helped get me setup this way.

What it looks like

Full desktop:

XMonad Shot 

And with an urgencyHook notification (Workspace turns a different color):

XMonad Shot Urgent 

To achieve this, we set up a dynamicLog in xmonad.hs and adjust our main function to output to this bar and also spawn our right bar as fed by conky.

Imports and the Main function

Your imports and main function will look like this:

import XMonad.Util.Run
import XMonad.Hooks.DynamicLog
import XMonad.Hooks.ManageDocks
import XMonad.Hooks.UrgencyHook

main = do
    -- spawn our left and right bars. in my case, I use two monitors,
    -- I want one bar on each, and my version of dzen supports the -xs
    -- argument for specifying on which screen to appear. if your
    -- situation is different in some way, use -w and -x to give your
    -- bars appriate width and x offsets for your needs.
    d <- spawnPipe "dzen2 -p -xs 1 -ta l -e 'onstart=lower'"

    spawn $ "conky -c ~/.xmonad/data/conky/dzen | " ++
                "dzen2 -p -xs 2 ta -r -e 'onstart=lower'"

    xmonad $ withUrgencyHook NoUrgencyHook $ defaultConfig
        { ...
        , logHook = myLogHook d

        -- having these call out to external functions makes it easier 
        -- to add the "no overlap" stuff later on. if you don't have 
        -- myLayoutHook or myManageHook, you can continue to use the 
        -- xmonad defaults by declaring them like so:
        -- > myManageHook = manageHook defaultConfig
        -- > myLayoutHook = layoutHook defaultConfig
        , manageHook = myManageHook
        , layoutHook = myLayoutHook

Don't worry about the things we haven't defined yet, I'll get to those. Also, the conky config file which I use can be found in my xmonad repo.

Your LogHook

Your logHook will setup the output of workspaces, layouts, and titles to the left dzen. You can customize the formatting, padding, shortening, etc.

Here's a commented version of myLogHook which, hopefully, is illustrative enough to not warrant further explanation.

-- Loghook
-- note: some of these colors may differ from what's in the
-- screenshot, it changes daily
myLogHook h = dynamicLogWithPP $ defaultPP

    -- display current workspace as darkgrey on light grey (opposite of 
    -- default colors)
    { ppCurrent         = dzenColor "#303030" "#909090" . pad 

    -- display other workspaces which contain windows as a brighter grey
    , ppHidden          = dzenColor "#909090" "" . pad 

    -- display other workspaces with no windows as a normal grey
    , ppHiddenNoWindows = dzenColor "#606060" "" . pad 

    -- display the current layout as a brighter grey
    , ppLayout          = dzenColor "#909090" "" . pad 

    -- if a window on a hidden workspace needs my attention, color it so
    , ppUrgent          = dzenColor "#ff0000" "" . pad . dzenStrip

    -- shorten if it goes over 100 characters
    , ppTitle           = shorten 100  

    -- no separator between workspaces
    , ppWsSep           = ""

    -- put a few spaces between each object
    , ppSep             = "  "

    -- output to the handle we were given as an argument
    , ppOutput          = hPutStrLn h

No Overlap

The last thing you should do is add two little things to make sure you leave a gap for the new statusbar:

-- add avoidStruts to your layoutHook like so
myLayoutHook = avoidStruts $ {- whatever you had before... -}

-- add manageDocks to your managehook like so
myManageHook = manageDocks <+> {- whatever you had before ... -}

Happy haskelling!

published on 05 Dec 2009, tagged with dzen haskell xmonad