From 5eb3dbd61b3f077485dd50c22f4f60573285994c Mon Sep 17 00:00:00 2001
From: Yecine Megdiche <yecine.megdiche@gmail.com>
Date: Sun, 21 Mar 2021 18:07:39 +0100
Subject: [PATCH 01/11] Split XMonad.Hooks.DynamicLog

- XMonad.Hooks.DynamicLog.PP: the pretty-printing abstracion, with the
corresponding utilities
- XMonad.Hooks.StatusBar: A new module to provide a better interface to
manage external status bars.
---
 CHANGES.md                   |  31 +-
 XMonad/Doc/Extending.hs      |  11 +-
 XMonad/Hooks/DynamicLog.hs   | 942 +++--------------------------------
 XMonad/Hooks/StatusBar.hs    | 420 ++++++++++++++++
 XMonad/Hooks/StatusBar/PP.hs | 441 ++++++++++++++++
 xmonad-contrib.cabal         |   2 +
 6 files changed, 949 insertions(+), 898 deletions(-)
 create mode 100644 XMonad/Hooks/StatusBar.hs
 create mode 100644 XMonad/Hooks/StatusBar/PP.hs

diff --git a/CHANGES.md b/CHANGES.md
index 9b89660c..9e6cc7b1 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -72,6 +72,17 @@
 
 ### New Modules
 
+  * `XMonad.Hooks.StatusBar.PP`
+
+    Originally contained inside `XMonad.Hooks.DynamicLog`, this module provides the
+    pretty-printing abstraction and utilities, used primarly with `logHook`.
+
+  * `XMonad.Hooks.StatusBar`
+
+    This module provides a new interface that replaces "XMonad.Hooks.DynamicLog",
+    by providing composoble status bars. Supports property-based as well
+    as pipe-based status bars.
+
   * `XMonad.Util.Hacks`
 
     Serves as a collection of hacks and fixes that should be easily acessible to users.
@@ -229,10 +240,6 @@
 
   * `XMonad.Hooks.DynamicLog`
 
-    - Added `statusBar'` function, like existing `statusBar` but accepts a pretty
-      printing options argument embedded in the X monad, to allow for dynamically
-      modified options such as `workspaceNamesPP`.
-
     - Added `shortenLeft` function, like existing `shorten` but shortens by
       truncating from left instead of right. Useful for showing directories.
 
@@ -242,16 +249,12 @@
     - Added `filterOutWsPP` for filtering out certain workspaces from being
       displayed.
 
-    - Added `xmobarProp`, `statusBarProp`, and `statusBarPropTo` for
-      property-based alternatives to `xmobar` and `statusBar` respectively.
+    - Added `xmobarProp`, for property-based alternative to `xmobar`.
 
     - Reworked the module documentation to suggest property-based logging
       instead of pipe-based logging, due to the various issues associated with
       the latter.
 
-    - Added `spawnStatusBarAndRemember` and `cleanupStatusBars` to provide
-      a way to safely restart status bars without relying on pipes.
-
     - Added `ppTitleUnfocused` to `PP` for showing unfocused windows on
       the current workspace in the status bar.
 
@@ -259,16 +262,6 @@
 
     - Add the -dock argument to the dzen spawn arguments
 
-    - Added `StatusBarConfig` and `makeStatusBar` and `makeStatusBar'` as
-      an abstraction for status bars; together with `statusBarPropConfig`,
-      `statusBarPropToConfig`, `statusBarHandleConfig` and `statusBarHandleConfig'`
-      to provide the configs for the already existing functionality. This provides
-      multiple status bars support.
-
-    - Added `ppRename` to `PP`, which makes it possible for extensions like
-      `workspaceNamesPP`, `marshallPP` and/or `clickablePP` to compose
-      intuitively.
-
   * `XMonad.Layout.BoringWindows`
 
     - Added boring-aware `swapUp`, `swapDown`, `siftUp`, and `siftDown` functions.
diff --git a/XMonad/Doc/Extending.hs b/XMonad/Doc/Extending.hs
index c52099db..2a756e4d 100644
--- a/XMonad/Doc/Extending.hs
+++ b/XMonad/Doc/Extending.hs
@@ -484,6 +484,10 @@ Here is a list of the modules found in @XMonad.Hooks@:
   putting in a status bar of some sort. See
   "XMonad.Doc.Extending#The_log_hook_and_external_status_bars".
 
+* "XMonad.Hooks.StatusBar.PP": originally in "XMonad.Hooks.DynamicLog",
+   this module provides the pretty-printing abstraction 'PP' and a set of
+   functions to interact with it.
+
 * "XMonad.Hooks.EwmhDesktops":
     Makes xmonad use the EWMH hints to tell panel applications about its
     workspaces and the windows therein. It also allows the user to interact
@@ -559,6 +563,10 @@ Here is a list of the modules found in @XMonad.Hooks@:
     _NET_SUPPORTING_WM_CHECK protocol.  May be useful for making Java GUI
     programs work.
 
+* "XMonad.Hooks.StatusBar":
+   This module provides a new interface that replaces "XMonad.Hooks.DynamicLog",
+   by providing composoble and dynamic status bars.
+
 * "XMonad.Hooks.ToggleHook":
     Hook and keybindings for toggling hook behavior.
 
@@ -1735,7 +1743,8 @@ specifically for logging some of the most interesting information
 about the internal state of xmonad: "XMonad.Hooks.DynamicLog".  This
 module can be used with an external status bar to print the produced
 logs in a convenient way; the most commonly used status bars are dzen
-and xmobar.
+and xmobar. The module "XMonad.Hooks.StatusBar" offers another interface
+to interact with status bars, that might be more convenient to use.
 
 By default the 'XMonad.Core.logHook' doesn't produce anything. To
 enable it you need first to import "XMonad.Hooks.DynamicLog":
