XMonad.Hooks.StatusBar

xmonad-contrib-0.18.1: Community-maintained extensions for xmonad

Copyright | (c) Yecine Megdiche <yecine.megdiche@gmail.com>
License | BSD3-style (see LICENSE)
Maintainer | Yecine Megdiche <yecine.megdiche@gmail.com>
Stability | unstable
Portability | unportable
Safe Haskell | None
Language | Haskell2010

XMonad.Hooks.StatusBar

Contents

- Usage
- Available Configs
- Multiple Status Bars
- Dynamic Status Bars
- Property Logging utilities
- Managing status bar Processes

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

- data StatusBarConfig = StatusBarConfig {
  - sbLogHook :: X ()
  - sbStartupHook :: X ()
  - sbCleanupHook :: X ()
  }
- withSB :: forall (l :: Type -> Type). LayoutClass l Window => StatusBarConfig -> XConfig l -> XConfig l
- withEasySB :: forall (l :: Type -> Type). LayoutClass l Window => StatusBarConfig -> (XConfig Layout -> (KeyMask, KeySym)) -> XConfig l -> XConfig (ModifiedLayout AvoidStruts l)
- defToggleStrutsKey :: forall (t :: Type -> Type). XConfig t -> (KeyMask, KeySym)
- statusBarProp :: String -> X PP -> StatusBarConfig
- statusBarPropTo :: String -> String -> X PP -> StatusBarConfig
- statusBarGeneric :: String -> X () -> StatusBarConfig
- statusBarPipe :: String -> X PP -> IO StatusBarConfig
- dynamicSBs :: forall (l :: Type -> Type). (ScreenId -> X StatusBarConfig) -> XConfig l -> XConfig l
- dynamicEasySBs :: forall (l :: Type -> Type). LayoutClass l Window => (ScreenId -> X StatusBarConfig) -> XConfig l -> XConfig (ModifiedLayout AvoidStruts l)
- xmonadPropLog :: String -> X ()
- xmonadPropLog' :: String -> String -> X ()
- xmonadDefProp :: String
- spawnStatusBar :: String -> X ()
- killStatusBar :: String -> X ()
- killAllStatusBars :: X ()
- startAllStatusBars :: X ()

# Usage

You can use this module with the following in your `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 #

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 sbLogHook :: X () What and how to log to the status bar. sbStartupHook :: X () How to start the status bar. sbCleanupHook :: X () How to kill the status bar.

#### Instances

Instances details

Default StatusBarConfig # | Per default, all the hooks do nothing.
Instance details Defined in XMonad.Hooks.StatusBar Methods def :: StatusBarConfig #
Monoid StatusBarConfig # |
Instance details Defined in XMonad.Hooks.StatusBar Methods mempty :: StatusBarConfig # mappend :: StatusBarConfig -> StatusBarConfig -> StatusBarConfig # mconcat :: [StatusBarConfig] -> StatusBarConfig #
Semigroup StatusBarConfig # |
Instance details Defined in XMonad.Hooks.StatusBar Methods (<>) :: StatusBarConfig -> StatusBarConfig -> StatusBarConfig # sconcat :: NonEmpty StatusBarConfig -> StatusBarConfig # stimes :: Integral b => b -> StatusBarConfig -> StatusBarConfig #

withSB #

Arguments

:: forall (l :: Type -> Type). 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 #

Arguments

:: forall (l :: Type -> Type). 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 :: forall (t :: Type -> Type). XConfig t -> (KeyMask, KeySym) #

Default `mod-b` key binding for `withEasySB`

# Available Configs

statusBarProp #

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 #

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 #

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 #

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 :: forall (l :: Type -> Type). (ScreenId -> X StatusBarConfig) -> XConfig l -> XConfig l #

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 :: forall (l :: Type -> Type). LayoutClass l Window => (ScreenId -> X StatusBarConfig) -> XConfig l -> XConfig (ModifiedLayout AvoidStruts l) #

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

# Property Logging utilities

xmonadPropLog :: String -> X () #

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

xmonadPropLog' #

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 #

The default property xmonad writes to. (`_XMONAD_LOG`).

# Managing status bar Processes

spawnStatusBar #

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 #

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 () #

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

startAllStatusBars :: X () #

Start all status bars. Note that you do not need this in your startup hook. This can be bound to a keybinding for example to be used in tandem with `killAllStatusBars`.