Search code examples
haskellxmonadxmobar

Clickable XMobar works... but crashes XMobar. What is the modern approach I should follow to have anything I want clickable in XMobar?


(I wrote "clickable XMobar" rather than "clickable workspaces" because eventually I want other plugins to be clickable too.)

tl;dr

Defining

foo = wrap "<action=`touch ~/foo` button=1>" "</action>"

or equivalently

foo = xmobarAction "touch ~/foo" "1"

and composing it with ppCurrent of xmobar's PP

myXmobarPP :: PP
myXmobarPP = def { ppCurrent = foo . ppCurrent def }

results in touch ~/foo being indeed executed when clicking on the current workspace's tag, but it also causes xmobar to be killed.

More datails

Gathering various information,

I gave a few tries, and these two below are alternative approaches that resulted in a partial success:

  • I changed XMonadLog to UnsafeXMonadLog in xmobar.hs, and passed my PP to clickablePP (from XMonad.Util.ClickableWorkspaces) in xmonad.hs (is this the intended modern approach?):
    main = xmonad
         . ewmhFullscreen
         . ewmh
         . withEasySB (statusBarProp (unwords ["xmobar", xmobarhs]) (clickablePP -- instead of return
                                                                     myXmobarPP)) toggleStrutsKey
         $ myConfig
    
  • Alternatively, I changed XMonadLog to UnsafeXMonadLog in xmobar.hs, and inside my PP I've composed ppHidden with a function to wrap in the supposedly appropriate action:
    clickable ws = "<action=xdotool key super+"++ws++">"++ws++"</action>"
    myXmobarPP :: PP
    myXmobarPP = def {
      ppHidden          = white . wrap " " "" . clickable,
      -- ...
    

In either case, clicking on a workspace does move to that workspace... but also kills the xmobar process.

I've also tried re-launching xmobar manually from the terminal to see if some error would help me understand

xmobar --verbose

but the error I see when it gets killed is just xmobar: waitForProcess: does not exist (No child processes)


Below are the minimal config files (plus non-minimal things you might want to edit to what you're used to, e.g. modMask):

  • xmonad.hs:
    import XMonad
    import XMonad.Hooks.EwmhDesktops
    import XMonad.Hooks.StatusBar
    import XMonad.Util.ClickableWorkspaces
    
    main :: IO ()
    main = xmonad
         . ewmhFullscreen
         . ewmh
         . withEasySB (statusBarProp myxmobar (clickablePP def)) defToggleStrutsKey
         $ myConfig
         where
           myxmobar = "xmobar ~/.config/xmobar/xmobar.hs"
    
    myConfig = def
        { terminal = "urxvt"
        , modMask = mod4Mask
        }
    
  • xmobar.hs:
    import Xmobar
    
    main :: IO ()
    main = xmobar defaultConfig {
             overrideRedirect = False
           , position = BottomSize L 100 25
           , commands = [ Run UnsafeXMonadLog ]
           , sepChar  = "%"
           , alignSep = "}{"
           , template = "%UnsafeXMonadLog% }{"
           }
    

Another attempt:

  • I've found that clickableWrap has type Int -> String -> String, so I tested it in GHCi and verified that clickableWrap 2 "ciao" gives back "<action=`xdotool set_desktop 2` button=1>ciao</action>",
  • I've verified that executing xdotool set_desktop 2 in a terminal sends be to workspace 3 (apparently xdotool has zero-based workspaces),
  • and I've edited set ppHidden (the PP I pass to statusBarProp) to \x -> clickableWrap ((read x :: Int) - 1) x.

The above still has the same effect that upon click on a hidden workspace brings me to it (good), but kills xmobar (bad).


Solution

  • Apparently there was a bug in XMobar, which was promptly fixed by the author.

    For details, see comments in my original bug report and the following.