xmonad-contrib-0.17.0: Community-maintained extensions extensions for xmonad
Copyright(c) Yecine Megdiche <yecine.megdiche@gmail.com>
LicenseBSD3-style (see LICENSE)
MaintainerYecine Megdiche <yecine.megdiche@gmail.com>
Stabilityunstable
Portabilityunportable
Safe HaskellNone
LanguageHaskell2010

XMonad.Hooks.StatusBar

Description

xmonad calls the logHook with every internal state update, which is useful for (among other things) outputting status information to an external status bar program such as xmobar or dzen.

This module provides a composable interface for (re)starting these status bars and logging to them, either using pipes or X properties. There's also XMonad.Hooks.StatusBar.PP which provides an abstraction and some utilities for customization what is logged to a status bar. Together, these are a modern replacement for XMonad.Hooks.DynamicLog, which is now just a compatibility wrapper.

Synopsis

Usage

You can use this module with the following in your ~/.xmonad/xmonad.hs:

import XMonad
import XMonad.Hooks.StatusBar
import XMonad.Hooks.StatusBar.PP

The easiest way to use this module with xmobar, as well as any other status bar that supports property logging, is to use statusBarProp with withEasySB; these take care of the necessary plumbing:

mySB = statusBarProp "xmobar" (pure xmobarPP)
main = xmonad $ withEasySB mySB defToggleStrutsKey def

You can read more about X11 properties here or here, although you don't have to understand them in order to use the functions mentioned above.

Most users will, however, want to customize the logging and integrate it into their existing custom xmonad configuration. The withSB function is more appropriate in this case: it doesn't touch your keybindings, layout modifiers, or event hooks; instead, you're expected to configure XMonad.Hooks.ManageDocks yourself. Here's what that might look like:

mySB = statusBarProp "xmobar" (pure myPP)
main = xmonad . withSB mySB . ewmh . docks $ def {...}

You then have to tell your status bar to read from the _XMONAD_LOG property of the root window. In the case of xmobar, this is achieved by simply using the XMonadLog plugin instead of StdinReader in your .xmobarrc:

Config { ...
       , commands = [ Run XMonadLog, ... ]
       , template = "%XMonadLog% }{ ..."
       }

If you don't have an .xmobarrc, create it; the XMonadLog plugin is not part of the default xmobar configuration and your status bar will not show workspace information otherwise!

With statusBarProp, you need to use property logging. Make sure the status bar you use supports reading a property string from the root window, or use some kind of wrapper that reads the property and pipes it into the bar (e.g. xmonadpropread | dzen2, see scripts/xmonadpropread.hs). The default property is _XMONAD_LOG, which is conveniently saved in xmonadDefProp. You can use another property by using the function statusBarPropTo.

If your status bar does not support property-based logging, you may also try statusBarPipe. It can be used in the same way as statusBarProp above (for xmobar, you now have to use the StdinReader plugin in your .xmobarrc). Instead of writing to a property, this function opens a pipe and makes the given status bar read from that pipe. Please be aware that this kind of setup is very bug-prone and hence is discouraged: if anything goes wrong with the bar, xmonad will freeze!

Also note that statusBarPipe returns 'IO StatusBarConfig', so you need to evaluate it before passing it to withSB or withEasySB:

main = do
  mySB <- statusBarPipe "xmobar" (pure myPP)
  xmonad $ withSB mySB myConf

data StatusBarConfig Source #

This datataype abstracts a status bar to provide a common interface functions like statusBarPipe or statusBarProp. Once defined, a status bar can be incorporated in XConfig by using withSB or withEasySB, which take care of the necessary plumbing.

Constructors

StatusBarConfig 

Fields

withSB Source #

Arguments

:: LayoutClass l Window 
=> StatusBarConfig

The status bar config

-> XConfig l

The base config

-> XConfig l 

Incorporates a StatusBarConfig into an XConfig by taking care of the necessary plumbing (starting, restarting and logging to it).

Using this function multiple times to combine status bars may result in only one status bar working properly. See the section on using multiple status bars for more details.

withEasySB Source #