diff --git a/XMonad/Hooks/DynamicLog.hs b/XMonad/Hooks/DynamicLog.hs
index 20e6bd20..f61ec0ae 100644
--- a/XMonad/Hooks/DynamicLog.hs
+++ b/XMonad/Hooks/DynamicLog.hs
@@ -1,5 +1,4 @@
-{-# LANGUAGE FlexibleContexts, PatternGuards, TypeApplications #-}
-{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE FlexibleContexts #-}
 
 -----------------------------------------------------------------------------
 -- |
@@ -26,10 +25,7 @@ module XMonad.Hooks.DynamicLog (
     -- * Drop-in loggers
     xmobarProp,
     xmobar,
-    statusBarProp,
-    statusBarPropTo,
     statusBar,
-    statusBar',
     dzen,
     dzenWithFlags,
     dynamicLog,
@@ -38,6 +34,7 @@ module XMonad.Hooks.DynamicLog (
     xmonadPropLog,
     xmonadPropLog',
     xmonadDefProp,
+
     -- * Build your own formatter
     dynamicLogWithPP,
     dynamicLogString,
@@ -56,226 +53,82 @@ module XMonad.Hooks.DynamicLog (
     pprWindowSet,
     pprWindowSetXinerama,
 
-    -- * Specialized spawning and cleaning
-    spawnStatusBarAndRemember,
-    cleanupStatusBars,
-
-    -- * Further customization
-    -- $furthercustomization
-    statusBarPropConfig,
-    statusBarPropToConfig,
-    statusBarHandleConfig,
-    statusBarHandleConfig',
-    makeStatusBar,
-    makeStatusBar',
-    StatusBarConfig(..),
-
-    -- * Multiple loggers
-    -- $multiple
-
-    -- * To Do
-    -- $todo
-
   ) where
 
 -- Useful imports
 
-import qualified Codec.Binary.UTF8.String as UTF8 (encode)
-import Control.Applicative (liftA2)
-import Control.Exception (SomeException, try)
-import Control.Monad (msum, void)
-import Data.Bool (bool)
-import Data.Char (isSpace)
-import Data.List (intercalate, isPrefixOf, sortOn, stripPrefix)
-import Data.Maybe (catMaybes, fromMaybe, isJust, mapMaybe)
-import System.Posix.Signals (sigTERM, signalProcessGroup)
-import System.Posix.Types (ProcessID)
-
-import qualified Data.Map        as M
-import qualified XMonad.StackSet as S
-
-import Foreign.C (CChar)
-
 import XMonad
 
-import XMonad.Util.NamedWindows
-import XMonad.Util.Run
-import XMonad.Util.WorkspaceCompare
-import qualified XMonad.Util.ExtensibleState as XS
-
 import XMonad.Layout.LayoutModifier
-import XMonad.Hooks.UrgencyHook
 import XMonad.Hooks.ManageDocks
+import XMonad.Hooks.StatusBar.PP
+import XMonad.Hooks.StatusBar
 
 -- $usage
+-- The API for this module is frozen, use "XMonad.Hooks.StatusBar" instead.
+--
 -- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
 --
 -- >    import XMonad
 -- >    import XMonad.Hooks.DynamicLog
 --
--- The recommended way to use this module with xmobar, as well as any other
--- status bar that supports property logging (you can read more about X11
--- properties
--- [here](https://en.wikipedia.org/wiki/X_Window_System_core_protocol#Properties)
--- or
--- [here](https://tronche.com/gui/x/xlib/window-information/properties-and-atoms.html),
--- although you don't have to understand them in order to use the functions
--- below), is to use one of the following two functions:
+-- If you just want a quick-and-dirty status bar with zero effort, try
+-- the 'xmobar' or 'dzen' functions:
 --
---   * 'xmobarProp' if you want to use the predefined pretty-printing function
---     and toggle struts key (@mod-b@).
---
---   * 'statusBarProp' if you want to define these things yourself.
---
--- These functions are preferred over the other options listed below, as they
--- take care of all the necessary plumbing—no shell scripting required!
---
--- For example, to use the 'xmobarProp' function you could do
---
--- > main = xmonad =<< xmobarProp myConfig
+-- > main = xmonad =<< xmobar myConfig
 -- >
 -- > myConfig = def { ... }
 --
--- With 'statusBarProp', this would look something like the following
+-- There is also 'statusBar' if you'd like to use another status bar, or would
+-- like to use different formatting options.  The 'xmobar', 'dzen', and
+-- 'statusBar' functions are preferred over the other options listed below, as
+-- they take care of all the necessary plumbing -- no shell scripting required!
 --
--- > main = xmonad =<< statusBarProp "xmobar" myXmobarPP myToggleStrutsKey myConfig
--- >
--- > myXmobarPP = def { ... }
--- > toggleStrutsKey XConfig{ modMask = modm } = (modm, xK_b)
--- > myConfig = 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 simply achieved by 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
--- otherwise!
---
--- Because 'statusBarProp' lets you define your own executable, you can also
--- give it a different status bar entirely; you only need to make sure that the
--- status bar supports reading a property string from the root window, or use
--- a wrapper that reads the property and pipes it into the bar (e.g.
--- @xmonadpropread | dzen2@, see @scripts/xmonadpropread.hs@).
---
--- If you don't want to use the default property, you can specify your own
--- with the 'statusBarPropTo' function.
---
--- If your status bar does not support property-based logging, you may also try
--- 'statusBar' and 'statusBar'', as well as the `xmobar` function, instead.
--- They can be used in the same way as the 'statusBarProp' and 'xmobarProp'
--- functions above (for xmobar, you will now use the @StdinReader@
--- plugin in your @.xmobarrc@).  Instead of writing to a property, these
--- functions open a pipe and make 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.
---
--- If you do not want to use any of the "batteries included" functions above,
--- you can also add all of the necessary plumbing yourself (the definition of
--- 'statusBarProp' may still come in handy here).
---
--- 'xmonadPropLog' allows you to write a string to the @_XMONAD_LOG@ property of
--- the root window.  Together with 'dynamicLogString', you can now simply set
--- your logHook to the appropriate function, for instance
+-- Alternatively, you can choose among several default status bar formats
+-- ('dynamicLog' or 'dynamicLogXinerama') by simply setting your logHook to the
+-- appropriate function, for instance:
 --
 -- > main = xmonad $ def {
 -- >    ...
--- >    , logHook = xmonadPropLog =<< dynamicLogString myPP
+-- >    logHook = dynamicLog
 -- >    ...
--- >    }
+-- >  }
 --
--- If you want to define your own property name, use 'xmonadPropLog'' instead of
--- 'xmonadPropLog'.
---
--- If you just want to use the default pretty-printing format, you can replace
--- @myPP@ with @def@ in the above logHook.
---
--- Note that setting the @logHook@ only sets up xmonad's output; you are
--- responsible for starting your own status bar program and making sure it reads
--- from the property that xmonad writes to.  To start your bar, simply put it
--- into your 'startupHook'.  You will also have also have to add 'docks' and
--- 'avoidStruts' to your config.  Putting all of this together would look
--- something like
---
--- > import XMonad.Util.SpawnOnce (spawnOnce)
--- > import XMonad.Hooks.ManageDocks (avoidStruts, docks)
--- >
--- > main = do
--- >     xmonad $ docks $ def {
--- >       ...
--- >       , logHook     = xmonadPropLog =<< dynamicLogString myPP
--- >       , startupHook = spawnOnce "xmobar"
--- >       , layoutHook  = avoidStruts myLayout
--- >       ...
--- >       }
--- > myPP = def { ... }
--- > myLayout = ...
---
--- If you want a keybinding to toggle your bar, you will also need to add this
--- to the rest of your keybindings.
---
--- The above has the problem that xmobar will not get restarted whenever you
--- restart xmonad ('XMonad.Util.SpawnOnce.spawnOnce' will simply prevent your
--- chosen status bar from spawning again). Using 'statusBarProp', however, takes
--- care of the necessary plumbing /and/ keeps track of the started status bars, so
--- they can be correctly restarted with xmonad. This is achieved using
--- 'spawnStatusBarAndRemember' to start them and 'cleanupStatusBars' to kill
--- previously started ones.
---
--- Even if you don't use a statusbar, you can still use 'dynamicLogString' to
--- show on-screen notifications in response to some events. For example, to show
--- the current layout when it changes, you could make a keybinding to cycle the
--- layout and display the current status:
---
--- > ((mod1Mask, xK_a), sendMessage NextLayout >> (dynamicLogString myPP >>= \d->spawn $"xmessage "++d))
---
--- If you use a status bar that does not support reading from a property log
--- (like dzen), and you don't want to use the 'statusBar' or 'statusBar''
--- functions, you can, again, also manually add all of the required components.
---
--- This works much like the property based solution above, just that you will
--- want to use 'dynamicLog' or 'dynamicLogXinerama' in place of 'xmonadPropLog'.
---
--- > main = xmonad $ def {
--- >    ...
--- >    , logHook = dynamicLog
--- >    ...
--- >    }
---
--- For more flexibility, you can also use 'dynamicLogWithPP' and supply your own
--- pretty-printing format (by either defining one from scratch, or customizing
--- one of the provided examples).  For example:
+-- For more flexibility, you can also use 'dynamicLogWithPP' and supply
+-- your own pretty-printing format (by either defining one from scratch,
+-- or customizing one of the provided examples).
+-- For example:
 --
 -- >    -- use sjanssen's pretty-printer format, but with the sections
 -- >    -- in reverse
 -- >    logHook = dynamicLogWithPP $ sjanssenPP { ppOrder = reverse }
 --
--- Again, you will have to do all the necessary plumbing yourself.  In addition,
--- you are also responsible for creating a pipe for you status bar to read from:
+-- Note that setting the @logHook@ only sets up xmonad's output; you
+-- are responsible for starting your own status bar program (e.g. dzen
+-- or xmobar) and making sure xmonad's output is piped into it
+-- appropriately, either by putting it in your @.xsession@ or similar
+-- file, or by using @spawnPipe@ in your @main@ function, for example:
 --
--- > import XMonad.Util.Run (hPutStrLn, spawnPipe)
+-- > import XMonad.Util.Run   -- for spawnPipe and hPutStrLn
 -- >
 -- > main = do
--- >     h <- spawnPipe "dzen2 -options -foo -bar"
+-- >     h <- spawnPipe "xmobar -options -foo -bar"
 -- >     xmonad $ def {
 -- >       ...
--- >       , logHook = dynamicLogWithPP $ def { ppOutput = hPutStrLn h }
--- >       ...
--- >       }
+-- >       logHook = dynamicLogWithPP $ def { ppOutput = hPutStrLn h }
 --
--- In the above, note that if you use @spawnPipe@ you need to redefine the
--- 'ppOutput' field of your pretty-printer, as was done in the example above; by
--- default the status will be printed to stdout rather than the pipe you create.
+-- If you use @spawnPipe@, be sure to redefine the 'ppOutput' field of
+-- your pretty-printer as in the example above; by default the status
+-- will be printed to stdout rather than the pipe you create.
 --
-
--- $todo
+-- Even if you don't use a statusbar, you can still use
+-- 'dynamicLogString' to show on-screen notifications in response to
+-- some events. For example, to show the current layout when it
+-- changes, you could make a keybinding to cycle the layout and
+-- display the current status:
+--
+-- >    , ((mod1Mask, xK_a     ), sendMessage NextLayout >> (dynamicLogString myPP >>= \d->spawn $"xmessage "++d))
 --
---   * incorporate dynamicLogXinerama into the PP framework somehow
 
 ------------------------------------------------------------------------
 
@@ -318,30 +171,6 @@ dzen = dzenWithFlags flags
     bg      = "'#3f3c6d'"
     flags   = "-e 'onstart=lower' -dock -w 400 -ta l -fg " ++ fg ++ " -bg " ++ bg
 
--- | Run xmonad with a property-based xmobar status bar set to some nice
--- defaults.
---
--- > main = xmonad =<< xmobarProp myConfig
--- >
--- > myConfig = def { ... }
---
--- The intent is that the above config file should provide a nice
--- status bar with minimal effort. Note that you still need to configure
--- xmobar to use the @XMonadLog@ plugin instead of the default @StdinReader@,
--- see above.
---
--- If you wish to customize the status bar format at all, you'll have to
--- use the 'statusBarProp' function instead.
---
--- The binding uses the "XMonad.Hooks.ManageDocks" module to automatically
--- handle screen placement for xmobar, and enables 'mod-b' for toggling
--- the menu bar.
---
-xmobarProp :: LayoutClass l Window
-           => XConfig l  -- ^ The base config
-           -> IO (XConfig (ModifiedLayout AvoidStruts l))
-xmobarProp = statusBarProp "xmobar" xmobarPP toggleStrutsKey
-
 -- | This function works like 'xmobarProp', but uses pipes instead of
 -- property-based logging.
 xmobar :: LayoutClass l Window
@@ -349,76 +178,6 @@ xmobar :: LayoutClass l Window
        -> IO (XConfig (ModifiedLayout AvoidStruts l))
 xmobar = statusBar "xmobar" xmobarPP toggleStrutsKey
 
--- | Modifies the given base configuration to launch the given status bar, write
--- status information to the @_XMONAD_LOG@ property for the bar to read, and
--- allocate space on the screen edges for the bar.
-statusBarProp :: LayoutClass l Window
-              => String    -- ^ The command line to launch the status bar
-              -> PP        -- ^ The pretty printing options
-              -> (XConfig Layout -> (KeyMask, KeySym))
-                           -- ^ The desired key binding to toggle bar visibility
-              -> XConfig l -- ^ The base config
-              -> IO (XConfig (ModifiedLayout AvoidStruts l))
-statusBarProp = statusBarPropTo xmonadDefProp
-
--- | Like 'statusBarProp', but one is able to specify the property to be written
--- to.
-statusBarPropTo :: LayoutClass l Window
-                => String    -- ^ Property to write the string to
-                -> String    -- ^ The command line to launch the status bar
-                -> PP        -- ^ The pretty printing options
-                -> (XConfig Layout -> (KeyMask, KeySym))
-                             -- ^ The desired key binding to toggle bar visibility
-                -> XConfig l -- ^ The base config
-                -> IO (XConfig (ModifiedLayout AvoidStruts l))
-statusBarPropTo prop a p k c = do {sb <- statusBarPropToConfig prop a p; makeStatusBar' sb k c}
-
-
--- | Spawns a status bar and saves its PID. This is useful when the status bars
--- should be restarted with xmonad. Use this in combination with 'cleanupStatusBars'.
---
--- 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.
---
-spawnStatusBarAndRemember :: String -- ^ The command used to spawn the status bar
-                          -> X ()
-spawnStatusBarAndRemember cmd = do
-  newPid <- spawnPID cmd
-  XS.modify (StatusBarPIDs . (newPid :) . getPIDs)
-
--- This newtype wrapper, together with the ExtensionClass instance makes use of
--- the extensible state to save the PIDs bewteen xmonad restarts.
-newtype StatusBarPIDs = StatusBarPIDs { getPIDs :: [ProcessID] }
-  deriving (Show, Read)
-
-instance ExtensionClass StatusBarPIDs where
-  initialValue = StatusBarPIDs []
-  extensionType = PersistentExtension
-
-
--- | Kills the status bars started with 'spawnStatusBarAndRemember', and resets
--- the state. This could go for example at the beginning of the startupHook.
---
--- 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  'spawnStatusBarAndRemember'.
---
--- 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.
---
-cleanupStatusBars :: X ()
-cleanupStatusBars =
-    getPIDs <$> XS.get
-    >>= (io . mapM_ killPid)
-    >> XS.put (StatusBarPIDs [])
-   where
-    killPid :: ProcessID -> IO ()
-    killPid pidToKill = void $ try @SomeException (signalProcessGroup sigTERM pidToKill)
-
-
 -- | Like 'statusBarProp', but uses pipes instead of property-based logging.
 -- Only use this function if your status bar does not support reading from a
 -- property of the root window.
@@ -429,86 +188,9 @@ statusBar :: LayoutClass l Window
                        -- ^ The desired key binding to toggle bar visibility
           -> XConfig l -- ^ The base config
           -> IO (XConfig (ModifiedLayout AvoidStruts l))
-statusBar = makeSB .: statusBarHandleConfig
-
-
--- | Like 'statusBar' with the pretty printing options embedded in the
--- 'X' monad. The @X PP@ value is re-executed every time the logHook runs.
--- Useful if printing options need to be modified dynamically.
-statusBar' :: LayoutClass l Window
-           => String       -- ^ The command line to launch the status bar
-           -> X PP         -- ^ The pretty printing options
-           -> (XConfig Layout -> (KeyMask, KeySym))
-                           -- ^ The desired key binding to toggle bar visibility
-           -> XConfig l    -- ^ The base config
-           -> IO (XConfig (ModifiedLayout AvoidStruts l))
-statusBar' = makeSB .: statusBarHandleConfig'
-
--- | 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.
-makeStatusBar :: LayoutClass l Window
-              => StatusBarConfig -- ^ The status bar config
-              -> XConfig l          -- ^ The base config
-              -> IO (XConfig l)
-makeStatusBar (StatusBarConfig lh sh ch) conf =
-  return $ conf
-    { logHook     = logHook conf *> lh
-    , startupHook = startupHook conf *> ch *> sh
-    }
-
--- | Like 'makeStatusBar', 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.
-makeStatusBar' :: LayoutClass l Window
-               => StatusBarConfig -- ^ The status bar config
-               -> (XConfig Layout -> (KeyMask, KeySym))
-                                    -- ^ The key binding
-               -> XConfig l          -- ^ The base config
-               -> IO (XConfig (ModifiedLayout AvoidStruts l))
-makeStatusBar' sb k conf = do
-  conf' <- makeStatusBar sb conf
-  return $ docks $ conf' { layoutHook = avoidStruts (layoutHook conf')
-                         , keys       = (<>) <$> keys' <*> keys conf'
-                         }
-  where keys' = (`M.singleton` sendMessage ToggleStruts) . k
-
--- | A helper function to be used with 'statusBar', 'statusBarProp'
--- and their variants.
-makeSB :: LayoutClass l Window
-       => IO StatusBarConfig -- ^ The status bar config
-       -> (XConfig Layout -> (KeyMask, KeySym))
-                            -- ^ The key binding
-       -> XConfig l          -- ^ The base config
-       -> IO (XConfig (ModifiedLayout AvoidStruts l))
-makeSB sb k conf = do
-  sb' <- sb
-  makeStatusBar' sb' k conf
-
--- | Write a string to a property on the root window.  This property is of type
--- @UTF8_STRING@.
-xmonadPropLog' :: String  -- ^ Property name
-               -> String  -- ^ Message to be written to the property
-               -> X ()
-xmonadPropLog' prop msg = do
-    d <- asks display
-    r <- asks theRoot
-    xlog <- getAtom prop
-    ustring <- getAtom "UTF8_STRING"
-    io $ changeProperty8 d r xlog ustring propModeReplace (encodeCChar msg)
- where
-    encodeCChar :: String -> [CChar]
-    encodeCChar = map fromIntegral . UTF8.encode
-
--- | Write a string to the @_XMONAD_LOG@ property on the root window.
-xmonadPropLog :: String -> X ()
-xmonadPropLog = xmonadPropLog' xmonadDefProp
+statusBar cmd pp k conf= do
+  sb <- statusBarPipe cmd (pure pp)
+  makeStatusBar' sb k conf
 
 -- |
 -- Helper function which provides ToggleStruts keybinding
@@ -516,85 +198,6 @@ xmonadPropLog = xmonadPropLog' xmonadDefProp
 toggleStrutsKey :: XConfig t -> (KeyMask, KeySym)
 toggleStrutsKey XConfig{modMask = modm} = (modm, xK_b )
 
--- | The default property xmonad writes to. (@_XMONAD_LOG@).
-xmonadDefProp :: String
-xmonadDefProp = "_XMONAD_LOG"
-
-------------------------------------------------------------------------
-
--- | An example log hook, which prints status information to stdout in
--- the default format:
---
--- > 1 2 [3] 4 7 : full : title
---
--- That is, the currently populated workspaces, the current
--- workspace layout, and the title of the focused window.
---
--- To customize the output format, see 'dynamicLogWithPP'.
---
-dynamicLog :: X ()
-dynamicLog = dynamicLogWithPP def
-
--- | Format the current status using the supplied pretty-printing format,
---   and write it to stdout.
-dynamicLogWithPP :: PP -> X ()
-dynamicLogWithPP pp = dynamicLogString pp >>= io . ppOutput pp
-
--- | The same as 'dynamicLogWithPP', except it simply returns the status
---   as a formatted string without actually printing it to stdout, to
---   allow for further processing, or use in some application other than
---   a status bar.
-dynamicLogString :: PP -> X String
-dynamicLogString pp = do
-
-    winset <- gets windowset
-    urgents <- readUrgents
-    sort' <- ppSort pp
-
-    -- layout description
-    let ld = description . S.layout . S.workspace . S.current $ winset
-
-    -- workspace list
-    let ws = pprWindowSet sort' urgents pp winset
-
-    -- window titles
-    let stack  = S.index winset
-        focWin = S.peek  winset
-        ppWin :: Window -> String -> String  -- pretty print a window title
-            = bool (ppTitleUnfocused pp) (ppTitle pp) . (focWin ==) . Just
-    winNames <- traverse (fmap show . getName) stack
-    let ppNames = unwords . filter (not . null) $
-            zipWith (\w n -> ppWin w $ ppTitleSanitize pp n) stack winNames
-
-    -- run extra loggers, ignoring any that generate errors.
-    extras <- mapM (`catchX` return Nothing) $ ppExtras pp
-
-    return $ sepBy (ppSep pp) . ppOrder pp $
-                        [ ws
-                        , ppLayout pp ld
-                        , ppNames
-                        ]
-                        ++ catMaybes extras
-
--- | Format the workspace information, given a workspace sorting function,
---   a list of urgent windows, a pretty-printer format, and the current
---   WindowSet.
-pprWindowSet :: WorkspaceSort -> [Window] -> PP -> WindowSet -> String
-pprWindowSet sort' urgents pp s = sepBy (ppWsSep pp) . map fmt . sort' $
-            map S.workspace (S.current s : S.visible s) ++ S.hidden s
-  where
-    this     = S.currentTag s
-    visibles = map (S.tag . S.workspace) (S.visible s)
-
-    fmt w = printer pp (ppRename pp (S.tag w) w)
-      where
-        printer | any (\x -> (== Just (S.tag w)) (S.findTag x s)) urgents = ppUrgent
-                | S.tag w == this                                         = ppCurrent
-                | S.tag w `elem` visibles && isJust (S.stack w)           = ppVisible
-                | S.tag w `elem` visibles                                 = liftA2 fromMaybe ppVisible ppVisibleNoWindows
-                | isJust (S.stack w)                                      = ppHidden
-                | otherwise                                               = ppHiddenNoWindows
-
 -- |
 -- Workspace logger with a format designed for Xinerama:
 --
@@ -615,445 +218,28 @@ pprWindowSet sort' urgents pp s = sepBy (ppWsSep pp) . map fmt . sort' $
 dynamicLogXinerama :: X ()
 dynamicLogXinerama = withWindowSet $ io . putStrLn . pprWindowSetXinerama
 
-pprWindowSetXinerama :: WindowSet -> String
-pprWindowSetXinerama ws = "[" ++ unwords onscreen ++ "] " ++ unwords offscreen
-  where onscreen  = map (S.tag . S.workspace)
-                        . sortOn S.screen $ S.current ws : S.visible ws
-        offscreen = map S.tag . filter (isJust . S.stack)
-                        . sortOn S.tag $ S.hidden ws
-
--- | Wrap a string in delimiters, unless it is empty.
-wrap :: String  -- ^ left delimiter
-     -> String  -- ^ right delimiter
-     -> String  -- ^ output string
-     -> String
-wrap _ _ "" = ""
-wrap l r m  = l ++ m ++ r
-
--- | Pad a string with a leading and trailing space.
-pad :: String -> String
-pad = wrap " " " "
-
--- | Trim leading and trailing whitespace from a string.
-trim :: String -> String
-trim = f . f
-    where f = reverse . dropWhile isSpace
-
--- | Limit a string to a certain length, adding "..." if truncated.
-shorten :: Int -> String -> String
-shorten = shorten' "..."
-
--- | Limit a string to a certain length, adding @end@ if truncated.
-shorten' :: String -> Int -> String -> String
-shorten' end n xs | length xs < n = xs
-                  | otherwise     = take (n - length end) xs ++ end
-
--- | Like 'shorten', but truncate from the left instead of right.
-shortenLeft :: Int -> String -> String
-shortenLeft = shortenLeft' "..."
-
--- | Like 'shorten'', but truncate from the left instead of right.
-shortenLeft' :: String -> Int -> String -> String
-shortenLeft' end n xs | l < n     = xs
-                      | otherwise = end ++ drop (l - n + length end) xs
- where l = length xs
-
--- | Output a list of strings, ignoring empty ones and separating the
---   rest with the given separator.
-sepBy :: String   -- ^ separator
-      -> [String] -- ^ fields to output
-      -> String
-sepBy sep = intercalate sep . filter (not . null)
-
--- | Use dzen escape codes to output a string with given foreground
---   and background colors.
-dzenColor :: String  -- ^ foreground color: a color name, or #rrggbb format
-          -> String  -- ^ background color
-          -> String  -- ^ output string
-          -> String
-dzenColor fg bg = wrap (fg1++bg1) (fg2++bg2)
- where (fg1,fg2) | null fg = ("","")
-                 | otherwise = ("^fg(" ++ fg ++ ")","^fg()")
-       (bg1,bg2) | null bg = ("","")
-                 | otherwise = ("^bg(" ++ bg ++ ")","^bg()")
-
--- | Escape any dzen metacharacters.
-dzenEscape :: String -> String
-dzenEscape = concatMap (\x -> if x == '^' then "^^" else [x])
-
--- | Strip dzen formatting or commands.
-dzenStrip :: String -> String
-dzenStrip = strip [] where
-    strip keep x
-      | null x              = keep
-      | "^^" `isPrefixOf` x = strip (keep ++ "^") (drop 2 x)
-      | '^' == head x       = strip keep (drop 1 . dropWhile (/= ')') $ x)
-      | otherwise           = let (good,x') = span (/= '^') x
-                              in strip (keep ++ good) x'
-
--- | Use xmobar escape codes to output a string with given foreground
---   and background colors.
-xmobarColor :: String  -- ^ foreground color: a color name, or #rrggbb format
-            -> String  -- ^ background color
-            -> String  -- ^ output string
-            -> String
-xmobarColor fg bg = wrap t "</fc>"
- where t = concat ["<fc=", fg, if null bg then "" else "," ++ bg, ">"]
-
--- | Encapsulate text with an action. The text will be displayed, and the
--- action executed when the displayed text is clicked. Illegal input is not
--- filtered, allowing xmobar to display any parse errors. Uses xmobar's new
--- syntax wherein the command is surrounded by backticks.
-xmobarAction :: String
-                -- ^ Command. Use of backticks (`) will cause a parse error.
-             -> String
-                -- ^ Buttons 1-5, such as "145". Other characters will cause a
-                -- parse error.
-             -> String
-                -- ^ Displayed/wrapped text.
-             -> String
-xmobarAction command button = wrap l r
-    where
-        l = "<action=`" ++ command ++ "` button=" ++ button ++ ">"
-        r = "</action>"
-
--- | Use xmobar box to add a border to an arbitrary string.
-xmobarBorder :: String -- ^ Border type. Possible values: VBoth, HBoth, Full,
-                       -- Top, Bottom, Left or Right
-             -> String -- ^ color: a color name, or #rrggbb format
-             -> Int    -- ^ width in pixels
-             -> String -- ^ output string
-             -> String
-xmobarBorder border color width = wrap prefix "</box>"
-  where
-    prefix = "<box type=" ++ border ++ " width=" ++ show width ++ " color="
-      ++ color ++ ">"
-
--- | Encapsulate arbitrary text for display only, i.e. untrusted content if
--- wrapped (perhaps from window titles) will be displayed only, with all tags
--- ignored. Introduced in xmobar 0.21; see their documentation. Be careful not
--- to shorten the result.
-xmobarRaw :: String -> String
-xmobarRaw "" = ""
-xmobarRaw s  = concat ["<raw=", show $ length s, ":", s, "/>"]
-
--- | Strip xmobar markup, specifically the <fc>, <icon> and <action> tags and
--- the matching tags like </fc>.
-xmobarStrip :: String -> String
-xmobarStrip = converge (xmobarStripTags ["fc","icon","action"])
-
-converge :: (Eq a) => (a -> a) -> a -> a
-converge f a = let xs = iterate f a
-    in fst $ head $ dropWhile (uncurry (/=)) $ zip xs $ tail xs
-
-xmobarStripTags :: [String] -- ^ tags
-        -> String -> String -- ^ with all <tag>...</tag> removed
-xmobarStripTags tags = strip [] where
-    strip keep [] = keep
-    strip keep x
-        | rest: _ <- mapMaybe dropTag tags = strip keep rest
-
-
-        | '<':xs <- x = strip (keep ++ "<") xs
-        | (good,x') <- span (/= '<') x = strip (keep ++ good) x' -- this is n^2 bad... but titles have few tags
-      where dropTag :: String -> Maybe String
-            dropTag tag = msum [fmap dropTilClose (openTag tag `stripPrefix` x),
-                                                   closeTag tag `stripPrefix` x]
-
-    dropTilClose, openTag, closeTag :: String -> String
-    dropTilClose = drop 1 . dropWhile (/= '>')
-    openTag str = "<" ++ str ++ "="
-    closeTag str = "</" ++ str ++ ">"
-
--- | Transforms a pretty-printer into one not displaying the given workspaces.
+-- | Run xmonad with a property-based xmobar status bar set to some nice
+-- defaults.
 --
--- For example, filtering out the @NSP@ workspace before giving the 'PP' to
--- 'dynamicLogWithPP':
+-- > main = xmonad =<< xmobarProp myConfig
+-- >
+-- > myConfig = def { ... }
 --
--- > logHook = dynamicLogWithPP . filterOutWsPP [scratchpadWorkspaceTag] $ def
+-- The intent is that the above config file should provide a nice
+-- status bar with minimal effort. Note that you still need to configure
+-- xmobar to use the @XMonadLog@ plugin instead of the default @StdinReader@,
+-- see above.
 --
--- Here is another example, when using "XMonad.Layout.IndependentScreens".  If
--- you have handles @hLeft@ and @hRight@ for bars on the left and right screens,
--- respectively, and @pp@ is a pretty-printer function that takes a handle, you
--- could write
+-- If you wish to customize the status bar format at all, use the modernized
+-- interface provided by the "XMonad.Hooks.StatusBar" and
+-- "XMonad.Hooks.StatusBar.PP" modules instead.
 --
--- > logHook = let log screen handle = dynamicLogWithPP . filterOutWsPP [scratchpadWorkspaceTag] . marshallPP screen . pp $ handle
--- >           in log 0 hLeft >> log 1 hRight
-filterOutWsPP :: [WorkspaceId] -> PP -> PP
-filterOutWsPP ws pp = pp { ppSort = (. filterOutWs ws) <$> ppSort pp }
-
-
--- $furthercustomization
--- Internally, the status bars are managed through the 'StatusBarConfig', which provides a convenient
--- abstraction over what a status bar is and how to manage it.
--- Instead of directly modifying the config, the functions 'statusBarProp' and 'statusBar' create an
--- appropriate 'StatusBarConfig' and call 'makeStatusBar' or 'makeStatusBar'' on it. This enables the user
--- to have more flexibility and control over the status bars, without having do the plumbing themselves.
---
--- The difference between 'makeStatusBar' and 'makeStatusBar'' is that 'makeStatusBar' tries to stay
--- out of your way, whereas 'makeStatusBar'' configures an extra keybinding to toggle the status bars,
--- and also applies the 'avoidStruts' layout modifier as well as the 'docks' combinator, which makes it
--- similar to the 'statusBar' function and its variants.
--- Using this interface has the benefit of enabling a cleaner config and composable status bars.
---
--- For example, if you want to use property logging as provided by 'statusBarProp' you could do the following:
---
--- > main = do
--- >   mySB <- statusBarPropConfig "xmobar" myPP
--- >   xmonad =<< makeStatusBar mySB myConf
---
--- This plays nicer with other combinators that you might have already in your config:
---
--- > main = do
--- >   mySB <- statusBarPropConfig "xmobar" myPP
--- >   xmonad =<< (makeStatusBar mySB . ewmh . docks $ def {...})
---
--- The different statusBar functions rely on the following status bar configs:
---
--- * 'statusBarProp' relies on 'statusBarPropConfig'
---
--- * 'statusBarPropTo' relies on 'statusBarPropConfig'
---
--- * 'statusBar' relies on 'statusBarHandleConfig'
---
--- * 'statusBar'' relies on 'statusBarHandleConfig''
-
--- | This datataype abstracts a status bar to provide a common interface functions like 'statusBar'
--- or 'statusBarProp'. Once defined, a status bar can be incorporated in 'XConfig' by using 'makeStatusBar',
--- which takes care of all the necessary plumbing.
-data StatusBarConfig = StatusBarConfig  { 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 when xmonad is restarted.
-                                        -- This is useful when the status bar is not started
-                                        -- with a pipe.
-                                        }
-
-instance Semigroup StatusBarConfig where
-    StatusBarConfig l s c <> StatusBarConfig l' s' c' =
-      StatusBarConfig (l <> l') (s <> s') (c <> c')
-
-instance Monoid StatusBarConfig where
-    mempty = StatusBarConfig mempty mempty mempty
-
--- | Per default, all the hooks do nothing.
-instance Default StatusBarConfig where
-    def = mempty
-
-
--- | Creates a 'StatusBarConfig' that uses property logging to @_XMONAD_LOG@, which
--- is set in 'xmonadDefProp'
-statusBarPropConfig :: String    -- ^ The command line to launch the status bar
-                    -> PP        -- ^ The pretty printing options
-                    -> IO StatusBarConfig
-statusBarPropConfig = statusBarPropToConfig xmonadDefProp
-
--- | Like 'statusBarPropConfig', but lets you define the property
-statusBarPropToConfig :: String    -- ^ Property to write the string to
-                      -> String    -- ^ The command line to launch the status bar
-                      -> PP        -- ^ The pretty printing options
-                      -> IO StatusBarConfig
-statusBarPropToConfig prop cmd pp = pure $ (def:: StatusBarConfig)
-    { sbLogHook     = xmonadPropLog' prop =<< dynamicLogString pp
-    , sbStartupHook = spawnStatusBarAndRemember cmd
-    , sbCleanupHook = cleanupStatusBars
-    }
-
--- | Creates a 'StatusBarConfig' with logging to the standard input
-statusBarHandleConfig :: String       -- ^ The command line to launch the status bar
-                      -> PP           -- ^ The pretty printing options
-                      -> IO StatusBarConfig
-statusBarHandleConfig cmd pp = do
-    h <- spawnPipe cmd
-    return $ def { sbLogHook = dynamicLogWithPP pp { ppOutput = hPutStrLn h } }
-
--- | Like 'statusBarHandleConfig', but the pretty printing options are embedded in the
--- 'X' monad.
-statusBarHandleConfig' :: String       -- ^ The command line to launch the status bar
-                       -> X PP         -- ^ The pretty printing options
-                       -> IO StatusBarConfig
-statusBarHandleConfig' cmd xpp  = do
-    h <- spawnPipe cmd
-    return $ def { sbLogHook = xpp >>= \pp -> dynamicLogWithPP pp { ppOutput = hPutStrLn h } }
-
--- $multiple
--- A pattern that is often found in a lot of configs that want multiple status bars,
--- generally goes something like this:
---
--- > main = do
--- >   xmproc0 <- spawnPipe "xmobar -x 0 $HOME/.config/xmobar/xmobarrc_top"
--- >   xmproc1 <- spawnPipe "xmobar -x 0 $HOME/.config/xmobar/xmobarrc_bottom"
--- >   xmproc2 <- spawnPipe "xmobar -x 1 $HOME/.config/xmobar/xmobarrc1"
--- >   xmonad $ def {
--- >     ...
--- >     , logHook = dynamicLogWithPP ppTop { ppOutput = hPutStrLn xmproc0 }
--- >              >> dynamicLogWithPP ppBottom { ppOutput = hPutStrLn xmproc1 }
--- >              >> dynamicLogWithPP pp1 { ppOutput = hPutStrLn xmproc2 }
--- >     ...
--- >   }
---
--- Which has a lot of boilerplate and error-prone. Using the provided functions
--- in this section, the amount of boilerplate is reduced.
--- If you wish to have multiple status bars managed by xmonad, you can do so by creating
--- the status bar configs and combining them with '<>':
---
--- > main = do
--- >   xmobarTop    <- statusBarHandleConfig "xmobar -x 0 $HOME/.config/xmobar/xmobarrc_top"    ppTop
--- >   xmobarBottom <- statusBarHandleConfig "xmobar -x 0 $HOME/.config/xmobar/xmobarrc_bottom" ppBottom
--- >   xmobar1      <- statusBarHandleConfig "xmobar -x 1 $HOME/.config/xmobar/xmobarrc1"       pp1
--- >   xmonad =<< makeStatusBar (xmobarTop <> xmobarBottom <> xmobar1) myConfig
---
--- Or if you're feeling adventurous:
---
--- > myBars = map (uncurry statusBarHandleConfig) [ ("xmobar -x 0 $HOME/.config/xmobar/xmobarrc_top",    ppTop)
--- >                                              , ("xmobar -x 0 $HOME/.config/xmobar/xmobarrc_bottom", ppBottom)
--- >                                              , ("xmobar -x 1 $HOME/.config/xmobar/xmobarrc1",       pp1) ]
--- > main = do
--- >   sbs <- sequence myBars
--- >   xmonad =<< makeStatusBar (mconcat sbs) myConfig
---
--- The above examples also work if the different status bars support different
--- logging methods: you could do mix property logging and logging via standard input.
--- One thing to keep in mind: 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.
-
--- | The 'PP' type allows the user to customize the formatting of
---   status information.
-data PP = PP { ppCurrent :: WorkspaceId -> String
-               -- ^ how to print the tag of the currently focused
-               -- workspace
-             , ppVisible :: WorkspaceId -> String
-               -- ^ how to print tags of visible but not focused
-               -- workspaces (xinerama only)
-             , ppHidden  :: WorkspaceId -> String
-               -- ^ how to print tags of hidden workspaces which
-               -- contain windows
-             , ppHiddenNoWindows :: WorkspaceId -> String
-               -- ^ how to print tags of empty hidden workspaces
-             , ppVisibleNoWindows :: Maybe (WorkspaceId -> String)
-               -- ^ how to print tags of empty visible workspaces
-             , ppUrgent :: WorkspaceId -> String
-               -- ^ format to be applied to tags of urgent workspaces.
-             , ppRename :: String -> WindowSpace -> String
-               -- ^ rename/augment the workspace tag
-               --   (note that @WindowSpace -> …@ acts as a Reader monad)
-             , ppSep :: String
-               -- ^ separator to use between different log sections
-               -- (window name, layout, workspaces)
-             , ppWsSep :: String
-               -- ^ separator to use between workspace tags
-             , ppTitle :: String -> String
-               -- ^ window title format for the focused window
-             , ppTitleUnfocused :: String -> String
-               -- ^ window title format for unfocused windows
-             , ppTitleSanitize :: String -> String
-              -- ^ escape / sanitizes input to 'ppTitle' and
-              --   'ppTitleUnfocused'
-             , ppLayout :: String -> String
-               -- ^ layout name format
-             , ppOrder :: [String] -> [String]
-               -- ^ how to order the different log sections. By
-               --   default, this function receives a list with three
-               --   formatted strings, representing the workspaces,
-               --   the layout, and the current window titles,
-               --   respectively. If you have specified any extra
-               --   loggers in 'ppExtras', their output will also be
-               --   appended to the list.  To get them in the reverse
-               --   order, you can just use @ppOrder = reverse@.  If
-               --   you don't want to display the current layout, you
-               --   could use something like @ppOrder = \\(ws:_:t:_) ->
-               --   [ws,t]@, and so on.
-             , ppSort :: X ([WindowSpace] -> [WindowSpace])
-               -- ^ how to sort the workspaces.  See
-               -- "XMonad.Util.WorkspaceCompare" for some useful
-               -- sorts.
-             , ppExtras :: [X (Maybe String)]
-               -- ^ loggers for generating extra information such as
-               -- time and date, system load, battery status, and so
-               -- on.  See "XMonad.Util.Loggers" for examples, or create
-               -- your own!
-             , ppOutput :: String -> IO ()
-               -- ^ applied to the entire formatted string in order to
-               -- output it.  Can be used to specify an alternative
-               -- output method (e.g. write to a pipe instead of
-               -- stdout), and\/or to perform some last-minute
-               -- formatting.
-             }
-
--- | The default pretty printing options, as seen in 'dynamicLog'.
-instance Default PP where
-    def   = PP { ppCurrent         = wrap "[" "]"
-               , ppVisible         = wrap "<" ">"
-               , ppHidden          = id
-               , ppHiddenNoWindows = const ""
-               , ppVisibleNoWindows= Nothing
-               , ppUrgent          = id
-               , ppRename          = pure
-               , ppSep             = " : "
-               , ppWsSep           = " "
-               , ppTitle           = shorten 80
-               , ppTitleUnfocused  = const ""
-               , ppTitleSanitize   = xmobarStrip . dzenEscape
-               , ppLayout          = id
-               , ppOrder           = id
-               , ppOutput          = putStrLn
-               , ppSort            = getSortByIndex
-               , ppExtras          = []
-               }
-
--- | Settings to emulate dwm's statusbar, dzen only.
-dzenPP :: PP
-dzenPP = def { ppCurrent  = dzenColor "white" "#2b4f98" . pad
-                   , ppVisible  = dzenColor "black" "#999999" . pad
-                   , ppHidden   = dzenColor "black" "#cccccc" . pad
-                   , ppHiddenNoWindows = const ""
-                   , ppUrgent   = dzenColor "red" "yellow" . pad
-                   , ppWsSep    = ""
-                   , ppSep      = ""
-                   , ppLayout   = dzenColor "black" "#cccccc" .
-                                  (\ x -> pad $ case x of
-                                            "TilePrime Horizontal" -> "TTT"
-                                            "TilePrime Vertical"   -> "[]="
-                                            "Hinted Full"          -> "[ ]"
-                                            _                      -> x
-                                  )
-                   , ppTitle    = ("^bg(#324c80) " ++) . dzenEscape
-                   }
-
--- | Some nice xmobar defaults.
-xmobarPP :: PP
-xmobarPP = def { ppCurrent = xmobarColor "yellow" "" . wrap "[" "]"
-                     , ppTitle   = xmobarColor "green"  "" . shorten 40
-                     , ppVisible = wrap "(" ")"
-                     , ppUrgent  = xmobarColor "red" "yellow"
-                     }
-
--- | The options that sjanssen likes to use with xmobar, as an
--- example.  Note the use of 'xmobarColor' and the record update on
--- 'def'.
-sjanssenPP :: PP
-sjanssenPP = def { ppCurrent = xmobarColor "white" "black"
-                 , ppTitle = xmobarColor "#00ee00" "" . shorten 120
-                 }
-
--- | The options that byorgey likes to use with dzen, as another example.
-byorgeyPP :: PP
-byorgeyPP = def { ppHiddenNoWindows = showNamedWorkspaces
-                , ppHidden  = dzenColor "black"  "#a8a3f7" . pad
-                , ppCurrent = dzenColor "yellow" "#a8a3f7" . pad
-                , ppUrgent  = dzenColor "red"    "yellow"  . pad
-                , ppSep     = " | "
-                , ppWsSep   = ""
-                , ppTitle   = shorten 70
-                , ppOrder   = reverse
-                }
-  where showNamedWorkspaces wsId = if any (`elem` wsId) ['a'..'z']
-                                       then pad wsId
-                                       else ""
-
--- | A helper combinator
-(.:) :: (d -> c) -> (a -> b -> d) -> a -> b -> c
-(.:) = (.) . (.)
+-- The binding uses the "XMonad.Hooks.ManageDocks" module to automatically
+-- handle screen placement for xmobar, and enables 'mod-b' for toggling
+-- the menu bar.
+xmobarProp :: LayoutClass l Window
+           => XConfig l  -- ^ The base config
+           -> IO (XConfig (ModifiedLayout AvoidStruts l))
+xmobarProp conf = do
+    xmobarPropConfig <- statusBarProp "xmobar" (pure xmobarPP)
+    makeStatusBar' xmobarPropConfig toggleStrutsKey conf
diff --git a/XMonad/Hooks/StatusBar.hs b/XMonad/Hooks/StatusBar.hs
new file mode 100644
index 00000000..9ee54ff2
--- /dev/null
+++ b/XMonad/Hooks/StatusBar.hs
@@ -0,0 +1,420 @@
+{-# LANGUAGE FlexibleContexts, TypeApplications #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  XMonad.Hooks.StatusBar
+-- Copyright   :  (c) Yecine Megdiche <yecine.megdiche@gmail.com>
+-- License     :  BSD3-style (see LICENSE)
+--
+-- Maintainer  :  Yecine Megdiche <yecine.megdiche@gmail.com>
+-- Stability   :  unstable
+-- Portability :  unportable
+--
+-- This module provides a new interface that replaces "XMonad.Hooks.DynamicLog",
+-- by providing composoble and dynamic status bars. Supports property-based as well
+-- as pipe-based status bars.
+--
+-----------------------------------------------------------------------------
+
+module XMonad.Hooks.StatusBar (
+  -- * Usage
+  -- $usage
+  StatusBarConfig(..),
+  makeStatusBar,
+  makeStatusBar',
+
+  -- * Available Configs
+  -- $availableconfigs
+  statusBarPipe,
+  statusBarProp,
+  statusBarPropTo,
+
+  -- * Multiple Status Bars
+  -- $multiple
+
+  -- * Property Logging utilities
+  xmonadPropLog,
+  xmonadPropLog',
+  xmonadDefProp,
+
+  -- * Managing Status Bar Processes
+  spawnStatusBarAndRemember,
+  cleanupStatusBars,
+  ) where
+
+import Control.Exception (SomeException, try)
+import Control.Monad (void)
+import qualified Codec.Binary.UTF8.String as UTF8 (encode)
+import System.Posix.Signals (sigTERM, signalProcessGroup)
+import System.Posix.Types (ProcessID)
+
+import qualified Data.Map        as M
+
+import Foreign.C (CChar)
+
+import XMonad
+
+import XMonad.Util.Run
+import qualified XMonad.Util.ExtensibleState as XS
+
+import XMonad.Layout.LayoutModifier
+import XMonad.Hooks.ManageDocks
+import XMonad.Hooks.StatusBar.PP
+
+-- $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 recommended way to use this module with xmobar, as well as any other
+-- status bar that supports property logging (you can read more about X11
+-- properties
+-- [here](https://en.wikipedia.org/wiki/X_Window_System_core_protocol#Properties)
+-- or
+-- [here](https://tronche.com/gui/x/xlib/window-information/properties-and-atoms.html),
+-- although you don't have to understand them in order to use the functions
+-- below), is to use 'statusBarProp' with 'makeStatusBar', which takes care of
+-- the necessary plumbing-no shell scripting required!
+--
+-- > main = do
+-- >   mySB <- statusBarProp "xmobar" (pure myPP)
+-- >   xmonad =<< makeStatusBar mySB myConf
+--
+-- which plays nice with other combinators that you might have already
+-- in your config:
+--
+-- > main = do
+-- >   mySB <- statusBarProp "xmobar" (pure myPP)
+-- >   xmonad =<< (makeStatusBar 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 simply achieved by 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
+-- 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 will now
+-- 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.
+--
+-- If you do not want to use any of the "batteries included" functions above,
+-- you can also add all of the necessary plumbing yourself (the source of
+-- 'makeStatusBar' might come in handy here).
+--
+-- 'xmonadPropLog' allows you to write a string to the @_XMONAD_LOG@ property of
+-- the root window.  Together with 'dynamicLogString', you can now simply set
+-- your 'logHook' to the appropriate function, for instance
+--
+-- > main = xmonad $ def {
+-- >    ...
+-- >    , logHook = xmonadPropLog =<< dynamicLogString myPP
+-- >    ...
+-- >    }
+--
+-- If you want to define your own property name, use 'xmonadPropLog'' instead of
+-- 'xmonadPropLog'.
+--
+-- If you just want to use the default pretty-printing format, you can replace
+-- @myPP@ with 'def' in the above 'logHook'.
+--
+-- Note that setting 'logHook' only sets up xmonad's output; you are
+-- responsible for starting your own status bar program and making sure it reads
+-- from the property that xmonad writes to.  To start your bar, simply put it
+-- into your 'startupHook'.  You will also have also have to add 'docks' and
+-- 'avoidStruts' to your config.  Putting all of this together would look
+-- something like
+--
+-- > import XMonad.Util.SpawnOnce (spawnOnce)
+-- > import XMonad.Hooks.ManageDocks (avoidStruts, docks)
+-- >
+-- > main = do
+-- >     xmonad $ docks $ def {
+-- >       ...
+-- >       , logHook     = xmonadPropLog =<< dynamicLogString myPP
+-- >       , startupHook = spawnOnce "xmobar"
+-- >       , layoutHook  = avoidStruts myLayout
+-- >       ...
+-- >       }
+-- > myPP = def { ... }
+-- > myLayout = ...
+--
+-- If you want a keybinding to toggle your bar, you will also need to add this
+-- to the rest of your keybindings.
+--
+-- The above has the problem that xmobar will not get restarted whenever you
+-- restart xmonad ('XMonad.Util.SpawnOnce.spawnOnce' will simply prevent your
+-- chosen status bar from spawning again). Using 'statusBarProp', however, takes
+-- care of the necessary plumbing /and/ keeps track of the started status bars, so
+-- they can be correctly restarted with xmonad. This is achieved using
+-- 'spawnStatusBarAndRemember' to start them and 'cleanupStatusBars' to kill
+-- previously started ones.
+--
+-- Even if you don't use a statusbar, you can still use 'dynamicLogString' to
+-- show on-screen notifications in response to some events. For example, to show
+-- the current layout when it changes, you could make a keybinding to cycle the
+-- layout and display the current status:
+--
+-- > ((mod1Mask, xK_a), sendMessage NextLayout >> (dynamicLogString myPP >>= \d->spawn $"xmessage "++d))
+--
+-- If you use a status bar that does not support reading from a property log
+-- (like dzen), and you don't want to use the 'statusBar' function, you can,
+-- again, also manually add all of the required components.
+--
+-- This works much like the property based solution above, just that you will
+-- want to use 'dynamicLog' or 'dynamicLogXinerama' in place of 'xmonadPropLog'.
+--
+-- > main = xmonad $ def {
+-- >    ...
+-- >    , logHook = dynamicLog
+-- >    ...
+-- >    }
+--
+-- For more flexibility, you can also use 'dynamicLogWithPP' and supply your own
+-- pretty-printing format (by either defining one from scratch, or customizing
+-- one of the provided examples).  For example:
+--
+-- >    -- use sjanssen's pretty-printer format, but with the sections
+-- >    -- in reverse
+-- >    logHook = dynamicLogWithPP $ sjanssenPP { ppOrder = reverse }
+--
+-- Again, you will have to do all the necessary plumbing yourself.  In addition,
+-- you are also responsible for creating a pipe for you status bar to read from:
+--
+-- > import XMonad.Util.Run (hPutStrLn, spawnPipe)
+-- >
+-- > main = do
+-- >     h <- spawnPipe "dzen2 -options -foo -bar"
+-- >     xmonad $ def {
+-- >       ...
+-- >       , logHook = dynamicLogWithPP $ def { ppOutput = hPutStrLn h }
+-- >       ...
+-- >       }
+--
+-- In the above, note that if you use @spawnPipe@ you need to redefine the
+-- 'ppOutput' field of your pretty-printer, as was done in the example above; by
+-- default the status will be printed to stdout rather than the pipe you create.
+--
+-- The status bars are managed through the 'StatusBarConfig', which provides
+-- a convenient abstraction over what a status bar is and how to manage it.
+-- This modules provides how to create these status bar configs, and how to
+-- incorporate them in your xmonad config: using 'makeStatusBar'
+-- or 'makeStatusBar''.
+--
+-- The difference between 'makeStatusBar' and 'makeStatusBar'' is that 'makeStatusBar'
+-- tries to stay out of your way, whereas 'makeStatusBar'' configures an
+-- extra keybinding to toggle the status bars, and also applies the
+-- 'avoidStruts' layout modifier as well as the 'docks' combinator.
+
+-- | 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 'makeStatusBar' or
+-- 'makeStatusBar'', which take care of the necessary plumbing.
+data StatusBarConfig = StatusBarConfig  { 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 when xmonad is restarted.
+                                        -- This is useful when the status bar is not started
+                                        -- with a pipe.
+                                        }
+
+instance Semigroup StatusBarConfig where
+    StatusBarConfig l s c <> StatusBarConfig l' s' c' =
+      StatusBarConfig (l <> l') (s <> s') (c <> c')
+
+instance Monoid StatusBarConfig where
+    mempty = StatusBarConfig mempty mempty mempty
+
+-- | Per default, all the hooks do nothing.
+instance Default StatusBarConfig where
+    def = mempty
+
+-- | 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.
+makeStatusBar :: LayoutClass l Window
+              => StatusBarConfig    -- ^ The status bar config
+              -> XConfig l          -- ^ The base config
+              -> IO (XConfig l)
+makeStatusBar (StatusBarConfig lh sh ch) conf =
+  return $ conf
+    { logHook     = logHook conf *> lh
+    , startupHook = startupHook conf *> ch *> sh
+    }
+
+-- | Like 'makeStatusBar', 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.
+makeStatusBar' :: LayoutClass l Window
+               => StatusBarConfig -- ^ The status bar config
+               -> (XConfig Layout -> (KeyMask, KeySym))
+                                  -- ^ The key binding
+               -> XConfig l       -- ^ The base config
+               -> IO (XConfig (ModifiedLayout AvoidStruts l))
+makeStatusBar' sb k conf = do
+  conf' <- makeStatusBar sb conf
+  return $ docks $ conf' { layoutHook = avoidStruts (layoutHook conf')
+                         , keys       = (<>) <$> keys' <*> keys conf'
+                         }
+  where keys' = (`M.singleton` sendMessage ToggleStruts) . k
+
+-- | Creates a 'StatusBarConfig' that uses property logging to @_XMONAD_LOG@, which
+-- is set in 'xmonadDefProp'
+statusBarProp :: String -- ^ The command line to launch the status bar
+                    -> X PP   -- ^ The pretty printing options
+                    -> IO StatusBarConfig
+statusBarProp = statusBarPropTo xmonadDefProp
+
+-- | Like 'statusBarProp', but lets you define the property
+statusBarPropTo :: String -- ^ Property to write the string to
+                      -> String -- ^ The command line to launch the status bar
+                      -> X PP   -- ^ The pretty printing options
+                      -> IO StatusBarConfig
+statusBarPropTo prop cmd pp = pure def
+    { sbLogHook     = xmonadPropLog' prop =<< dynamicLogString =<< pp
+    , sbStartupHook = spawnStatusBarAndRemember cmd
+    , sbCleanupHook = cleanupStatusBars
+    }
+
+-- | Like 'statusBarProp', but uses pipe-based logging instead.
+statusBarPipe :: String -- ^ The command line to launch the status bar
+                      -> X PP   -- ^ The pretty printing options
+                      -> IO StatusBarConfig
+statusBarPipe cmd xpp  = do
+    h <- spawnPipe cmd
+    return $ def { sbLogHook = xpp >>= \pp -> dynamicLogWithPP pp { ppOutput = hPutStrLn h } }
+
+-- $multiple
+-- A pattern that is often found in a lot of configs that want multiple status bars,
+-- generally goes something like this:
+--
+-- > main = do
+-- >   xmproc0 <- spawnPipe "xmobar -x 0 $HOME/.config/xmobar/xmobarrc_top"
+-- >   xmproc1 <- spawnPipe "xmobar -x 0 $HOME/.config/xmobar/xmobarrc_bottom"
+-- >   xmproc2 <- spawnPipe "xmobar -x 1 $HOME/.config/xmobar/xmobarrc1"
+-- >   xmonad $ def {
+-- >     ...
+-- >     , logHook = dynamicLogWithPP ppTop { ppOutput = hPutStrLn xmproc0 }
+-- >              >> dynamicLogWithPP ppBottom { ppOutput = hPutStrLn xmproc1 }
+-- >              >> dynamicLogWithPP pp1 { ppOutput = hPutStrLn xmproc2 }
+-- >     ...
+-- >   }
+--
+-- Which has a lot of boilerplate and is error-prone. By using the new interface, the
+-- config becomes more declarative and there's much less room for errors. You use it
+-- by creating the suitable status bar configs and combining them with '<>':
+--
+-- > main = do
+-- >   xmobarTop    <- statusBarPipe "xmobar -x 0 $HOME/.config/xmobar/xmobarrc_top"    (pure ppTop)
+-- >   xmobarBottom <- statusBarPipe "xmobar -x 0 $HOME/.config/xmobar/xmobarrc_bottom" (pure ppBottom)
+-- >   xmobar1      <- statusBarPipe "xmobar -x 1 $HOME/.config/xmobar/xmobarrc1"       (pure pp1)
+-- >   xmonad =<< makeStatusBar (xmobarTop <> xmobarBottom <> xmobar1) myConfig
+--
+-- Or if you're feeling adventurous:
+--
+-- > myBars = map (uncurry statusBarPipe) [ ("xmobar -x 0 $HOME/.config/xmobar/xmobarrc_top",    pure ppTop)
+-- >                                              , ("xmobar -x 0 $HOME/.config/xmobar/xmobarrc_bottom", pure ppBottom)
+-- >                                              , ("xmobar -x 1 $HOME/.config/xmobar/xmobarrc1",       pure pp1) ]
+-- > main = do
+-- >   sbs <- sequence myBars
+-- >   xmonad =<< makeStatusBar (mconcat sbs) myConfig
+--
+-- The above examples also work if the different status bars support different
+-- logging methods: you could do mix property logging and logging via standard input.
+-- One thing to keep in mind: 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.
+
+
+-- | The default property xmonad writes to. (@_XMONAD_LOG@).
+xmonadDefProp :: String
+xmonadDefProp = "_XMONAD_LOG"
+
+-- | Write a string to the @_XMONAD_LOG@ property on the root window.
+xmonadPropLog :: String -> X ()
+xmonadPropLog = xmonadPropLog' xmonadDefProp
+
+-- | Write a string to a property on the root window.  This property is of type
+-- @UTF8_STRING@. The string must have been processed by 'encodeString'
+-- ('dynamicLogString' does this).
+xmonadPropLog' :: String  -- ^ Property name
+               -> String  -- ^ Message to be written to the property
+               -> X ()
+xmonadPropLog' prop msg = do
+    d <- asks display
+    r <- asks theRoot
+    xlog <- getAtom prop
+    ustring <- getAtom "UTF8_STRING"
+    io $ changeProperty8 d r xlog ustring propModeReplace (encodeCChar msg)
+ where
+    encodeCChar :: String -> [CChar]
+    encodeCChar = map fromIntegral . UTF8.encode
+
+
+-- This newtype wrapper, together with the ExtensionClass instance make use of
+-- the extensible state to save the PIDs bewteen xmonad restarts.
+newtype StatusBarPIDs = StatusBarPIDs { getPIDs :: [ProcessID] }
+  deriving (Show, Read)
+
+instance ExtensionClass StatusBarPIDs where
+  initialValue = StatusBarPIDs []
+  extensionType = PersistentExtension
+
+-- | Kills the status bars started with 'spawnStatusBarAndRemember', and resets
+-- the state. This could go for example at the beginning of the startupHook.
+--
+-- 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  'spawnStatusBarAndRemember'.
+--
+-- 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.
+cleanupStatusBars :: X ()
+cleanupStatusBars =
+    getPIDs <$> XS.get
+    >>= (io . mapM_ killPid)
+    >> XS.put (StatusBarPIDs [])
+   where
+    killPid :: ProcessID -> IO ()
+    killPid pidToKill = void $ try @SomeException (signalProcessGroup sigTERM pidToKill)
+
+-- | Spawns a status bar and saves its PID. This is useful when the status bars
+-- should be restarted with xmonad. Use this in combination with 'cleanupStatusBars'.
+--
+-- 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.
+spawnStatusBarAndRemember :: String -- ^ The command used to spawn the status bar
+                          -> X ()
+spawnStatusBarAndRemember cmd = do
+  newPid <- spawnPID cmd
+  XS.modify (StatusBarPIDs . (newPid :) . getPIDs)
diff --git a/XMonad/Hooks/StatusBar/PP.hs b/XMonad/Hooks/StatusBar/PP.hs
new file mode 100644
index 00000000..c30b6d8f
--- /dev/null
+++ b/XMonad/Hooks/StatusBar/PP.hs
@@ -0,0 +1,441 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  XMonad.Hooks.StatusBar.PP
+-- Copyright   :  (c) Don Stewart <dons@cse.unsw.edu.au>
+-- License     :  BSD3-style (see LICENSE)
+--
+-- Maintainer  :  Don Stewart <dons@cse.unsw.edu.au>
+-- Stability   :  unstable
+-- Portability :  unportable
+--
+-- This module provides the pretty-printing abstraction and utilities that
+-- can be used inside the 'logHook'.
+--
+-----------------------------------------------------------------------------
+
+module XMonad.Hooks.StatusBar.PP (
+    -- * Usage
+    -- $usage
+
+    -- * Build your own formatter
+    PP(..), def,
+    dynamicLog,
+    dynamicLogString,
+    dynamicLogWithPP,
+
+    -- * Example formatters
+    dzenPP, xmobarPP, sjanssenPP, byorgeyPP,
+
+    -- * Formatting utilities
+    wrap, pad, trim, shorten, shorten', shortenLeft, shortenLeft',
+    xmobarColor, xmobarAction, xmobarBorder,
+    xmobarRaw, xmobarStrip, xmobarStripTags,
+    dzenColor, dzenEscape, dzenStrip, filterOutWsPP,
+
+    -- * Internal formatting functions
+    pprWindowSet,
+    pprWindowSetXinerama
+
+    ) where
+
+import Control.Applicative (liftA2)
+import Control.Monad (msum)
+import Data.Bool (bool)
+import Data.Char (isSpace)
+import Data.List (intercalate, isPrefixOf, sortOn, stripPrefix)
+import Data.Maybe (catMaybes, fromMaybe, isJust, mapMaybe)
+
+import qualified XMonad.StackSet as S
+
+import XMonad
+
+import XMonad.Util.NamedWindows
+import XMonad.Util.WorkspaceCompare
+import XMonad.Hooks.UrgencyHook
+
+-- $usage
+-- An example usage for this module would be:
+--
+-- > import XMonad
+-- > import XMonad.Hooks.DynamicLog
+-- >
+-- > myPP = def { ppCurrent = xmobarColor "black" "white" }
+-- > main = xmonad =<< statusBar "xmobar" myPP myToggleStrutsKey myConfig
+--
+-- Check "XMonad.Hooks.StatusBar" for how to use with status bars.
+--
+
+-- | The 'PP' type allows the user to customize the formatting of
+--   status information.
+data PP = PP { ppCurrent :: WorkspaceId -> String
+               -- ^ how to print the tag of the currently focused
+               -- workspace
+             , ppVisible :: WorkspaceId -> String
+               -- ^ how to print tags of visible but not focused
+               -- workspaces (xinerama only)
+             , ppHidden  :: WorkspaceId -> String
+               -- ^ how to print tags of hidden workspaces which
+               -- contain windows
+             , ppHiddenNoWindows :: WorkspaceId -> String
+               -- ^ how to print tags of empty hidden workspaces
+             , ppVisibleNoWindows :: Maybe (WorkspaceId -> String)
+               -- ^ how to print tags of empty visible workspaces
+             , ppUrgent :: WorkspaceId -> String
+               -- ^ format to be applied to tags of urgent workspaces.
+             , ppRename :: String -> WindowSpace -> String
+               -- ^ rename/augment the workspace tag
+               --   (note that @WindowSpace -> …@ acts as a Reader monad)
+             , ppSep :: String
+               -- ^ separator to use between different log sections
+               -- (window name, layout, workspaces)
+             , ppWsSep :: String
+               -- ^ separator to use between workspace tags
+             , ppTitle :: String -> String
+               -- ^ window title format for the focused window
+             , ppTitleUnfocused :: String -> String
+               -- ^ window title format for unfocused windows
+             , ppTitleSanitize :: String -> String
+              -- ^ escape / sanitizes input to 'ppTitle' and
+              --   'ppTitleUnfocused'
+             , ppLayout :: String -> String
+               -- ^ layout name format
+             , ppOrder :: [String] -> [String]
+               -- ^ how to order the different log sections. By
+               --   default, this function receives a list with three
+               --   formatted strings, representing the workspaces,
+               --   the layout, and the current window titles,
+               --   respectively. If you have specified any extra
+               --   loggers in 'ppExtras', their output will also be
+               --   appended to the list.  To get them in the reverse
+               --   order, you can just use @ppOrder = reverse@.  If
+               --   you don't want to display the current layout, you
+               --   could use something like @ppOrder = \\(ws:_:t:_) ->
+               --   [ws,t]@, and so on.
+             , ppSort :: X ([WindowSpace] -> [WindowSpace])
+               -- ^ how to sort the workspaces.  See
+               -- "XMonad.Util.WorkspaceCompare" for some useful
+               -- sorts.
+             , ppExtras :: [X (Maybe String)]
+               -- ^ loggers for generating extra information such as
+               -- time and date, system load, battery status, and so
+               -- on.  See "XMonad.Util.Loggers" for examples, or create
+               -- your own!
+             , ppOutput :: String -> IO ()
+               -- ^ applied to the entire formatted string in order to
+               -- output it.  Can be used to specify an alternative
+               -- output method (e.g. write to a pipe instead of
+               -- stdout), and\/or to perform some last-minute
+               -- formatting.
+             }
+
+-- | The default pretty printing options, as seen in 'dynamicLog'.
+instance Default PP where
+  def = PP { ppCurrent          = wrap "[" "]"
+           , ppVisible          = wrap "<" ">"
+           , ppHidden           = id
+           , ppHiddenNoWindows  = const ""
+           , ppVisibleNoWindows = Nothing
+           , ppUrgent           = id
+           , ppRename           = pure
+           , ppSep              = " : "
+           , ppWsSep            = " "
+           , ppTitle            = shorten 80
+           , ppTitleUnfocused   = const ""
+           , ppTitleSanitize    = xmobarStrip . dzenEscape
+           , ppLayout           = id
+           , ppOrder            = id
+           , ppOutput           = putStrLn
+           , ppSort             = getSortByIndex
+           , ppExtras           = []
+           }
+-- | An example log hook, which prints status information to stdout in
+-- the default format:
+--
+-- > 1 2 [3] 4 7 : full : title
+--
+-- That is, the currently populated workspaces, the current
+-- workspace layout, and the title of the focused window.
+--
+-- To customize the output format, see 'dynamicLogWithPP'.
+--
+dynamicLog :: X ()
+dynamicLog = dynamicLogWithPP def
+
+-- | Format the current status using the supplied pretty-printing format,
+--   and write it to stdout.
+dynamicLogWithPP :: PP -> X ()
+dynamicLogWithPP pp = dynamicLogString pp >>= io . ppOutput pp
+
+-- | The same as 'dynamicLogWithPP', except it simply returns the status
+--   as a formatted string without actually printing it to stdout, to
+--   allow for further processing, or use in some application other than
+--   a status bar.
+dynamicLogString :: PP -> X String
+dynamicLogString pp = do
+
+    winset <- gets windowset
+    urgents <- readUrgents
+    sort' <- ppSort pp
+
+    -- layout description
+    let ld = description . S.layout . S.workspace . S.current $ winset
+
+    -- workspace list
+    let ws = pprWindowSet sort' urgents pp winset
+
+    -- window titles
+    let stack  = S.index winset
+        focWin = S.peek  winset
+        ppWin :: Window -> String -> String  -- pretty print a window title
+            = bool (ppTitleUnfocused pp) (ppTitle pp) . (focWin ==) . Just
+    winNames <- traverse (fmap show . getName) stack
+    let ppNames = unwords . filter (not . null) $
+            zipWith (\w n -> ppWin w $ ppTitleSanitize pp n) stack winNames
+
+    -- run extra loggers, ignoring any that generate errors.
+    extras <- mapM (`catchX` return Nothing) $ ppExtras pp
+
+    return $ sepBy (ppSep pp) . ppOrder pp $
+                        [ ws
+                        , ppLayout pp ld
+                        , ppNames
+                        ]
+                        ++ catMaybes extras
+
+-- | Format the workspace information, given a workspace sorting function,
+--   a list of urgent windows, a pretty-printer format, and the current
+--   WindowSet.
+pprWindowSet :: WorkspaceSort -> [Window] -> PP -> WindowSet -> String
+pprWindowSet sort' urgents pp s = sepBy (ppWsSep pp) . map fmt . sort' $
+            map S.workspace (S.current s : S.visible s) ++ S.hidden s
+  where
+    this     = S.currentTag s
+    visibles = map (S.tag . S.workspace) (S.visible s)
+
+    fmt w = printer pp (ppRename pp (S.tag w) w)
+      where
+        printer | any (\x -> (== Just (S.tag w)) (S.findTag x s)) urgents = ppUrgent
+                | S.tag w == this                                         = ppCurrent
+                | S.tag w `elem` visibles && isJust (S.stack w)           = ppVisible
+                | S.tag w `elem` visibles                                 = liftA2 fromMaybe ppVisible ppVisibleNoWindows
+                | isJust (S.stack w)                                      = ppHidden
+                | otherwise                                               = ppHiddenNoWindows
+
+pprWindowSetXinerama :: WindowSet -> String
+pprWindowSetXinerama ws = "[" ++ unwords onscreen ++ "] " ++ unwords offscreen
+  where onscreen  = map (S.tag . S.workspace)
+                        . sortOn S.screen $ S.current ws : S.visible ws
+        offscreen = map S.tag . filter (isJust . S.stack)
+                        . sortOn S.tag $ S.hidden ws
+
+-- | Wrap a string in delimiters, unless it is empty.
+wrap :: String  -- ^ left delimiter
+     -> String  -- ^ right delimiter
+     -> String  -- ^ output string
+     -> String
+wrap _ _ "" = ""
+wrap l r m  = l ++ m ++ r
+
+-- | Pad a string with a leading and trailing space.
+pad :: String -> String
+pad = wrap " " " "
+
+-- | Trim leading and trailing whitespace from a string.
+trim :: String -> String
+trim = f . f
+    where f = reverse . dropWhile isSpace
+
+-- | Limit a string to a certain length, adding "..." if truncated.
+shorten :: Int -> String -> String
+shorten = shorten' "..."
+
+-- | Limit a string to a certain length, adding @end@ if truncated.
+shorten' :: String -> Int -> String -> String
+shorten' end n xs | length xs < n = xs
+                  | otherwise     = take (n - length end) xs ++ end
+
+-- | Like 'shorten', but truncate from the left instead of right.
+shortenLeft :: Int -> String -> String
+shortenLeft = shortenLeft' "..."
+
+-- | Like 'shorten'', but truncate from the left instead of right.
+shortenLeft' :: String -> Int -> String -> String
+shortenLeft' end n xs | l < n     = xs
+                      | otherwise = end ++ drop (l - n + length end) xs
+ where l = length xs
+
+-- | Output a list of strings, ignoring empty ones and separating the
+--   rest with the given separator.
+sepBy :: String   -- ^ separator
+      -> [String] -- ^ fields to output
+      -> String
+sepBy sep = intercalate sep . filter (not . null)
+
+-- | Use dzen escape codes to output a string with given foreground
+--   and background colors.
+dzenColor :: String  -- ^ foreground color: a color name, or #rrggbb format
+          -> String  -- ^ background color
+          -> String  -- ^ output string
+          -> String
+dzenColor fg bg = wrap (fg1++bg1) (fg2++bg2)
+ where (fg1,fg2) | null fg = ("","")
+                 | otherwise = ("^fg(" ++ fg ++ ")","^fg()")
+       (bg1,bg2) | null bg = ("","")
+                 | otherwise = ("^bg(" ++ bg ++ ")","^bg()")
+
+-- | Escape any dzen metacharacters.
+dzenEscape :: String -> String
+dzenEscape = concatMap (\x -> if x == '^' then "^^" else [x])
+
+-- | Strip dzen formatting or commands.
+dzenStrip :: String -> String
+dzenStrip = strip [] where
+    strip keep x
+      | null x              = keep
+      | "^^" `isPrefixOf` x = strip (keep ++ "^") (drop 2 x)
+      | '^' == head x       = strip keep (drop 1 . dropWhile (/= ')') $ x)
+      | otherwise           = let (good,x') = span (/= '^') x
+                              in strip (keep ++ good) x'
+
+-- | Use xmobar escape codes to output a string with given foreground
+--   and background colors.
+xmobarColor :: String  -- ^ foreground color: a color name, or #rrggbb format
+            -> String  -- ^ background color
+            -> String  -- ^ output string
+            -> String
+xmobarColor fg bg = wrap t "</fc>"
+ where t = concat ["<fc=", fg, if null bg then "" else "," ++ bg, ">"]
+
+-- | Encapsulate text with an action. The text will be displayed, and the
+-- action executed when the displayed text is clicked. Illegal input is not
+-- filtered, allowing xmobar to display any parse errors. Uses xmobar's new
+-- syntax wherein the command is surrounded by backticks.
+xmobarAction :: String
+                -- ^ Command. Use of backticks (`) will cause a parse error.
+             -> String
+                -- ^ Buttons 1-5, such as "145". Other characters will cause a
+                -- parse error.
+             -> String
+                -- ^ Displayed/wrapped text.
+             -> String
+xmobarAction command button = wrap l r
+    where
+        l = "<action=`" ++ command ++ "` button=" ++ button ++ ">"
+        r = "</action>"
+
+-- | Use xmobar box to add a border to an arbitrary string.
+xmobarBorder :: String -- ^ Border type. Possible values: VBoth, HBoth, Full,
+                       -- Top, Bottom, Left or Right
+             -> String -- ^ color: a color name, or #rrggbb format
+             -> Int    -- ^ width in pixels
+             -> String -- ^ output string
+             -> String
+xmobarBorder border color width = wrap prefix "</box>"
+  where
+    prefix = "<box type=" ++ border ++ " width=" ++ show width ++ " color="
+      ++ color ++ ">"
+
+-- | Encapsulate arbitrary text for display only, i.e. untrusted content if
+-- wrapped (perhaps from window titles) will be displayed only, with all tags
+-- ignored. Introduced in xmobar 0.21; see their documentation. Be careful not
+-- to shorten the result.
+xmobarRaw :: String -> String
+xmobarRaw "" = ""
+xmobarRaw s  = concat ["<raw=", show $ length s, ":", s, "/>"]
+
+-- | Strip xmobar markup, specifically the <fc>, <icon> and <action> tags and
+-- the matching tags like </fc>.
+xmobarStrip :: String -> String
+xmobarStrip = converge (xmobarStripTags ["fc","icon","action"])
+
+converge :: (Eq a) => (a -> a) -> a -> a
+converge f a = let xs = iterate f a
+    in fst $ head $ dropWhile (uncurry (/=)) $ zip xs $ tail xs
+
+xmobarStripTags :: [String] -- ^ tags
+        -> String -> String -- ^ with all <tag>...</tag> removed
+xmobarStripTags tags = strip [] where
+    strip keep [] = keep
+    strip keep x
+        | rest: _ <- mapMaybe dropTag tags = strip keep rest
+
+
+        | '<':xs <- x = strip (keep ++ "<") xs
+        | (good,x') <- span (/= '<') x = strip (keep ++ good) x' -- this is n^2 bad... but titles have few tags
+      where dropTag :: String -> Maybe String
+            dropTag tag = msum [fmap dropTilClose (openTag tag `stripPrefix` x),
+                                                   closeTag tag `stripPrefix` x]
+
+    dropTilClose, openTag, closeTag :: String -> String
+    dropTilClose = drop 1 . dropWhile (/= '>')
+    openTag str = "<" ++ str ++ "="
+    closeTag str = "</" ++ str ++ ">"
+
+-- | Transforms a pretty-printer into one not displaying the given workspaces.
+--
+-- For example, filtering out the @NSP@ workspace before giving the 'PP' to
+-- 'dynamicLogWithPP':
+--
+-- > logHook = dynamicLogWithPP . filterOutWsPP [scratchpadWorkspaceTag] $ def
+--
+-- Here is another example, when using "XMonad.Layout.IndependentScreens".  If
+-- you have handles @hLeft@ and @hRight@ for bars on the left and right screens,
+-- respectively, and @pp@ is a pretty-printer function that takes a handle, you
+-- could write
+--
+-- > logHook = let log screen handle = dynamicLogWithPP . filterOutWsPP [scratchpadWorkspaceTag] . marshallPP screen . pp $ handle
+-- >           in log 0 hLeft >> log 1 hRight
+filterOutWsPP :: [WorkspaceId] -> PP -> PP
+filterOutWsPP ws pp = pp { ppSort = (. filterOutWs ws) <$> ppSort pp }
+
+-- | Settings to emulate dwm's statusbar, dzen only.
+dzenPP :: PP
+dzenPP = def
+  { ppCurrent         = dzenColor "white" "#2b4f98" . pad
+  , ppVisible         = dzenColor "black" "#999999" . pad
+  , ppHidden          = dzenColor "black" "#cccccc" . pad
+  , ppHiddenNoWindows = const ""
+  , ppUrgent          = dzenColor "red" "yellow" . pad
+  , ppWsSep           = ""
+  , ppSep             = ""
+  , ppLayout          = dzenColor "black" "#cccccc"
+                          . (\x -> pad $ case x of
+                              "TilePrime Horizontal" -> "TTT"
+                              "TilePrime Vertical"   -> "[]="
+                              "Hinted Full"          -> "[ ]"
+                              _                      -> x
+                            )
+  , ppTitle           = ("^bg(#324c80) " ++) . dzenEscape
+  }
+
+-- | Some nice xmobar defaults.
+xmobarPP :: PP
+xmobarPP = def { ppCurrent = xmobarColor "yellow" "" . wrap "[" "]"
+               , ppTitle   = xmobarColor "green" "" . shorten 40
+               , ppVisible = wrap "(" ")"
+               , ppUrgent  = xmobarColor "red" "yellow"
+               }
+
+-- | The options that sjanssen likes to use with xmobar, as an
+-- example.  Note the use of 'xmobarColor' and the record update on
+-- 'def'.
+sjanssenPP :: PP
+sjanssenPP = def { ppCurrent = xmobarColor "white" "black"
+                 , ppTitle   = xmobarColor "#00ee00" "" . shorten 120
+                 }
+
+-- | The options that byorgey likes to use with dzen, as another example.
+byorgeyPP :: PP
+byorgeyPP = def { ppHiddenNoWindows = showNamedWorkspaces
+                , ppHidden          = dzenColor "black" "#a8a3f7" . pad
+                , ppCurrent         = dzenColor "yellow" "#a8a3f7" . pad
+                , ppUrgent          = dzenColor "red" "yellow" . pad
+                , ppSep             = " | "
+                , ppWsSep           = ""
+                , ppTitle           = shorten 70
+                , ppOrder           = reverse
+                }
+ where
+  showNamedWorkspaces wsId =
+    if any (`elem` wsId) ['a' .. 'z'] then pad wsId else ""
diff --git a/xmonad-contrib.cabal b/xmonad-contrib.cabal
index cff18491..e8894547 100644
--- a/xmonad-contrib.cabal
+++ b/xmonad-contrib.cabal
@@ -186,6 +186,8 @@ library
                         XMonad.Hooks.Script
                         XMonad.Hooks.ServerMode
                         XMonad.Hooks.SetWMName
+                        XMonad.Hooks.StatusBar
+                        XMonad.Hooks.StatusBar.PP
                         XMonad.Hooks.ToggleHook
                         XMonad.Hooks.UrgencyHook
                         XMonad.Hooks.WallpaperSetter

From eea41cbf762e088dbc34a4624bfcdb6a80a52985 Mon Sep 17 00:00:00 2001
From: Tomas Janousek <tomi@nomi.cz>
Date: Thu, 25 Mar 2021 16:18:22 +0000
Subject: [PATCH 02/11] Sort exposed-modules in xmonad-contrib.cabal

---
 xmonad-contrib.cabal | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/xmonad-contrib.cabal b/xmonad-contrib.cabal
index e8894547..3034d094 100644
--- a/xmonad-contrib.cabal
+++ b/xmonad-contrib.cabal
@@ -93,7 +93,6 @@ library
                         XMonad.Actions.FindEmptyWorkspace
                         XMonad.Actions.FlexibleManipulate
                         XMonad.Actions.FlexibleResize
-                        XMonad.Actions.TiledWindowDragging
                         XMonad.Actions.FloatKeys
                         XMonad.Actions.FloatSnap
                         XMonad.Actions.FocusNth
@@ -116,8 +115,8 @@ library
                         XMonad.Actions.Prefix
                         XMonad.Actions.Promote
                         XMonad.Actions.RandomBackground
-                        XMonad.Actions.RotateSome
                         XMonad.Actions.RotSlaves
+                        XMonad.Actions.RotateSome
                         XMonad.Actions.Search
                         XMonad.Actions.ShowText
                         XMonad.Actions.Sift
@@ -125,9 +124,10 @@ library
                         XMonad.Actions.SinkAll
                         XMonad.Actions.SpawnOn
                         XMonad.Actions.Submap
-                        XMonad.Actions.SwapWorkspaces
                         XMonad.Actions.SwapPromote
+                        XMonad.Actions.SwapWorkspaces
                         XMonad.Actions.TagWindows
+                        XMonad.Actions.TiledWindowDragging
                         XMonad.Actions.TopicSpace
                         XMonad.Actions.TreeSelect
                         XMonad.Actions.UpdateFocus
@@ -191,9 +191,9 @@ library
                         XMonad.Hooks.ToggleHook
                         XMonad.Hooks.UrgencyHook
                         XMonad.Hooks.WallpaperSetter
+                        XMonad.Hooks.WindowSwallowing
                         XMonad.Hooks.WorkspaceByPos
                         XMonad.Hooks.WorkspaceHistory
-                        XMonad.Hooks.WindowSwallowing
                         XMonad.Hooks.XPropManage
                         XMonad.Layout.Accordion
                         XMonad.Layout.AutoMaster
@@ -213,7 +213,6 @@ library
                         XMonad.Layout.DecorationAddons
                         XMonad.Layout.DecorationMadness
                         XMonad.Layout.Dishes
-                        XMonad.Layout.MultiDishes
                         XMonad.Layout.DragPane
                         XMonad.Layout.DraggingVisualizer
                         XMonad.Layout.Drawer
@@ -253,6 +252,7 @@ library
                         XMonad.Layout.MosaicAlt
                         XMonad.Layout.MouseResizableTile
                         XMonad.Layout.MultiColumns
+                        XMonad.Layout.MultiDishes
                         XMonad.Layout.MultiToggle
                         XMonad.Layout.MultiToggle.Instances
                         XMonad.Layout.MultiToggle.TabBarDecoration
@@ -266,8 +266,8 @@ library
                         XMonad.Layout.PositionStoreFloat
                         XMonad.Layout.Reflect
                         XMonad.Layout.Renamed
-                        XMonad.Layout.ResizableTile
                         XMonad.Layout.ResizableThreeColumns
+                        XMonad.Layout.ResizableTile
                         XMonad.Layout.ResizeScreen
                         XMonad.Layout.Roledex
                         XMonad.Layout.ShowWName
@@ -348,8 +348,8 @@ library
                         XMonad.Util.Replace
                         XMonad.Util.Run
                         XMonad.Util.Scratchpad
-                        XMonad.Util.SpawnNamedPipe
                         XMonad.Util.SessionStart
+                        XMonad.Util.SpawnNamedPipe
                         XMonad.Util.SpawnOnce
                         XMonad.Util.Stack
                         XMonad.Util.StringProp

From f316d52c1c3de970f5d049628bdbba0a293e7edd Mon Sep 17 00:00:00 2001
From: Tomas Janousek <tomi@nomi.cz>
Date: Thu, 25 Mar 2021 18:24:30 +0000
Subject: [PATCH 03/11] X.H.StatusBar: Reword the $multiple section

Do not assume knowledge of what this used to be like in the past. Start
by describing how to configure multiple status bars now.
---
 XMonad/Hooks/StatusBar.hs | 57 +++++++++++++++++++--------------------
 1 file changed, 28 insertions(+), 29 deletions(-)

diff --git a/XMonad/Hooks/StatusBar.hs b/XMonad/Hooks/StatusBar.hs
index 9ee54ff2..169a4f74 100644
--- a/XMonad/Hooks/StatusBar.hs
+++ b/XMonad/Hooks/StatusBar.hs
@@ -311,14 +311,35 @@ statusBarPipe cmd xpp  = do
     h <- spawnPipe cmd
     return $ def { sbLogHook = xpp >>= \pp -> dynamicLogWithPP pp { ppOutput = hPutStrLn h } }
 
+
 -- $multiple
--- A pattern that is often found in a lot of configs that want multiple status bars,
--- generally goes something like this:
+-- 'StatusBarConfig' is a 'Monoid', which means that multiple status bars can
+-- be combined together using '<>' or 'mconcat' and passed to 'makeStatusBar'.
+--
+-- Here's an example of what such declarative configuration of multiple status
+-- bars may look like:
 --
 -- > main = do
--- >   xmproc0 <- spawnPipe "xmobar -x 0 $HOME/.config/xmobar/xmobarrc_top"
--- >   xmproc1 <- spawnPipe "xmobar -x 0 $HOME/.config/xmobar/xmobarrc_bottom"
--- >   xmproc2 <- spawnPipe "xmobar -x 1 $HOME/.config/xmobar/xmobarrc1"
+-- >   xmobarTop    <- statusBarPipe "xmobar -x 0 ~/.config/xmobar/xmobarrc_top"    (pure ppTop)
+-- >   xmobarBottom <- statusBarPipe "xmobar -x 0 ~/.config/xmobar/xmobarrc_bottom" (pure ppBottom)
+-- >   xmobar1      <- statusBarPipe "xmobar -x 1 ~/.config/xmobar/xmobarrc1"       (pure pp1)
+-- >   xmonad =<< makeStatusBar (xmobarTop <> xmobarBottom <> xmobar1) myConfig
+--
+-- The above example also works if the different status bars support different
+-- logging methods: you could mix property logging and logging via standard input.
+-- One thing to keep in mind: 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.
+--
+-- 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 }
@@ -327,30 +348,8 @@ statusBarPipe cmd xpp  = do
 -- >     ...
 -- >   }
 --
--- Which has a lot of boilerplate and is error-prone. By using the new interface, the
--- config becomes more declarative and there's much less room for errors. You use it
--- by creating the suitable status bar configs and combining them with '<>':
---
--- > main = do
--- >   xmobarTop    <- statusBarPipe "xmobar -x 0 $HOME/.config/xmobar/xmobarrc_top"    (pure ppTop)
--- >   xmobarBottom <- statusBarPipe "xmobar -x 0 $HOME/.config/xmobar/xmobarrc_bottom" (pure ppBottom)
--- >   xmobar1      <- statusBarPipe "xmobar -x 1 $HOME/.config/xmobar/xmobarrc1"       (pure pp1)
--- >   xmonad =<< makeStatusBar (xmobarTop <> xmobarBottom <> xmobar1) myConfig
---
--- Or if you're feeling adventurous:
---
--- > myBars = map (uncurry statusBarPipe) [ ("xmobar -x 0 $HOME/.config/xmobar/xmobarrc_top",    pure ppTop)
--- >                                              , ("xmobar -x 0 $HOME/.config/xmobar/xmobarrc_bottom", pure ppBottom)
--- >                                              , ("xmobar -x 1 $HOME/.config/xmobar/xmobarrc1",       pure pp1) ]
--- > main = do
--- >   sbs <- sequence myBars
--- >   xmonad =<< makeStatusBar (mconcat sbs) myConfig
---
--- The above examples also work if the different status bars support different
--- logging methods: you could do mix property logging and logging via standard input.
--- One thing to keep in mind: 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.
+-- By using the new interface, the config becomes more declarative and there's
+-- less room for errors.
 
 
 -- | The default property xmonad writes to. (@_XMONAD_LOG@).

From 2865268a7a5a32eebc2f71da22a14d6426cca0b2 Mon Sep 17 00:00:00 2001
From: Tomas Janousek <tomi@nomi.cz>
Date: Thu, 25 Mar 2021 22:24:04 +0000
Subject: [PATCH 04/11] Improve descriptions of
 X.H.{DynamicLog,StatusBar,StatusBar.PP}

---
 XMonad/Hooks/DynamicLog.hs   | 12 ++++++++++--
 XMonad/Hooks/StatusBar.hs    | 13 ++++++++++---
 XMonad/Hooks/StatusBar/PP.hs | 11 +++++++++--
 3 files changed, 29 insertions(+), 7 deletions(-)

diff --git a/XMonad/Hooks/DynamicLog.hs b/XMonad/Hooks/DynamicLog.hs
index f61ec0ae..bc260dc7 100644
--- a/XMonad/Hooks/DynamicLog.hs
+++ b/XMonad/Hooks/DynamicLog.hs
@@ -10,6 +10,16 @@
 -- Stability   :  unstable
 -- Portability :  unportable
 --
+-- __Note:__ This module is a __compatibility wrapper__ for the following:
+--
+-- * "XMonad.Hooks.StatusBar"
+-- * "XMonad.Hooks.StatusBar.PP"
+--
+-- DynamicLog API is frozen and users are encouraged to migrate to these
+-- modern replacements.
+--
+-- /Original description and documentation follows:/
+--
 -- 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.  DynamicLog
@@ -65,8 +75,6 @@ import XMonad.Hooks.StatusBar.PP
 import XMonad.Hooks.StatusBar
 
 -- $usage
--- The API for this module is frozen, use "XMonad.Hooks.StatusBar" instead.
---
 -- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
 --
 -- >    import XMonad
diff --git a/XMonad/Hooks/StatusBar.hs b/XMonad/Hooks/StatusBar.hs
index 169a4f74..666f4fc4 100644
--- a/XMonad/Hooks/StatusBar.hs
+++ b/XMonad/Hooks/StatusBar.hs
@@ -9,9 +9,16 @@
 -- Stability   :  unstable
 -- Portability :  unportable
 --
--- This module provides a new interface that replaces "XMonad.Hooks.DynamicLog",
--- by providing composoble and dynamic status bars. Supports property-based as well
--- as pipe-based status bars.
+-- 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.
 --
 -----------------------------------------------------------------------------
 
diff --git a/XMonad/Hooks/StatusBar/PP.hs b/XMonad/Hooks/StatusBar/PP.hs
index c30b6d8f..e7f907a3 100644
--- a/XMonad/Hooks/StatusBar/PP.hs
+++ b/XMonad/Hooks/StatusBar/PP.hs
@@ -9,8 +9,15 @@
 -- Stability   :  unstable
 -- Portability :  unportable
 --
--- This module provides the pretty-printing abstraction and utilities that
--- can be used inside the 'logHook'.
+-- 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 pretty-printing abstraction and utilities that can
+-- be used to customize what is logged to a status bar. See
+-- "XMonad.Hooks.StatusBar" for an abstraction over starting these status
+-- bars. Together these are a modern replacement for
+-- "XMonad.Hooks.DynamicLog", which is now just a compatibility wrapper.
 --
 -----------------------------------------------------------------------------
 

From e32e17aa2da3bcf73734610948c874a9c91ac18e Mon Sep 17 00:00:00 2001
From: Tomas Janousek <tomi@nomi.cz>
Date: Thu, 25 Mar 2021 22:16:53 +0000
Subject: [PATCH 05/11] X.U.ClickableWorkspaces: Update docs for X.H.StatusBar

---
 XMonad/Util/ClickableWorkspaces.hs | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/XMonad/Util/ClickableWorkspaces.hs b/XMonad/Util/ClickableWorkspaces.hs
index 18916e18..24ccf1fd 100644
--- a/XMonad/Util/ClickableWorkspaces.hs
+++ b/XMonad/Util/ClickableWorkspaces.hs
@@ -8,8 +8,8 @@
 -- Stability   :  unstable
 -- Portability :  unportable
 --
--- Provides @clickablePP@, which when applied to the PP pretty-printer used by
--- the "XMonad.Hooks.DynamicLog" hook, will make the workspace tags clickable in
+-- Provides @clickablePP@, which when applied to the 'PP' pretty-printer used
+-- by "XMonad.Hooks.StatusBar" will make the workspace tags clickable in
 -- XMobar (for switching focus).
 --
 -----------------------------------------------------------------------------
@@ -25,17 +25,22 @@ import Control.Monad ((>=>))
 import Data.Functor ((<&>))
 
 import XMonad
-import XMonad.Hooks.DynamicLog (xmobarAction, PP(..))
+import XMonad.Hooks.StatusBar.PP (xmobarAction, PP(..))
 import XMonad.Util.WorkspaceCompare (getWsIndex)
 import qualified XMonad.StackSet as W
 
 -- $usage
--- However you have set up your PP, apply @clickablePP@ to it, and bind the result
--- to "XMonad.Hooks.DynamicLog"\'s dynamicLogWithPP like so:
+-- If you're using the "XMonad.Hooks.StatusBar" interface, apply 'clickablePP'
+-- to the 'PP' passed to 'XMonad.Hooks.StatusBar.statusBarProp':
+--
+-- > mySB <- statusBarProp "xmobar" (clickablePP xmobarPP)
+--
+-- Or if you're using the old "XMonad.Hooks.DynamicLog" interface:
 --
 -- > logHook = clickablePP xmobarPP { ... } >>= dynamicLogWithPP
 --
--- * Requirements:
+-- Requirements:
+--
 --   * @xdotool@ on system (in path)
 --   * "XMonad.Hooks.EwmhDesktops" for @xdotool@ support (see Hackage docs for setup)
 --   * use of UnsafeStdinReader/UnsafeXMonadLog in xmobarrc (rather than StdinReader/XMonadLog)

From 4e1b277784ce25b23df7d77866134465f9574307 Mon Sep 17 00:00:00 2001
From: Tomas Janousek <tomi@nomi.cz>
Date: Thu, 25 Mar 2021 22:20:42 +0000
Subject: [PATCH 06/11] X.H.StatusBar: Clean up usage documentation

---
 XMonad/Hooks/DynamicLog.hs |  2 +-
 XMonad/Hooks/StatusBar.hs  | 80 ++++++++++++++++----------------------
 2 files changed, 34 insertions(+), 48 deletions(-)

diff --git a/XMonad/Hooks/DynamicLog.hs b/XMonad/Hooks/DynamicLog.hs
index bc260dc7..cd6e7e6b 100644
--- a/XMonad/Hooks/DynamicLog.hs
+++ b/XMonad/Hooks/DynamicLog.hs
@@ -204,7 +204,7 @@ statusBar cmd pp k conf= do
 -- Helper function which provides ToggleStruts keybinding
 --
 toggleStrutsKey :: XConfig t -> (KeyMask, KeySym)
-toggleStrutsKey XConfig{modMask = modm} = (modm, xK_b )
+toggleStrutsKey = defToggleStrutsKey
 
 -- |
 -- Workspace logger with a format designed for Xinerama:
diff --git a/XMonad/Hooks/StatusBar.hs b/XMonad/Hooks/StatusBar.hs
index 666f4fc4..2589c6f1 100644
--- a/XMonad/Hooks/StatusBar.hs
+++ b/XMonad/Hooks/StatusBar.hs
@@ -28,12 +28,13 @@ module XMonad.Hooks.StatusBar (
   StatusBarConfig(..),
   makeStatusBar,
   makeStatusBar',
+  defToggleStrutsKey,
 
   -- * Available Configs
   -- $availableconfigs
-  statusBarPipe,
   statusBarProp,
   statusBarPropTo,
+  statusBarPipe,
 
   -- * Multiple Status Bars
   -- $multiple
@@ -46,6 +47,9 @@ module XMonad.Hooks.StatusBar (
   -- * Managing Status Bar Processes
   spawnStatusBarAndRemember,
   cleanupStatusBars,
+
+  -- * Manual Plumbing
+  -- $plumbing
   ) where
 
 import Control.Exception (SomeException, try)
@@ -74,22 +78,25 @@ import XMonad.Hooks.StatusBar.PP
 -- >    import XMonad.Hooks.StatusBar
 -- >    import XMonad.Hooks.StatusBar.PP
 --
--- The recommended way to use this module with xmobar, as well as any other
+-- The easiest way to use this module with xmobar, as well as any other
 -- status bar that supports property logging (you can read more about X11
 -- properties
 -- [here](https://en.wikipedia.org/wiki/X_Window_System_core_protocol#Properties)
 -- or
 -- [here](https://tronche.com/gui/x/xlib/window-information/properties-and-atoms.html),
 -- although you don't have to understand them in order to use the functions
--- below), is to use 'statusBarProp' with 'makeStatusBar', which takes care of
--- the necessary plumbing-no shell scripting required!
+-- below), is to use 'statusBarProp' with 'makeStatusBar'', which takes care of
+-- the necessary plumbing:
 --
 -- > main = do
--- >   mySB <- statusBarProp "xmobar" (pure myPP)
--- >   xmonad =<< makeStatusBar mySB myConf
+-- >   mySB <- statusBarProp "xmobar" (pure xmobarPP)
+-- >   xmonad =<< makeStatusBar' mySB defToggleStrutsKey def
 --
--- which plays nice with other combinators that you might have already
--- in your config:
+-- Most users will, however, want to customize the logging and integrate it
+-- into their existing custom xmonad configuration. The 'makeStatusBar'
+-- function is more appropriate in this case: it doesn't touch your
+-- keybindings, layout modifiers, or event hooks. You're expected to configure
+-- "XMonad.Hooks.ManageDocks" yourself. Here's what that might look like:
 --
 -- > main = do
 -- >   mySB <- statusBarProp "xmobar" (pure myPP)
@@ -123,7 +130,9 @@ import XMonad.Hooks.StatusBar.PP
 -- 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.
---
+
+
+-- $plumbing
 -- If you do not want to use any of the "batteries included" functions above,
 -- you can also add all of the necessary plumbing yourself (the source of
 -- 'makeStatusBar' might come in handy here).
@@ -185,27 +194,7 @@ import XMonad.Hooks.StatusBar.PP
 --
 -- If you use a status bar that does not support reading from a property log
 -- (like dzen), and you don't want to use the 'statusBar' function, you can,
--- again, also manually add all of the required components.
---
--- This works much like the property based solution above, just that you will
--- want to use 'dynamicLog' or 'dynamicLogXinerama' in place of 'xmonadPropLog'.
---
--- > main = xmonad $ def {
--- >    ...
--- >    , logHook = dynamicLog
--- >    ...
--- >    }
---
--- For more flexibility, you can also use 'dynamicLogWithPP' and supply your own
--- pretty-printing format (by either defining one from scratch, or customizing
--- one of the provided examples).  For example:
---
--- >    -- use sjanssen's pretty-printer format, but with the sections
--- >    -- in reverse
--- >    logHook = dynamicLogWithPP $ sjanssenPP { ppOrder = reverse }
---
--- Again, you will have to do all the necessary plumbing yourself.  In addition,
--- you are also responsible for creating a pipe for you status bar to read from:
+-- again, also manually add all of the required components, like this:
 --
 -- > import XMonad.Util.Run (hPutStrLn, spawnPipe)
 -- >
@@ -218,19 +207,14 @@ import XMonad.Hooks.StatusBar.PP
 -- >       }
 --
 -- In the above, note that if you use @spawnPipe@ you need to redefine the
--- 'ppOutput' field of your pretty-printer, as was done in the example above; by
--- default the status will be printed to stdout rather than the pipe you create.
---
--- The status bars are managed through the 'StatusBarConfig', which provides
--- a convenient abstraction over what a status bar is and how to manage it.
--- This modules provides how to create these status bar configs, and how to
--- incorporate them in your xmonad config: using 'makeStatusBar'
--- or 'makeStatusBar''.
---
--- The difference between 'makeStatusBar' and 'makeStatusBar'' is that 'makeStatusBar'
--- tries to stay out of your way, whereas 'makeStatusBar'' configures an
--- extra keybinding to toggle the status bars, and also applies the
--- 'avoidStruts' layout modifier as well as the 'docks' combinator.
+-- 'ppOutput' field of your pretty-printer; by default the status will be
+-- printed to stdout rather than the pipe you create. This was meant to be
+-- used together with running xmonad piped to a status bar like so: @xmonad |
+-- dzen2@, and is what the old 'XMonad.Hooks.DynamicLog.dynamicLog' assumes,
+-- but it isn't recommended in modern setups. Applications launched from
+-- xmonad inherit its stdout and stderr, and will print their own garbage to
+-- the status bar.
+
 
 -- | This datataype abstracts a status bar to provide a common interface
 -- functions like 'statusBarPipe' or 'statusBarProp'. Once defined, a status
@@ -241,9 +225,7 @@ data StatusBarConfig = StatusBarConfig  { sbLogHook     :: X ()
                                         , sbStartupHook :: X ()
                                         -- ^ How to start the status bar.
                                         , sbCleanupHook :: X ()
-                                        -- ^ How to kill the status bar when xmonad is restarted.
-                                        -- This is useful when the status bar is not started
-                                        -- with a pipe.
+                                        -- ^ How to kill the status bar.
                                         }
 
 instance Semigroup StatusBarConfig where
@@ -292,6 +274,10 @@ makeStatusBar' sb k conf = do
                          }
   where keys' = (`M.singleton` sendMessage ToggleStruts) . k
 
+-- | Default @mod-b@ key binding for 'makeStatusBar''
+defToggleStrutsKey :: XConfig t -> (KeyMask, KeySym)
+defToggleStrutsKey XConfig{modMask = modm} = (modm, xK_b)
+
 -- | Creates a 'StatusBarConfig' that uses property logging to @_XMONAD_LOG@, which
 -- is set in 'xmonadDefProp'
 statusBarProp :: String -- ^ The command line to launch the status bar
@@ -333,7 +319,7 @@ statusBarPipe cmd xpp  = do
 -- >   xmonad =<< makeStatusBar (xmobarTop <> xmobarBottom <> xmobar1) myConfig
 --
 -- The above example also works if the different status bars support different
--- logging methods: you could mix property logging and logging via standard input.
+-- logging methods: you could mix property logging and logging via pipes.
 -- One thing to keep in mind: 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.

From 8e39d22cec9c71c7a4c97e8a14ea50d82896d0b6 Mon Sep 17 00:00:00 2001
From: Tomas Janousek <tomi@nomi.cz>
Date: Thu, 25 Mar 2021 22:45:42 +0000
Subject: [PATCH 07/11] X.H.StatusBar.PP: Move dynamicLog to DynamicLog

This function isn't useful in the new interface, ditch it.
---
 XMonad/Hooks/DynamicLog.hs   | 13 +++++++++++++
 XMonad/Hooks/StatusBar/PP.hs | 20 ++++++--------------
 2 files changed, 19 insertions(+), 14 deletions(-)

diff --git a/XMonad/Hooks/DynamicLog.hs b/XMonad/Hooks/DynamicLog.hs
index cd6e7e6b..49387b33 100644
--- a/XMonad/Hooks/DynamicLog.hs
+++ b/XMonad/Hooks/DynamicLog.hs
@@ -206,6 +206,19 @@ statusBar cmd pp k conf= do
 toggleStrutsKey :: XConfig t -> (KeyMask, KeySym)
 toggleStrutsKey = defToggleStrutsKey
 
+-- | An example log hook, which prints status information to stdout in
+-- the default format:
+--
+-- > 1 2 [3] 4 7 : full : title
+--
+-- That is, the currently populated workspaces, the current
+-- workspace layout, and the title of the focused window.
+--
+-- To customize the output format, see 'dynamicLogWithPP'.
+--
+dynamicLog :: X ()
+dynamicLog = dynamicLogWithPP def
+
 -- |
 -- Workspace logger with a format designed for Xinerama:
 --
diff --git a/XMonad/Hooks/StatusBar/PP.hs b/XMonad/Hooks/StatusBar/PP.hs
index e7f907a3..0b304d94 100644
--- a/XMonad/Hooks/StatusBar/PP.hs
+++ b/XMonad/Hooks/StatusBar/PP.hs
@@ -27,7 +27,6 @@ module XMonad.Hooks.StatusBar.PP (
 
     -- * Build your own formatter
     PP(..), def,
-    dynamicLog,
     dynamicLogString,
     dynamicLogWithPP,
 
@@ -136,7 +135,12 @@ data PP = PP { ppCurrent :: WorkspaceId -> String
                -- formatting.
              }
 
--- | The default pretty printing options, as seen in 'dynamicLog'.
+-- | The default pretty printing options:
+--
+-- > 1 2 [3] 4 7 : full : title
+--
+-- That is, the currently populated workspaces, the current
+-- workspace layout, and the title of the focused window.
 instance Default PP where
   def = PP { ppCurrent          = wrap "[" "]"
            , ppVisible          = wrap "<" ">"
@@ -156,18 +160,6 @@ instance Default PP where
            , ppSort             = getSortByIndex
            , ppExtras           = []
            }
--- | An example log hook, which prints status information to stdout in
--- the default format:
---
--- > 1 2 [3] 4 7 : full : title
---
--- That is, the currently populated workspaces, the current
--- workspace layout, and the title of the focused window.
---
--- To customize the output format, see 'dynamicLogWithPP'.
---
-dynamicLog :: X ()
-dynamicLog = dynamicLogWithPP def
 
 -- | Format the current status using the supplied pretty-printing format,
 --   and write it to stdout.

From b9913bd4df378e6750f908e144de41d011e768ce Mon Sep 17 00:00:00 2001
From: Yecine Megdiche <yecine.megdiche@gmail.com>
Date: Sat, 27 Mar 2021 22:39:37 +0100
Subject: [PATCH 08/11] Cleanup CHANGES.md

Adapted mentions of `XMonad.Hooks.DynamicLog`, and moved the changes in
`XMonad.Hooks.DynamicLog` to the appropriate entries
---
 CHANGES.md | 53 ++++++++++++++++++++++++++++++-----------------------
 1 file changed, 30 insertions(+), 23 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 9e6cc7b1..c231001b 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -50,7 +50,8 @@
     - The type of `getWorkspaceNames` was changed to fit into the new `ppRename`
       field of `PP`.
 
-  * `XMonad.Hooks.DynamicLog` and `XMonad.Util.Run`
+  * `XMonad.Hooks.StatusBar`, `XMonad.Hooks.StatusBar.PP` (previously
+    `XMonad.Hooks.DynamicLog`) and `XMonad.Util.Run`
 
     - `spawnPipe` no longer uses binary mode handles but defaults to the
       current locale encoding instead.
@@ -77,9 +78,31 @@
     Originally contained inside `XMonad.Hooks.DynamicLog`, this module provides the
     pretty-printing abstraction and utilities, used primarly with `logHook`.
 
+    Below are changes from `XMonad.Hooks.DynamicLog`, that now are included in
+    this module:
+
+    - Added `shortenLeft` function, like existing `shorten` but shortens by
+      truncating from left instead of right. Useful for showing directories.
+
+    - Added `shorten'` and `shortenLeft'` functions with customizable overflow
+      markers.
+
+    - Added `filterOutWsPP` for filtering out certain workspaces from being
+      displayed.
+
+    - Added `ppTitleUnfocused` to `PP` for showing unfocused windows on
+      the current workspace in the status bar.
+
+    - Added `xmobarBorder` function to create borders around strings.
+
+    - Added `ppRename` to `PP`, which makes it possible for extensions like
+      `workspaceNamesPP`, `marshallPP` and/or `clickablePP` to compose
+      intuitively.
+
+
   * `XMonad.Hooks.StatusBar`
 
-    This module provides a new interface that replaces "XMonad.Hooks.DynamicLog",
+    This module provides a new interface that replaces `XMonad.Hooks.DynamicLog`,
     by providing composoble status bars. Supports property-based as well
     as pipe-based status bars.
 
@@ -161,8 +184,8 @@
   * `XMonad.Util.ClickableWorkspaces`
 
     Provides `clickablePP`, which when applied to the `PP` pretty-printer used by
-    `XMonad.Hooks.DynamicLog.dynamicLogWithPP`, will make the workspace tags
-    clickable in XMobar (for switching focus).
+    `XMonad.Hooks.StatusBar.PP`, will make the workspace tags clickable in XMobar
+    (for switching focus).
 
   * `XMonad.Layout.VoidBorders`
 
@@ -240,28 +263,12 @@
 
   * `XMonad.Hooks.DynamicLog`
 
-    - Added `shortenLeft` function, like existing `shorten` but shortens by
-      truncating from left instead of right. Useful for showing directories.
-
-    - Added `shorten'` and `shortenLeft'` functions with customizable overflow
-      markers.
-
-    - Added `filterOutWsPP` for filtering out certain workspaces from being
-      displayed.
-
     - Added `xmobarProp`, for property-based alternative to `xmobar`.
-
-    - Reworked the module documentation to suggest property-based logging
-      instead of pipe-based logging, due to the various issues associated with
-      the latter.
-
-    - Added `ppTitleUnfocused` to `PP` for showing unfocused windows on
-      the current workspace in the status bar.
-
-    - Added `xmobarBorder` function to create borders around strings.
-
+    
     - Add the -dock argument to the dzen spawn arguments
 
+    - The API for this module is frozen: this is now a compatibility wrapper.
+
   * `XMonad.Layout.BoringWindows`
 
     - Added boring-aware `swapUp`, `swapDown`, `siftUp`, and `siftDown` functions.

From 3e11bae4b1c58ede9dc87559641314ca0bcdb238 Mon Sep 17 00:00:00 2001
From: slotThe <soliditsallgood@mailbox.org>
Date: Mon, 29 Mar 2021 13:22:11 +0200
Subject: [PATCH 09/11] X.H.StatusBar: Doc suggestions

---
 XMonad/Hooks/StatusBar.hs | 54 ++++++++++++++++++++-------------------
 1 file changed, 28 insertions(+), 26 deletions(-)

diff --git a/XMonad/Hooks/StatusBar.hs b/XMonad/Hooks/StatusBar.hs
index 2589c6f1..ac6b3d62 100644
--- a/XMonad/Hooks/StatusBar.hs
+++ b/XMonad/Hooks/StatusBar.hs
@@ -16,7 +16,7 @@
 -- 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
+-- 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.
 --
@@ -79,31 +79,33 @@ import XMonad.Hooks.StatusBar.PP
 -- >    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 (you can read more about X11
--- properties
--- [here](https://en.wikipedia.org/wiki/X_Window_System_core_protocol#Properties)
--- or
--- [here](https://tronche.com/gui/x/xlib/window-information/properties-and-atoms.html),
--- although you don't have to understand them in order to use the functions
--- below), is to use 'statusBarProp' with 'makeStatusBar'', which takes care of
--- the necessary plumbing:
+-- status bar that supports property logging, is to use 'statusBarProp'
+-- with 'makeStatusBar''; these take care of the necessary plumbing:
 --
 -- > main = do
 -- >   mySB <- statusBarProp "xmobar" (pure xmobarPP)
 -- >   xmonad =<< makeStatusBar' mySB defToggleStrutsKey def
 --
+-- You can read more about X11 properties
+-- [here](https://en.wikipedia.org/wiki/X_Window_System_core_protocol#Properties)
+-- or
+-- [here](https://tronche.com/gui/x/xlib/window-information/properties-and-atoms.html),
+-- 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 'makeStatusBar'
 -- function is more appropriate in this case: it doesn't touch your
--- keybindings, layout modifiers, or event hooks. You're expected to configure
--- "XMonad.Hooks.ManageDocks" yourself. Here's what that might look like:
+-- keybindings, layout modifiers, or event hooks; instead, you're expected
+-- to configure "XMonad.Hooks.ManageDocks" yourself. Here's what that might
+-- look like:
 --
 -- > main = do
 -- >   mySB <- statusBarProp "xmobar" (pure myPP)
 -- >   xmonad =<< (makeStatusBar 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 simply achieved by using
+-- 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 { ...
@@ -113,7 +115,7 @@ import XMonad.Hooks.StatusBar.PP
 --
 -- 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
--- otherwise!
+-- 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,
@@ -124,12 +126,12 @@ import XMonad.Hooks.StatusBar.PP
 --
 -- 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 will now
--- 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.
+-- 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.
+-- discouraged: if anything goes wrong with the bar, xmonad will freeze!
 
 
 -- $plumbing
@@ -139,7 +141,7 @@ import XMonad.Hooks.StatusBar.PP
 --
 -- 'xmonadPropLog' allows you to write a string to the @_XMONAD_LOG@ property of
 -- the root window.  Together with 'dynamicLogString', you can now simply set
--- your 'logHook' to the appropriate function, for instance
+-- your 'logHook' to the appropriate function; for instance:
 --
 -- > main = xmonad $ def {
 -- >    ...
@@ -183,16 +185,16 @@ import XMonad.Hooks.StatusBar.PP
 -- care of the necessary plumbing /and/ keeps track of the started status bars, so
 -- they can be correctly restarted with xmonad. This is achieved using
 -- 'spawnStatusBarAndRemember' to start them and 'cleanupStatusBars' to kill
--- previously started ones.
+-- previously started bars.
 --
--- Even if you don't use a statusbar, you can still use 'dynamicLogString' to
+-- Even if you don't use a status bar, you can still use 'dynamicLogString' to
 -- show on-screen notifications in response to some events. For example, to show
 -- the current layout when it changes, you could make a keybinding to cycle the
 -- layout and display the current status:
 --
--- > ((mod1Mask, xK_a), sendMessage NextLayout >> (dynamicLogString myPP >>= \d->spawn $"xmessage "++d))
+-- > ((mod1Mask, xK_a), sendMessage NextLayout >> (dynamicLogString myPP >>= \d -> spawn $ "xmessage " ++ d))
 --
--- If you use a status bar that does not support reading from a property log
+-- If you use a status bar that does not support reading from a property
 -- (like dzen), and you don't want to use the 'statusBar' function, you can,
 -- again, also manually add all of the required components, like this:
 --
@@ -320,9 +322,9 @@ statusBarPipe cmd xpp  = do
 --
 -- 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: 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.
+-- 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.
 --
 -- Long-time xmonad users will note that the above config is equivalent to
 -- the following less robust and more verbose configuration that they might

From 06fd90a5f825c2d620b13e96a85ae4d8381c4f46 Mon Sep 17 00:00:00 2001
From: Yecine Megdiche <yecine.megdiche@gmail.com>
Date: Fri, 2 Apr 2021 18:12:55 +0200
Subject: [PATCH 10/11] Rename makeStatusBar to withSB, makeStatusBar' to
 withEasySB

---
 XMonad/Hooks/DynamicLog.hs |  4 +--
 XMonad/Hooks/StatusBar.hs  | 52 +++++++++++++++++++-------------------
 2 files changed, 28 insertions(+), 28 deletions(-)

diff --git a/XMonad/Hooks/DynamicLog.hs b/XMonad/Hooks/DynamicLog.hs
index 49387b33..66b5a134 100644
--- a/XMonad/Hooks/DynamicLog.hs
+++ b/XMonad/Hooks/DynamicLog.hs
@@ -198,7 +198,7 @@ statusBar :: LayoutClass l Window
           -> IO (XConfig (ModifiedLayout AvoidStruts l))
 statusBar cmd pp k conf= do
   sb <- statusBarPipe cmd (pure pp)
-  makeStatusBar' sb k conf
+  withEasySB sb k conf
 
 -- |
 -- Helper function which provides ToggleStruts keybinding
@@ -263,4 +263,4 @@ xmobarProp :: LayoutClass l Window
            -> IO (XConfig (ModifiedLayout AvoidStruts l))
 xmobarProp conf = do
     xmobarPropConfig <- statusBarProp "xmobar" (pure xmobarPP)
-    makeStatusBar' xmobarPropConfig toggleStrutsKey conf
+    withEasySB xmobarPropConfig toggleStrutsKey conf
diff --git a/XMonad/Hooks/StatusBar.hs b/XMonad/Hooks/StatusBar.hs
index ac6b3d62..b4f5d7fa 100644
--- a/XMonad/Hooks/StatusBar.hs
+++ b/XMonad/Hooks/StatusBar.hs
@@ -26,8 +26,8 @@ module XMonad.Hooks.StatusBar (
   -- * Usage
   -- $usage
   StatusBarConfig(..),
-  makeStatusBar,
-  makeStatusBar',
+  withSB,
+  withEasySB,
   defToggleStrutsKey,
 
   -- * Available Configs
@@ -80,11 +80,11 @@ 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 'makeStatusBar''; these take care of the necessary plumbing:
+-- with 'withEasySB'; these take care of the necessary plumbing:
 --
 -- > main = do
 -- >   mySB <- statusBarProp "xmobar" (pure xmobarPP)
--- >   xmonad =<< makeStatusBar' mySB defToggleStrutsKey def
+-- >   xmonad =<< withEasySB mySB defToggleStrutsKey def
 --
 -- You can read more about X11 properties
 -- [here](https://en.wikipedia.org/wiki/X_Window_System_core_protocol#Properties)
@@ -94,7 +94,7 @@ import XMonad.Hooks.StatusBar.PP
 -- mentioned above.
 --
 -- Most users will, however, want to customize the logging and integrate it
--- into their existing custom xmonad configuration. The 'makeStatusBar'
+-- 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
@@ -102,7 +102,7 @@ import XMonad.Hooks.StatusBar.PP
 --
 -- > main = do
 -- >   mySB <- statusBarProp "xmobar" (pure myPP)
--- >   xmonad =<< (makeStatusBar mySB . ewmh . docks $ def {...})
+-- >   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
@@ -137,7 +137,7 @@ import XMonad.Hooks.StatusBar.PP
 -- $plumbing
 -- If you do not want to use any of the "batteries included" functions above,
 -- you can also add all of the necessary plumbing yourself (the source of
--- 'makeStatusBar' might come in handy here).
+-- 'withSB' might come in handy here).
 --
 -- 'xmonadPropLog' allows you to write a string to the @_XMONAD_LOG@ property of
 -- the root window.  Together with 'dynamicLogString', you can now simply set
@@ -220,8 +220,8 @@ import XMonad.Hooks.StatusBar.PP
 
 -- | 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 'makeStatusBar' or
--- 'makeStatusBar'', which take care of the necessary plumbing.
+-- bar can be incorporated in 'XConfig' by using 'withSB' or
+-- 'withEasySB', which take care of the necessary plumbing.
 data StatusBarConfig = StatusBarConfig  { sbLogHook     :: X ()
                                         -- ^ What and how to log to the status bar.
                                         , sbStartupHook :: X ()
@@ -247,36 +247,36 @@ instance Default StatusBarConfig where
 -- 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.
-makeStatusBar :: LayoutClass l Window
-              => StatusBarConfig    -- ^ The status bar config
-              -> XConfig l          -- ^ The base config
-              -> IO (XConfig l)
-makeStatusBar (StatusBarConfig lh sh ch) conf =
+withSB :: LayoutClass l Window
+       => StatusBarConfig    -- ^ The status bar config
+       -> XConfig l          -- ^ The base config
+       -> IO (XConfig l)
+withSB (StatusBarConfig lh sh ch) conf =
   return $ conf
     { logHook     = logHook conf *> lh
     , startupHook = startupHook conf *> ch *> sh
     }
 
--- | Like 'makeStatusBar', but takes an extra key to toggle struts. It also
+-- | 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.
-makeStatusBar' :: LayoutClass l Window
-               => StatusBarConfig -- ^ The status bar config
-               -> (XConfig Layout -> (KeyMask, KeySym))
-                                  -- ^ The key binding
-               -> XConfig l       -- ^ The base config
-               -> IO (XConfig (ModifiedLayout AvoidStruts l))
-makeStatusBar' sb k conf = do
-  conf' <- makeStatusBar sb conf
+withEasySB :: LayoutClass l Window
+           => StatusBarConfig -- ^ The status bar config
+           -> (XConfig Layout -> (KeyMask, KeySym))
+                              -- ^ The key binding
+           -> XConfig l       -- ^ The base config
+           -> IO (XConfig (ModifiedLayout AvoidStruts l))
+withEasySB sb k conf = do
+  conf' <- withSB sb conf
   return $ docks $ conf' { layoutHook = avoidStruts (layoutHook conf')
                          , keys       = (<>) <$> keys' <*> keys conf'
                          }
   where keys' = (`M.singleton` sendMessage ToggleStruts) . k
 
--- | Default @mod-b@ key binding for 'makeStatusBar''
+-- | Default @mod-b@ key binding for 'withEasySB'
 defToggleStrutsKey :: XConfig t -> (KeyMask, KeySym)
 defToggleStrutsKey XConfig{modMask = modm} = (modm, xK_b)
 
@@ -309,7 +309,7 @@ statusBarPipe cmd xpp  = do
 
 -- $multiple
 -- 'StatusBarConfig' is a 'Monoid', which means that multiple status bars can
--- be combined together using '<>' or 'mconcat' and passed to 'makeStatusBar'.
+-- 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:
@@ -318,7 +318,7 @@ statusBarPipe cmd xpp  = do
 -- >   xmobarTop    <- statusBarPipe "xmobar -x 0 ~/.config/xmobar/xmobarrc_top"    (pure ppTop)
 -- >   xmobarBottom <- statusBarPipe "xmobar -x 0 ~/.config/xmobar/xmobarrc_bottom" (pure ppBottom)
 -- >   xmobar1      <- statusBarPipe "xmobar -x 1 ~/.config/xmobar/xmobarrc1"       (pure pp1)
--- >   xmonad =<< makeStatusBar (xmobarTop <> xmobarBottom <> xmobar1) myConfig
+-- >   xmonad =<< withSB (xmobarTop <> xmobarBottom <> xmobar1) myConfig
 --
 -- The above example also works if the different status bars support different
 -- logging methods: you could mix property logging and logging via pipes.

From a1c2d144b3987053a3cfe16d7fdd632b535c7f04 Mon Sep 17 00:00:00 2001
From: Tomas Janousek <tomi@nomi.cz>
Date: Fri, 2 Apr 2021 21:28:41 +0100
Subject: [PATCH 11/11] Final touch of DynamicLog/StatusBar/PP docs

---
 XMonad/Hooks/DynamicLog.hs   |  4 ++--
 XMonad/Hooks/StatusBar.hs    |  6 +++---
 XMonad/Hooks/StatusBar/PP.hs | 11 +++++++----
 3 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/XMonad/Hooks/DynamicLog.hs b/XMonad/Hooks/DynamicLog.hs
index 66b5a134..e0db8952 100644
--- a/XMonad/Hooks/DynamicLog.hs
+++ b/XMonad/Hooks/DynamicLog.hs
@@ -77,8 +77,8 @@ import XMonad.Hooks.StatusBar
 -- $usage
 -- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
 --
--- >    import XMonad
--- >    import XMonad.Hooks.DynamicLog
+-- > import XMonad
+-- > import XMonad.Hooks.DynamicLog
 --
 -- If you just want a quick-and-dirty status bar with zero effort, try
 -- the 'xmobar' or 'dzen' functions:
diff --git a/XMonad/Hooks/StatusBar.hs b/XMonad/Hooks/StatusBar.hs
index b4f5d7fa..d13dd1d4 100644
--- a/XMonad/Hooks/StatusBar.hs
+++ b/XMonad/Hooks/StatusBar.hs
@@ -74,9 +74,9 @@ import XMonad.Hooks.StatusBar.PP
 -- $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
+-- > 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'
diff --git a/XMonad/Hooks/StatusBar/PP.hs b/XMonad/Hooks/StatusBar/PP.hs
index 0b304d94..29921cf3 100644
--- a/XMonad/Hooks/StatusBar/PP.hs
+++ b/XMonad/Hooks/StatusBar/PP.hs
@@ -64,13 +64,16 @@ import XMonad.Hooks.UrgencyHook
 -- An example usage for this module would be:
 --
 -- > import XMonad
--- > import XMonad.Hooks.DynamicLog
+-- > import XMonad.Hooks.StatusBar
+-- > import XMonad.Hooks.StatusBar.PP
 -- >
 -- > myPP = def { ppCurrent = xmobarColor "black" "white" }
--- > main = xmonad =<< statusBar "xmobar" myPP myToggleStrutsKey myConfig
---
--- Check "XMonad.Hooks.StatusBar" for how to use with status bars.
+-- > main = do
+-- >   mySB <- statusBarProp "xmobar" (pure myPP)
+-- >   xmonad =<< withEasySB mySB defToggleStrutsKey myConfig
 --
+-- Check "XMonad.Hooks.StatusBar" for more examples and an in depth
+-- explanation.
 
 -- | The 'PP' type allows the user to customize the formatting of
 --   status information.