-----------------------------------------------------------------------------
-- |
-- Module      :  XMonad.Util.WindowProperties
-- Description :  EDSL for specifying window properties.
-- Copyright   :  (c) Roman Cheplyaka
-- License     :  BSD-style (see LICENSE)
--
-- Maintainer  :  Roman Cheplyaka <roma@ro-che.info>
-- Stability   :  unstable
-- Portability :  unportable
--
-- EDSL for specifying window properties; various utilities related to window
-- properties.
--
-----------------------------------------------------------------------------
module XMonad.Util.WindowProperties (
    -- * EDSL for window properties
    -- $edsl
    Property(..), hasProperty, focusedHasProperty, allWithProperty,
    propertyToQuery,
    -- * Helper functions
    -- $helpers
    getProp32, getProp32s)
where

import Foreign.C.Types (CLong)
import XMonad
import XMonad.Actions.TagWindows (hasTag)
import XMonad.Prelude (filterM)
import qualified XMonad.StackSet as W

-- $edsl
-- Allows to specify window properties, such as title, classname or
-- resource, and to check them.
--
-- In contrast to ManageHook properties, these are instances of Show and Read,
-- so they can be used in layout definitions etc. For example usage see "XMonad.Layout.IM"

-- | Most of the property constructors are quite self-explaining.
data Property = Title String
              | ClassName String
              | Resource String
              | Role String -- ^ WM_WINDOW_ROLE property
              | Machine String -- ^ WM_CLIENT_MACHINE property
              | And Property Property
              | Or  Property Property
              | Not Property
              | Const Bool
              | Tagged String -- ^ Tagged via "XMonad.Actions.TagWindows"
              deriving (ReadPrec [Property]
ReadPrec Property
Int -> ReadS Property
ReadS [Property]
(Int -> ReadS Property)
-> ReadS [Property]
-> ReadPrec Property
-> ReadPrec [Property]
-> Read Property
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
$creadsPrec :: Int -> ReadS Property
readsPrec :: Int -> ReadS Property
$creadList :: ReadS [Property]
readList :: ReadS [Property]
$creadPrec :: ReadPrec Property
readPrec :: ReadPrec Property
$creadListPrec :: ReadPrec [Property]
readListPrec :: ReadPrec [Property]
Read, Int -> Property -> ShowS
[Property] -> ShowS
Property -> String
(Int -> Property -> ShowS)
-> (Property -> String) -> ([Property] -> ShowS) -> Show Property
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Property -> ShowS
showsPrec :: Int -> Property -> ShowS
$cshow :: Property -> String
show :: Property -> String
$cshowList :: [Property] -> ShowS
showList :: [Property] -> ShowS
Show)
infixr 9 `And`
infixr 8 `Or`

-- | Does given window have this property?
hasProperty :: Property -> Window -> X Bool
hasProperty :: Property -> Window -> X Bool
hasProperty Property
p = Query Bool -> Window -> X Bool
forall a. Query a -> Window -> X a
runQuery (Property -> Query Bool
propertyToQuery Property
p)

-- | Does the focused window have this property?
focusedHasProperty :: Property -> X Bool
focusedHasProperty :: Property -> X Bool
focusedHasProperty Property
p = do
    WindowSet
ws <- (XState -> WindowSet) -> X WindowSet
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets XState -> WindowSet
windowset
    let ms :: Maybe (Stack Window)
ms = Workspace String (Layout Window) Window -> Maybe (Stack Window)
forall i l a. Workspace i l a -> Maybe (Stack a)
W.stack (Workspace String (Layout Window) Window -> Maybe (Stack Window))
-> Workspace String (Layout Window) Window -> Maybe (Stack Window)
forall a b. (a -> b) -> a -> b
$ Screen String (Layout Window) Window ScreenId ScreenDetail
-> Workspace String (Layout Window) Window
forall i l a sid sd. Screen i l a sid sd -> Workspace i l a
W.workspace (Screen String (Layout Window) Window ScreenId ScreenDetail
 -> Workspace String (Layout Window) Window)
-> Screen String (Layout Window) Window ScreenId ScreenDetail
-> Workspace String (Layout Window) Window
forall a b. (a -> b) -> a -> b
$ WindowSet
-> Screen String (Layout Window) Window ScreenId ScreenDetail
forall i l a sid sd. StackSet i l a sid sd -> Screen i l a sid sd
W.current WindowSet
ws
    case Maybe (Stack Window)
ms of
        Just Stack Window
s  -> Property -> Window -> X Bool
hasProperty Property
p (Window -> X Bool) -> Window -> X Bool
forall a b. (a -> b) -> a -> b
$ Stack Window -> Window
forall a. Stack a -> a
W.focus Stack Window
s
        Maybe (Stack Window)
Nothing -> Bool -> X Bool
forall a. a -> X a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False

