dzen-utils-0.1: Utilities for creating inputs for dzen.

Portabilitysemi-portable (uses MPTC and type families)
Stabilityexperimental
Maintainerfelipe.lessa@gmail.com

System.Dzen

Contents

Description

Hello!

This is dzen-utils' main module. It re-exports every other module from this library, so you may just say

 import System.Dzen

and you'll have everything in hand. To learn more about this library, please see the documentation of each module exported here. To get you started, there are some simple examples below. :)

Synopsis

Example 1

Suppose you have

 import Data.Time
 import System.Locale

 getZonedTime     :: IO ZonedTime -- from time package
 getCurrentWindow :: IO String    -- from somewhere you know :)

and that you want your bar to look like

 "Wndw: [my window title here] - Sun 15 Mar 07:10:02"

Okay, that's pretty nice. What you will do first will be to construct the Printer of your bar. Basically, what you want is to concatenate "Wndw: ", the result of getCurrentWindow, the separator, and the formated result of getZonedTime. The Printer we'll build below contain all the information necessary to create the final output string above from the two get functions!

Constructing - Part 1

To write the constant strings, we use str:

 str          :: String -> DString
 str "Wndw: " :: DString
 str " - "    :: DString

That is pretty straightforward. But how can we print the current window? We will just use cstr here

 cstr :: Printer String

While str is used as a common function, you just apply the string you want to it, cstr will get the string from another source. What we'll do shortly is to pipe IO String into Printer String to obtain our final output. Instead of Printer a, think of it as Printer input.

To concatenate we just have to use +++ from Combine, which is an ugly class used to create beautiful types:

 str "Wndn: " +++ cstr +++ str " - "  :: Printer String

Whenever you concatenate a DString with a Printer input, you get a Printer input as a result. That is, if you concatenate a constant string to something that takes an input and produces a string, what you get is something that takes an input and produces the concatenated string. Note that you don't need to write the type above.

Constructing - Part 2

Now we can't just go ahead and use cstr directly with getZonedTime because, unlike getCurrentWindow, its result is not a string, but a ZonedTime. To format the ZonedTime is outside the scope of this example, but you can get the format shown above with

 format :: ZonedTime -> String
 format = formatTime defaultTimeLocale "%a %e %b %H:%M:%S"

While you can write

 fmap format getZoneTime :: IO String

you don't need to push the format function into the IO monad, just put it in the Printer, the place where it belongs! To accomplish that use simple' instead of cstr:

 simple'        :: (input -> String) -> Printer input
 simple' format :: Printer ZonedTime

And it's done! Concatenating everything:

 printer :: Printer (String, ZonedTime)
 printer = str "Wndn: " +++ cstr +++ str " - " +++ simple' format

Whenever you concatenate Printer a with Printer b you get Printer (a,b).

Applying

In the end we just want strings, not Printers, so we need to apply our printer to some inputs. We already have functions for each part of our input, but we need to combine them. We may just use from System.Dzen.Process in this case (and in most others as well):

 supply :: IO (String, ZonedTime)
 supply = getCurrentWindow ## getZonedTime

Now we could use apply, applyMany or applyForever to get strings out of our printer, but we'll use runDzen directly which is a tad easier:

 myDzen :: IO ()
 myDzen = runDzen "dzen2" [] 500 printer supply

And that's it! You may use myDzen directly as main, or you may use forkIO myDzen inside xmonad. Pretty nice, uh?

Whole code

This is the whole code plus a stub getCurrentWindow and a main. We omit the signatures on purpose to show how you could have written it lazily. You may also get the file Example1.hs from the source code which contains all the type signatures.

import Data.Time
import System.Dzen
import System.Locale

printer = str "Wndn: " +++ cstr +++ str " - " +++ simple' format
format  = formatTime defaultTimeLocale "%a %e %b %H:%M:%S"
supply  = getCurrentWindow ## getZonedTime
myDzen  = runDzen "dzen2" [] 500 printer supply

getCurrentWindow = return "My =^.^= Lolcats"
main = myDzen

Example 2

Now we want to do something different: let's have a graphical bar for our clock! And, why not, a graphical separator.

Separator

The graphical separator will be very simple. Instead of dash (" - ") we'll use a small dot:

 import Data.Monoid

 sep :: DString
 sep = mconcat [pos 4, rect 3 3, pos 4]

This is a 3x3 rectangle with 4 pixels of spacing on each side. And, thats it!

Time bar

Now something more challenging. Instead of showing the minutes and the seconds, we want to show a graphical bar, something like

 "Wndw: [my window title here] - Sun 15 Mar 07:[==    ]"

Well, that bar will show minutes and seconds, but we can take as input just seconds, ranging from 0 to 60*60-1. We'll use plain simple cgdbar, which mimics gdbar: (please look at System.Dzen.Bars for more info)

 timeBar :: Printer Int
 timeBar = cgdbar False (40,10) Nothing Nothing True (0, 60*60-1)

But what we have is not the number of seconds, but a ZonedTime. So we need

 zonedSecs :: ZonedTime -> Int
 zonedSecs = extract . localTimeOfDay . zonedTimeToLocalTime
     where extract t = let minutes = fromIntegral (todMin t)
                       in round (minutes * 60 + todSec t)

As Printers are cofunctors, we can use comap to get a suitable timeBar:

 timeBar' :: Printer ZonedTime
 timeBar' = comap zonedSecs timeBar

Glueing the time text with the bar

We also want the rest of the time, not just the bar. First, we need a new format, as we don't want to show the minutes anymore:

 format :: ZonedTime -> String
 format = formatTime defaultTimeLocale "%a %e %b %H:"

You may be tempted to write simple' format +++ timeBar' now, but there's a small glitch: it would have type Printer (ZonedTime, ZonedTime), but we want both to use the same ZonedTime. We could use comap again or combine, but it is easier to write

 time :: Printer ZonedTime
 time = simple' format +=+ timeBar'

Whole code

The rest is just glue! So we now present the whole code, again in a compact form. See Example2.hs for the whole code with type signatures:

import Data.Monoid
import Data.Time
import System.Dzen
import System.Locale

zonedSecs = extract . localTimeOfDay . zonedTimeToLocalTime
    where extract t = let minutes = fromIntegral (todMin t)
                      in round (minutes * 60 + todSec t)

time = simple' format +=+ comap zonedSecs timeBar
  where format  = formatTime defaultTimeLocale "%a %e %b %H:"
        timeBar = cgdbar False (40,10) Nothing Nothing True (0, 60*60-1)

printer = str "Wndn: " +++ cstr +++ sep +++ time
    where sep = mconcat [str " ", rectO 5 5, str " "]

supply = getCurrentWindow ## getZonedTime
myDzen = runDzen "dzen2" [] 500 printer supply

getCurrentWindow = return "My =^.^= Lolcats"
main = myDzen

Module exports