Arguments

:: LayoutClass l Window 
=> StatusBarConfig

The status bar config

-> (XConfig Layout -> (KeyMask, KeySym))

The key binding

-> XConfig l

The base config

-> XConfig (ModifiedLayout AvoidStruts l) 

Like withSB, but takes an extra key to toggle struts. It also applies the avoidStruts layout modifier and the docks combinator.

Using this function multiple times to combine status bars may result in only one status bar working properly. See the section on using multiple status bars for more details.

defToggleStrutsKey :: XConfig t -> (KeyMask, KeySym) Source #

Default mod-b key binding for withEasySB

Available Configs

statusBarProp Source #

Arguments

:: String

The command line to launch the status bar

-> X PP

The pretty printing options

-> StatusBarConfig 

Creates a StatusBarConfig that uses property logging to _XMONAD_LOG, which is set in xmonadDefProp

statusBarPropTo Source #

Arguments

:: String

Property to write the string to

-> String

The command line to launch the status bar

-> X PP

The pretty printing options

-> StatusBarConfig 

Like statusBarProp, but lets you define the property

statusBarGeneric Source #

Arguments

:: String

The command line to launch the status bar

-> X ()

What and how to log to the status bar (sbLogHook)

-> StatusBarConfig 

A generic StatusBarConfig that launches a status bar but takes a generic X () logging function instead of a PP. This has several uses:

  • With xmonadPropLog or xmonadPropLog' in the logging function, a custom non-PP-based logger can be used for logging into an xmobar.
  • With mempty as the logging function, it's possible to manage a status bar that reads information from EWMH properties like taffybar.
  • With mempty as the logging function, any other dock like trayer or stalonetray can be managed by this module.

statusBarPipe Source #

Arguments

:: String

The command line to launch the status bar

-> X PP

The pretty printing options

-> IO StatusBarConfig 

Like statusBarProp, but uses pipe-based logging instead.

Multiple Status Bars

StatusBarConfig is a Monoid, which means that multiple status bars can be combined together using <> or mconcat and passed to withSB.

Here's an example of what such declarative configuration of multiple status bars may look like:

-- Make sure to setup the xmobar configs accordingly
xmobarTop    = statusBarPropTo "_XMONAD_LOG_1" "xmobar -x 0 ~/.config/xmobar/xmobarrc_top"    (pure ppTop)
xmobarBottom = statusBarPropTo "_XMONAD_LOG_2" "xmobar -x 0 ~/.config/xmobar/xmobarrc_bottom" (pure ppBottom)
xmobar1      = statusBarPropTo "_XMONAD_LOG_3" "xmobar -x 1 ~/.config/xmobar/xmobarrc1"       (pure pp1)

main = xmonad $ withSB (xmobarTop <> xmobarBottom <> xmobar1) myConfig

And here is an example of the related xmobar configuration for the multiple status bars mentioned above:

xmobarrc_top
Config { ...
       , commands = [ Run XPropertyLog "_XMONAD_LOG_1", ... ]
       , template = "%_XMONAD_LOG_1% }{ ..."
       }

The above example also works if the different status bars support different logging methods: you could mix property logging and logging via pipes. One thing to keep in mind is that if multiple bars read from the same property, their content will be the same. If you want to use property-based logging with multiple bars, they should read from different properties.

XMonad.Util.Loggers includes loggers that can be bound to specific screens, like logCurrentOnScreen, that might be useful with multiple screens.

Long-time xmonad users will note that the above config is equivalent to the following less robust and more verbose configuration that they might find in their old configs:

main = do
  -- do not use this, this is an example of a deprecated config
  xmproc0 <- spawnPipe "xmobar -x 0 ~/.config/xmobar/xmobarrc_top"
  xmproc1 <- spawnPipe "xmobar -x 0 ~/.config/xmobar/xmobarrc_bottom"
  xmproc2 <- spawnPipe "xmobar -x 1 ~/.config/xmobar/xmobarrc1"
  xmonad $ def {
    ...
    , logHook = dynamicLogWithPP ppTop { ppOutput = hPutStrLn xmproc0 }
             >> dynamicLogWithPP ppBottom { ppOutput = hPutStrLn xmproc1 }
             >> dynamicLogWithPP pp1 { ppOutput = hPutStrLn xmproc2 }
    ...
  }

