{-# LANGUAGE ScopedTypeVariables, GeneralizedNewtypeDeriving, FlexibleInstances, MultiParamTypeClasses, FlexibleContexts -- ghc-6.12 only #-} ----------------------------------------------------------------------------- -- | -- Module : XMonad.Util.WindowState -- Copyright : (c) Dmitry Bogatov -- License : BSD -- -- Maintainer : Dmitry Bogatov -- 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