Search code examples
haskellfunctional-programmingconfigurationxmonadxmobar

How do I make xmonad use a haskell-based xmobar?


tl;dr

How do I change this xmonad.hs,

import XMonad
main :: IO ()
main = xmonad def { terminal = "urxvt" , modMask = mod4Mask }

in order for XMonad to spawn xmobar correctly (spawn once, and kill and re-spawn if I restart XMonad in place, and any other sensible requirement) without an xmobarrc config file but with an actual xmobar.hs Haskell file containing the following?

import Xmobar
main :: IO ()
main = xmobar defaultConfig { commands = [Run XMonadLog], template = "%XMonadLog%" }

More datails

From XMonad tutorial I read

It is also possible to completely configure xmobar in Haskell, just like xmonad. If you want to know more about that, you can check out the xmobar.hs example in the official documentation. For a more complicated example, you can also check out jao’s xmobar.hs (he’s the current maintainer of xmobar).

In the file at the first link, the instruction is

-- An example of a Haskell-based xmobar. Compile it with
--   ghc --make -- xmobar.hs
-- with the xmobar library installed or simply call:
--   xmobar /path/to/xmobar.hs
-- and xmobar will compile and launch it for you and

and I have verified that indeed executing ghc --make xmbobar.hs (GHC 9.4.8) generates an xmobar executable that, when executed, spawns the bar (xmobar /path/to/xmobar.hs instead just never returns).

But what is the proper way to tell XMonad to run xmobar?

From the first link in the quote above, I can get here, where I read

Or put your xmobar.hs program in ~/.config/xmobar/xmobar.hs and, when running the system-wide xmobar, it will notice that you have your own implementation and (re)compile and run it as needed.

but I still don't know how to have XMobar do the "running system-wide xmobar".


Some more finding

I think I've got to a minimal working example, but I don't really understand quite a few details.

Given this ~/.config/xmonad/xmonad.hs,

import XMonad
import XMonad.Hooks.EwmhDesktops
import XMonad.Hooks.StatusBar.PP
import XMonad.Util.Run

main :: IO ()
main = do
     xmproc <- spawnPipe "xmobar ~/.config/xmonad/xmobar.hs"
     xmonad . ewmhFullscreen
            . ewmh
            $ myConfig xmproc

myConfig h = def
    { terminal = "urxvt"
    , modMask = mod4Mask
    , logHook = dynamicLogWithPP xmobarPP
    { ppOutput = hPutStrLn h
    }
    }

and this ~/.config/xmonad/xmobar.hs,

import Xmobar

config :: Config
config = defaultConfig
    {
      commands =
        [ Run StdinReader,
          Run $ Date "%a %_d %b %Y <fc=#ee9a00>%H:%M:%S</fc>" "date" 10
        ],
      template = "%StdinReader% }{ %date%",
      alignSep = "}{"
    }

main :: IO ()
main = xmobar config

the two seem to work fine together.

However, below is what I don't understand.

  • The tutorial mentions a problem I don't really understand yet,

    It is thus much better to switch over to property based logging, where we are writing to an X11 property and having xmobar read that; no danger when things are not being read!

    but most importantly it concludes that

    For this reason we have to use XMonadLog instead of StdinReader in our xmobar.

    But as you can see, in the xmobar.hs above, I use StdinReader; if I plainly substitute that with XMonadLog without doing any other change anywhere (and I think I should instead do something in xmobar.hs to make the change work) then two things happen:

    • the selected work-space will not reflect anymore what workspace I'm in,
    • and restarting XMonad via Super+q results in the one more xmobar process to be spawned, without the previous one being killed (in other words, once more I hit Super+q, one more pid is shown by pidof xmobar).
  • In the xmonad.hs there a line that doesn't look right to me

    xmproc <- spawnPipe "xmobar ~/.config/xmonad/xmobar.hs"
    

    If I'm using XMobar as a library, then why am I running the xmobar executable (which is at /home/enrico/.cabal/bin/xmobar) passing to it the path to my xmobar.hs instead of just spawning the executable I have compiled?

    Well, going back to this, I think that probably makes sense:

    -- An example of a Haskell-based xmobar. Compile it with
    --   ghc --make -- xmobar.hs
    -- with the xmobar library installed or simply call:
    --   xmobar /path/to/xmobar.hs
    -- and xmobar will compile and launch it for you and
    
  • The bar doesn't impose an offset to the windows, so the windows partly cover the bar

    enter image description here

    and for the life of me, I can't really find how to prevent that; the same thing doesn't happen with the xmobarrc approach.


Solution

  • Well, my mistake consisted in think that moving from one approach (xmonad.hs + xmobarrc), call it A, to the other approach (xmonad.hs + xmobar.hs), call it B, I was also removing the usage of withEasySB,

         . withEasySB (statusBarProp (unwords ["xmobar", xmobarhs]) (pure myXmobarPP)) toggleStrutsKey
    

    to launch xmobar and just using spawnPipe (as shown in the question), thinking that with the approach B the configuration of xmobar was all up to xmobar.hs.

    Apparently, I've misunderstood the way XMonad and XMobar are supposed to work together:

    • XMobar can do and does some things on its own (e.g. showing the date, or other data that has nothing to do with window management),
    • whereas it has to receive from XMonad information about window management, and that's what %XMonadLog% is for.
    • Additionally, it's XMonad that has to deal with the fact that XMobar can or cannot be overlapped by other windows or not.