By using the new interface, the config becomes more declarative and there's less room for errors.

The only *problem* now is that the status bars will not be updated when your screen configuration changes (by plugging in a monitor, for example). Check the section on dynamic status bars for how to do that.

Dynamic Status Bars

Using multiple status bars by just combining them with <> works well as long as the screen configuration does not change often. If it does, you should use dynamicSBs: by providing a function that creates status bars, it takes care of setting up the event hook, the log hook and the startup hook necessary to make the status bars, well, dynamic.

xmobarTop    = statusBarPropTo "_XMONAD_LOG_1" "xmobar -x 0 ~/.config/xmobar/xmobarrc_top"    (pure ppTop)
xmobarBottom = statusBarPropTo "_XMONAD_LOG_2" "xmobar -x 0 ~/.config/xmobar/xmobarrc_bottom" (pure ppBottom)
xmobar1      = statusBarPropTo "_XMONAD_LOG_3" "xmobar -x 1 ~/.config/xmobar/xmobarrc1"       (pure pp1)

barSpawner :: ScreenId -> IO StatusBarConfig
barSpawner 0 = pure $ xmobarTop <> xmobarBottom -- two bars on the main screen
barSpawner 1 = pure $ xmobar1
barSpawner _ = mempty -- nothing on the rest of the screens

main = xmonad $ dynamicSBs barSpawner (def { ... })

Make sure you specify which screen to place the status bar on (in xmobar, this is achieved by the -x argument). In addition to making sure that your status bar lands where you intended it to land, the commands are used internally to keep track of the status bars.

Note also that this interface can be used with one screen, or if the screen configuration doesn't change.

dynamicSBs :: (ScreenId -> IO StatusBarConfig) -> XConfig l -> XConfig l Source #

Given a function to create status bars, dynamicSBs adds the dynamic status bar capabilities to the config. For a version of this function that applies docks and avoidStruts, check dynamicEasySBs.

Heavily inspired by XMonad.Hooks.DynamicBars

dynamicEasySBs :: LayoutClass l Window => (ScreenId -> IO StatusBarConfig) -> XConfig l -> XConfig (ModifiedLayout AvoidStruts l) Source #

Like dynamicSBs, but applies docks to the resulting config and adds avoidStruts to the layout.

Property Logging utilities

xmonadPropLog :: String -> X () Source #

Write a string to the _XMONAD_LOG property on the root window.

xmonadPropLog' Source #

Arguments

:: String

Property name

-> String

Message to be written to the property

-> X () 

Write a string to a property on the root window. This property is of type UTF8_STRING.

xmonadDefProp :: String Source #

The default property xmonad writes to. (_XMONAD_LOG).

Managing status bar Processes

spawnStatusBar Source #

Arguments

:: String

The command used to spawn the status bar

-> X () 

Spawns a status bar and saves its PID together with the commands that was used to start it. This is useful when the status bars should be restarted with xmonad. Use this in combination with killStatusBar.

Note: in some systems, multiple processes might start, even though one command is provided. This means the first PID, of the group leader, is saved.

killStatusBar Source #

Arguments

:: String

The command used to start the status bar

-> X () 

Kills the status bar started with spawnStatusBar using the given command and resets the state. This could go for example at the beginning of the startupHook, to kill the status bars that need to be restarted.

Concretely, this function sends a sigTERM to the saved PIDs using signalProcessGroup to effectively terminate all processes, regardless of how many were started by using spawnStatusBar.

There is one caveat to keep in mind: to keep the implementation simple; no checks are executed before terminating the processes. This means: if the started process dies for some reason, and enough time passes for the PIDs to wrap around, this function might terminate another process that happens to have the same PID. However, this isn't a typical usage scenario.

killAllStatusBars :: X () Source #

Kill all status bars started with spawnStatusBar. Note the caveats in cleanupStatusBar