    -----------------------------------------------------------------------------
    -- |
    -- Module      :  XMonad.Hooks.DebugStack
    -- Copyright   :  (c) Brandon S Allbery KF8NH, 2014
    -- License     :  BSD3-style (see LICENSE)
    --
    -- Maintainer  :  allbery.b@gmail.com
    -- Stability   :  unstable
    -- Portability :  not portable
    --
    -- Dump the state of the 'StackSet'. A @logHook@ and @handleEventHook@ are
    -- also provided.
    --
    -----------------------------------------------------------------------------

    module XMonad.Hooks.DebugStack (debugStack
                                   ,debugStackFull
                                   ,debugStackString
                                   ,debugStackFullString
                                   ,debugStackLogHook
                                   ,debugStackFullLogHook
                                   ,debugStackEventHook
                                   ,debugStackFullEventHook
                                   ) where

    import           XMonad.Core
    import qualified XMonad.StackSet                                       as W

    import           XMonad.Util.DebugWindow

    import           Graphics.X11.Types                  (Window)
    import           Graphics.X11.Xlib.Extras            (Event)

    import           Control.Monad                       (foldM)
    import           Data.Map                            (member)
    import           Data.Monoid                         (All(..))
    import           Data.List                           (intercalate)

    -- | Print the state of the current window stack for the current workspace to
    --   @stderr@, which for most installations goes to @~/.xsession-errors@.
    --   "XMonad.Util.DebugWindow" is used to display the individual windows.
    debugStack :: X ()
    debugStack =  debugStackString >>= trace

    -- | Print the state of the current window stack for all workspaces to
    --   @stderr@, which for most installations goes to @~/.xsession-errors@.
    --   "XMonad.Util.DebugWindow" is used to display the individual windows.
    debugStackFull :: X ()
    debugStackFull =  debugStackFullString >>= trace

    -- | 'debugStack' packaged as a 'logHook'. (Currently this is identical.)
    debugStackLogHook :: X ()
    debugStackLogHook =  debugStack

    -- | 'debugStackFull packaged as a 'logHook'. (Currently this is identical.)
    debugStackFullLogHook :: X ()
    debugStackFullLogHook =  debugStackFull

    -- | 'debugStack' packaged as a 'handleEventHook'. You almost certainly do not
    --   want to use this unconditionally, as it will cause massive amounts of
    --   output and possibly slow @xmonad@ down severely.

    debugStackEventHook   :: Event -> X All
    debugStackEventHook _ =  debugStack >> return (All True)

    -- | 'debugStackFull' packaged as a 'handleEventHook'. You almost certainly do
    --   not want to use this unconditionally, as it will cause massive amounts of
    --   output and possibly slow @xmonad@ down severely.

    debugStackFullEventHook   :: Event -> X All
    debugStackFullEventHook _ =  debugStackFull >> return (All True)

    -- | Dump the state of the current workspace in the 'StackSet' as a multiline 'String'.
    debugStackString :: X String
    debugStackString =  withWindowSet $ debugStackWs . W.workspace . W.current

    -- | Dump the state of all workspaces in the 'StackSet' as a multiline 'String'.
    -- @@@ this is in stackset order, which is roughly lru-ish
    debugStackFullString :: X String
    debugStackFullString =  withWindowSet $ fmap (intercalate "\n") . mapM debugStackWs . W.workspaces

    -- | Dump the state of a workspace in the current 'StackSet' as a multiline 'String'.
    --   @
    --   Workspace "foo::
    --     mm
    --   * ww
    --    ^ww
    --   @
    --   * indicates the focused window, ^ indicates a floating window
    debugStackWs   :: W.Workspace String (Layout Window) Window -> X String
    debugStackWs w =  withWindowSet $ \ws -> do
      let cur = if wt == W.currentTag ws then " (current)" else ""
          wt  = W.tag w
      s <- emit ws $ W.integrate' . W.stack $ w
      return $ intercalate "\n" $ ("Workspace " ++ show wt ++ cur):s
      where
        emit       :: WindowSet -> [Window] -> X [String]
        emit _  [] =  return ["    -empty workspace-"]
        emit ww ws = do
          (_,ss) <- foldM emit' (ww,[]) ws
          return ss

        emit' :: (WindowSet,[String])
              -> Window
              -> X (WindowSet,[String])
        emit' (ws,a) w' = do
          let focus = if Just w' == W.peek ws then '*' else ' '
              float = if w' `member` W.floating ws then '^' else ' '
          s <- debugWindow w'
          return (ws,(focus:float:s):a)