cheezsteak
cheezsteak

Reputation: 2917

How do I run a custom built xmobar on multiple screens

I'm currently running a system installed xmobar on both of my monitors by using the -x parameter. My xmonad is custom built with stack and spawns two separate xmobar processes like so:

main :: IO ()
main = do
  bars <- traverse (spawnPipe . mappend "xmobar ~/.xmonad/resources/xmobar.hs -x ") ["0", "1"]
  xmonad . plugins $
    def
      { manageHook = myManageHook,
        layoutHook = myLayout,
        logHook = for_ bars myLogHook,
      }

I want to migrate to custom building my own xmobar instance but if I do, I can't use the -x <screen> feature unless I implement it myself. I figured, if I'm going to do this myself, why not have my custom xmobar automatically run on multiple screens? So I made this my App/xmobar/Main.hs

import Control.Concurrent (MVar, ThreadId, forkFinally, newEmptyMVar, newMVar, putMVar, takeMVar)
import Control.Concurrent.Async (forConcurrently_)
import XMonad.Theme.XMobar (configFromTheme)
import XMonad.Theme.XResources (loadXResources)
import Xmobar
import Xmobar.App.Opts

-- import Control.Concurrent (forkIO)

type ChildPool = MVar [MVar ()]

main :: IO ()
main = do
  children <- newMVar []
  config <- fmap configFromTheme <$> loadXResources
  let configs = maybe [] (flip fmap [0, 1] . setScreen) config
  forConcurrently_ configs $ forkChild children . xmobar
  waitForChildren children
  where
    setScreen config sid = config {position = OnScreen sid (position config)}

forkChild :: ChildPool -> IO () -> IO ThreadId
forkChild children io = do
  mvar <- newEmptyMVar
  childs <- takeMVar children
  putMVar children (mvar : childs)
  forkFinally io (const $ putMVar mvar ())

waitForChildren :: ChildPool -> IO ()
waitForChildren children = do
  cs <- takeMVar children
  case cs of
    [] -> return ()
    m : ms -> do
      putMVar children ms
      takeMVar m
      waitForChildren children

and updated app/xmonad/Main.hs to use bar <- spawnPipe "~/.xmonad/xmobar"

This sort of works except there's some bugs:

  1. The memory usage monitor is always Updating... (I think this is just a missing library).
  2. Clocks and system monitors plugins will only update on the first regularly. The second screens's bar will have frozen system monitors.
  3. The UnsafeStdinReader will only update for one bar at a time and will sometimes not update at all. I suspect this is from both bars fighting over stdin.

Is running multiple xmobars from one process possible and I'm just doing it wrong (if so how) or should is this entire attempt a dead end and I should implement a custom -x <SCREENID> argument and go back to what I was doing before?

Upvotes: 0

Views: 87

Answers (1)

cheezsteak
cheezsteak

Reputation: 2917

I don't have to implement the -x <screen> feature myself. I can just use the configFromArgs function exported by Xmobar.

main :: IO ()
main = do
  theme <- loadXResources
  let config = maybe defaultConfig configFromTheme theme
  xmobar =<< configFromArgs config

is all I need.

Upvotes: 0

Related Questions