-- | Find all existing windows with specified property
allWithProperty :: Property -> X [Window]
allWithProperty :: Property -> X [Window]
allWithProperty Property
prop = (Display -> X [Window]) -> X [Window]
forall a. (Display -> X a) -> X a
withDisplay ((Display -> X [Window]) -> X [Window])
-> (Display -> X [Window]) -> X [Window]
forall a b. (a -> b) -> a -> b
$ \Display
dpy -> do
    Window
rootw <- (XConf -> Window) -> X Window
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks XConf -> Window
theRoot
    (Window
_,Window
_,[Window]
wins) <- IO (Window, Window, [Window]) -> X (Window, Window, [Window])
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO (Window, Window, [Window]) -> X (Window, Window, [Window]))
-> IO (Window, Window, [Window]) -> X (Window, Window, [Window])
forall a b. (a -> b) -> a -> b
$ Display -> Window -> IO (Window, Window, [Window])
queryTree Display
dpy Window
rootw
    Property -> Window -> X Bool
hasProperty Property
prop (Window -> X Bool) -> [Window] -> X [Window]
forall (m :: * -> *) a.
Applicative m =>
(a -> m Bool) -> [a] -> m [a]
`filterM` [Window]
wins

-- | Convert property to 'Query' 'Bool' (see "XMonad.ManageHook")
propertyToQuery :: Property -> Query Bool
propertyToQuery :: Property -> Query Bool
propertyToQuery (Title String
s) = Query String
title Query String -> String -> Query Bool
forall a. Eq a => Query a -> a -> Query Bool
=? String
s
propertyToQuery (Resource String
s) = Query String
resource Query String -> String -> Query Bool
forall a. Eq a => Query a -> a -> Query Bool
=? String
s
propertyToQuery (ClassName String
s) = Query String
className Query String -> String -> Query Bool
forall a. Eq a => Query a -> a -> Query Bool
=? String
s
propertyToQuery (Role String
s) = String -> Query String
stringProperty String
"WM_WINDOW_ROLE" Query String -> String -> Query Bool
forall a. Eq a => Query a -> a -> Query Bool
=? String
s
propertyToQuery (Machine String
s) = String -> Query String
stringProperty String
"WM_CLIENT_MACHINE" Query String -> String -> Query Bool
forall a. Eq a => Query a -> a -> Query Bool
=? String
s
propertyToQuery (And Property
p1 Property
p2) = Property -> Query Bool
propertyToQuery Property
p1 Query Bool -> Query Bool -> Query Bool
forall (m :: * -> *). Monad m => m Bool -> m Bool -> m Bool
<&&> Property -> Query Bool
propertyToQuery Property
p2
propertyToQuery (Or Property
p1 Property
p2) = Property -> Query Bool
propertyToQuery Property
p1 Query Bool -> Query Bool -> Query Bool
forall (m :: * -> *). Monad m => m Bool -> m Bool -> m Bool
<||> Property -> Query Bool
propertyToQuery Property
p2
propertyToQuery (Not Property
p) = Bool -> Bool
not (Bool -> Bool) -> Query Bool -> Query Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Property -> Query Bool
propertyToQuery Property
p
propertyToQuery (Const Bool
b) = Bool -> Query Bool
forall a. a -> Query a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
b
propertyToQuery (Tagged String
s) = Query Window
forall r (m :: * -> *). MonadReader r m => m r
ask Query Window -> (Window -> Query Bool) -> Query Bool
forall a b. Query a -> (a -> Query b) -> Query b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \Window
w -> X Bool -> Query Bool
forall a. X a -> Query a
liftX (String -> Window -> X Bool
hasTag String
s Window
w)

-- $helpers

-- | Get a window property from atom
getProp32 :: Atom -> Window -> X (Maybe [CLong])
getProp32 :: Window -> Window -> X (Maybe [CLong])
getProp32 Window
a Window
w = (Display -> X (Maybe [CLong])) -> X (Maybe [CLong])
forall a. (Display -> X a) -> X a
withDisplay ((Display -> X (Maybe [CLong])) -> X (Maybe [CLong]))
-> (Display -> X (Maybe [CLong])) -> X (Maybe [CLong])
forall a b. (a -> b) -> a -> b
$ \Display
dpy -> IO (Maybe [CLong]) -> X (Maybe [CLong])
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO (Maybe [CLong]) -> X (Maybe [CLong]))
-> IO (Maybe [CLong]) -> X (Maybe [CLong])
forall a b. (a -> b) -> a -> b
$ Display -> Window -> Window -> IO (Maybe [CLong])
getWindowProperty32 Display
dpy Window
a Window
w

-- | Get a window property from string
getProp32s :: String -> Window -> X (Maybe [CLong])
getProp32s :: String -> Window -> X (Maybe [CLong])
getProp32s String
str Window
w = do { Window
a <- String -> X Window
getAtom String
str; Window -> Window -> X (Maybe [CLong])
getProp32 Window
a Window
w }