Clickable DZen2 Panel for XMonad

September 1st, 2009 | Categories: Desktop, Linux | Tags: , , , ,

2009-09-02: Uploaded the config files package clickable-dzen2.tar.bz2, and the complete XMonad config is available on github.

Xmobar and dzen2 are two poplar status panel for XMonad, because they can be hooked into the logHook and display the status of XMonad. But they are not interactively friendly. Although I can use keyboard to perform all the tasks, in very rare cases, I still want to be able to use only one hand and the mouse.

Fortunately, latest version 1 of dzen2 supports mouse action on region. But XMonad does not support client command to invoke windows management command. Two tools wmctrl and xdotool can help to resolve this problem. The former can interact with any EWMH/NetWM compatible X Window Manager, and the latter can simulate keyboard input. So my solution is adding mouse action in dzen2, use wmctrl to switch desktop and xdotool to change layout.

Let XMonad use EWMH

XMonad.Hook.EwmhDesktops makes XMonad use the EWMH hints to tell panel applications about its workspaces and the windows therein. It is required by wmctrl, otherwise wmctrl cannot control windows and switch workspaces in XMonad.

Just follow the example in XMonad.Hooks.EwmhDesktops to enable EWMH:

import XMonad
import XMonad.Hooks.EwmhDesktops

main = do
  xmonad $ defaultConfig
       { layoutHook = ewmhDesktopsLayout $ layoutHook defaultConfig
       , logHook = ewmhDesktopsLogHook
       }

Format Dynamic Log

XMonad calls the logHook with every internal state update. The state then is sent to status panel. The function dynamicLogWithPP can simplify the output formatting. I use it to add ^ca for dzen2, which is used to add mouse action. Following code let me switch to a workspace by a left click on its name, and toggle layout by clicking the layout indicator.

myDzenPP h = defaultPP
             { ppOutput   = hPutStrLn h
             , ppCurrent  = dzenColor "#f8f8f8" "DodgerBlue4" . pad
             , ppVisible  = dzenColor "#f8f8f8" "LightSkyBlue4"
                            . pad . dzenSwitchWs
             , ppUrgent   = dzenColor "#f8f8f8" "red4" . pad . dzenSwitchWs . dzenStrip
             , ppHidden   = pad . dzenSwitchWs
             , ppLayout   = dzenColor "DarkOrange" "" . wrap "^ca(1,xdotool key Super_L+space)[" "]^ca()"
             , ppTitle    = dzenColor "#61ce3c" "" . dzenEscape
             , ppSep      = " "
             , ppWsSep    = "|"
            }

dzenSwitchWs :: String -> String
dzenSwitchWs s = "^ca(1,switch-workspace.pl " ++ (show s) ++ ")" ++ s ++ "^ca()"

Layout indicator is formatted using ppLayout, in which I add a mouse action. When it is clicked, xdotool sends a keyboard input Super_L+space, which is the default shortcut to cycle layouts.

The function dzenSwitchWs adds a mouse action around the workspace name. 1 indicates left button and switch-workspace.pl is a script switching to workspace by name.

Function dzenStrip is required, because the workspace name passed to ppUrgent has already formatted by ppVisible or ppHiden. The color markups, white space padding and mouse action must be stripped first.

dzenStrip :: String -> String
dzenStrip = strip [] where
    strip keep x
      | null x || " " == x         = keep
      | null keep && ' ' == head x = strip keep (tail x)
      | "^"  `isPrefixOf` x        = strip keep (drop 1 . dropWhile (/= ')') $ x)
      | otherwise                  = let (good,x') = span (/= '^') x
                                     in strip (keep ++ good) x'

Switch Workspace Script

The script should be executable and placed in a directory listed in PATH.

File: switch-workspace.pl

#!/usr/bin/env perl

use strict;

die unless @ARGV && -x "/usr/bin/wmctrl";

open DESKLIST, "/usr/bin/wmctrl -d |";

while (<DESKLIST>) {
    chomp;
    split ' ', $_, 9;

    next if $_[-1] ne $ARGV[0];

    system("/usr/bin/wmctrl -s $_[0]");
    last;
}

close DESKLIST;

The script uses wmctrl -d to list workspaces, and uses wmctrl -s switch to the matching workspace.

Complete xmonad.hs

File: xmonad.hs

import XMonad
import XMonad.Hooks.DynamicLog
import XMonad.Hooks.ManageDocks
import XMonad.Hooks.EwmhDesktops
import XMonad.Util.EZConfig(additionalKeys)
import XMonad.Util.Run(spawnPipe)
import System.IO
import Data.List(isPrefixOf)

dzenStrip :: String -> String
dzenStrip = strip [] where
    strip keep x
      | null x || " " == x         = keep
      | null keep && ' ' == head x = strip keep (tail x)
      | "^"  `isPrefixOf` x        = strip keep (drop 1 . dropWhile (/= ')') $ x)
      | otherwise                  = let (good,x') = span (/= '^') x
                                     in strip (keep ++ good) x'

dzenSwitchWs :: String -> String
dzenSwitchWs s = "^ca(1,switch-workspace.pl " ++ (show s) ++ ")" ++ s ++ "^ca()"

myDzenPP h = defaultPP
             { ppOutput   = hPutStrLn h
             , ppCurrent  = dzenColor "#f8f8f8" "DodgerBlue4" . pad
             , ppVisible  = dzenColor "#f8f8f8" "LightSkyBlue4"
                            . pad . dzenSwitchWs
             , ppUrgent   = dzenColor "#f8f8f8" "red4" . pad . dzenSwitchWs . dzenStrip
             , ppHidden   = pad . dzenSwitchWs
             , ppLayout   = dzenColor "DarkOrange" "" . wrap "^ca(1,xdotool key Super_L+space)[" "]^ca()"
             , ppTitle    = dzenColor "#61ce3c" "" . dzenEscape
             , ppSep      = " "
             , ppWsSep    = "|"
            }

main = do
  dzen <- spawnPipe "dzen2 -ta l -fg grey80 -bg grey20 -e 'button3='"
  xmonad $ defaultConfig
       { manageHook = manageDocks <+> manageHook defaultConfig
       , layoutHook = ewmhDesktopsLayout $ avoidStruts  $  layoutHook defaultConfig
       , logHook = ewmhDesktopsLogHook
                   >> (dynamicLogWithPP $ myDzenPP dzen)
       , modMask = mod4Mask
       }

Footnotes:

1 Tested in dzen2 0.8.5

Share or Save it:
  • Twitter
  • Facebook
  • FriendFeed
  • del.icio.us
  • Digg
  • StumbleUpon
  • Ping.fm
  • HelloTxt
  • RSS
  • email
  • Print

No related posts.

  1. transtone
    September 2nd, 2009 at 09:27
    Reply | Quote | #1

    能不能以打包的形式共享一下配置:)

  2. September 2nd, 2009 at 10:33
    Reply | Quote | #2
  3. Archer
    May 12th, 2010 at 09:32
    Reply | Quote | #3

    Good tutorial, but there is an easier way:

    http://www.arch-ed.dk/wiki/doku.php?id=clickable_workspaces

  4. May 12th, 2010 at 09:41
    Reply | Quote | #4

    @Archer
    Thanks for shareing.

    My solution uses wmctrl to switch workspace because I use XMonad.Actions.DynamicWorkspaces. It is used to create new workspace dynamically.

Leave a comment here:
Your email is never published nor shared.

Additional comments powered by BackType