    {-#
      LANGUAGE ScopedTypeVariables, GeneralizedNewtypeDeriving,
      FlexibleInstances, MultiParamTypeClasses,
      FlexibleContexts -- ghc-6.12 only
      #-}
    -----------------------------------------------------------------------------
    -- |
    -- Module       : XMonad.Util.WindowState
    -- Copyright    : (c) Dmitry Bogatov <KAction@gnu.org>
    -- License      : BSD
    --
    -- Maintainer   : Dmitry Bogatov <KAction@gnu.org>
    -- Stability    : unstable
    -- Portability  : unportable
    --
    -- Functions for saving per-window data.
    -----------------------------------------------------------------------------

    module XMonad.Util.WindowState ( -- * Usage
                                     -- $usage
                                     get,
                                     put,
                                     StateQuery(..),
                                     runStateQuery,
                                     catchQuery ) where
    import XMonad hiding (get, put, modify)
    import Control.Monad.Reader(ReaderT(..))
    import Control.Monad.State.Class
    import Data.Typeable (typeOf)
    import Control.Applicative((<$>), Applicative)
    -- $usage
    --
    -- This module allow to store state data with some 'Window'.
    -- It is implemented with XProperties, so resources will be freed when
    -- 'Window' is destoyed.
    --
    -- This module have advantage over "XMonad.Actions.TagWindows" in that it
    -- hides from you implementation details and provides simple type-safe
    -- interface.  Main datatype is "StateQuery", which is simple wrapper around
    -- "Query", which is instance of MonadState, with 'put' and 'get' are
    -- functions to acess data, stored in "Window".
    --
    -- To save some data in window you probably want to do following:
    -- > (runStateQuery  (put $ Just value)  win) :: X ()
    -- To retrive it, you can use
    -- > (runStateQuery get win) :: X (Maybe YourValueType)
    -- "Query" can be promoted to "StateQuery" simply by constructor,
    -- and reverse is 'getQuery'.
    --
    -- For example, I use it to have all X applications @russian@ or @dvorak@
    -- layout, but emacs have only @us@, to not screw keybindings. Use your
    -- imagination!

    -- | Wrapper around "Query" with phanom type @s@, representing state, saved in
    -- window.
    newtype StateQuery s a = StateQuery {
          getQuery :: Query a
        } deriving (Monad, MonadIO, Applicative, Functor)

    packIntoQuery :: (Window -> X a) -> Query a
    packIntoQuery = Query . ReaderT

    -- | Apply "StateQuery" to "Window".
    runStateQuery :: StateQuery s a -> Window ->  X a
    runStateQuery = runQuery . getQuery

    -- | Lifted to "Query" version of 'catchX'
    catchQuery :: Query a -> Query (Maybe a)
    catchQuery q = packIntoQuery $ \win -> userCode $ runQuery q win

    -- | Instance of MonadState for StateQuery.
    instance (Show s, Read s, Typeable s) => MonadState (Maybe s) (StateQuery s) where
        get = StateQuery  $ read' <$> get' undefined where
            get'   :: (Maybe s) -> Query String
            get' x = stringProperty (typePropertyName x)
            read'  :: (Read s) => String -> Maybe s
            read' "" = Nothing
            read' s  = Just $ read s
        put = StateQuery . packIntoQuery <$> setWindowProperty' where
            setWindowProperty' val = setWindowProperty prop strValue where
                prop = typePropertyName val
                strValue = maybe "" show val

    typePropertyName :: (Typeable a) => a -> String
    typePropertyName x = "_XMONAD_WINSTATE__" ++ show (typeOf x)

    type PropertyName = String
    setWindowProperty :: PropertyName -> String -> Window -> X ()
    setWindowProperty prop val win = withDisplay $ \d -> io $
                                     internAtom d prop False >>=
                                     setTextProperty d win val