    -----------------------------------------------------------------------------
    -- |
    -- Module      :  XMonad.Hooks.DynamicProperty
    -- Copyright   :  (c) Brandon S Allbery, 2015
    -- License     :  BSD3-style (see LICENSE)
    --
    -- Maintainer  :  allbery.b@gmail.com
    -- Stability   :  unstable
    -- Portability :  not portable
    --
    -- Module to apply a ManageHook to an already-mapped window when a property
    -- changes. This would commonly be used to match browser windows by title,
    -- since the final title will only be set after (a) the window is mapped,
    -- (b) its document has been loaded, (c) all load-time scripts have run.
    -- (Don't blame browsers for this; it's inherent in HTML and the DOM. And
    -- changing title dynamically is explicitly permitted by ICCCM and EWMH;
    -- you don't really want to have your editor window umapped/remapped to
    -- show the current document and modified state in the titlebar, do you?)
    --
    -- This is a handleEventHook that triggers on a PropertyChange event. It
    -- currently ignores properties being removed, in part because you can't
    -- do anything useful in a ManageHook involving nonexistence of a property.
    --
    -----------------------------------------------------------------------------

    module XMonad.Hooks.DynamicProperty where

    import XMonad
    import Data.Monoid
    import Control.Applicative
    import Control.Monad (when)

    -- |
    -- Run a 'ManageHook' when a specific property is changed on a window. Note
    -- that this will run on any window which changes the property, so you should
    -- be very specific in your 'MansgeHook' matching (lots of windows change
    -- their titles on the fly!):
    --
    -- dynamicPropertyChange "WM_NAME" (className =? "Iceweasel" <&&> title =? "whatever" --> doShift "2")
    --
    -- Note that the fixity of (-->) won't allow it to be mixed with ($), so you
    -- can't use the obvious $ shorthand.
    --
    -- > dynamicPropertyChange "WM_NAME" $ title =? "Foo" --> doFloat -- won't work!
    --
    -- Consider instead phrasing it like any
    -- other 'ManageHook':
    --
    -- >      , handleEventHook = dynamicPropertyChange "WM_NAME" myDynHook <+> handleEventHook baseConfig
    -- >
    -- >    {- ... -}
    -- >
    -- >    myDynHook = composeAll [...]
    --
    dynamicPropertyChange :: String -> ManageHook -> Event -> X All
    dynamicPropertyChange :: String -> ManageHook -> Event -> X All
    dynamicPropertyChange prop :: String
    prop hook :: ManageHook
    hook PropertyEvent { ev_window :: Event -> Window
    ev_window = Window
    w, ev_atom :: Event -> Window
    ev_atom = Window
    a, ev_propstate :: Event -> CInt
    ev_propstate = CInt
    ps } = do
      Window
    pa <- String -> X Window
    getAtom String
    prop
      Bool -> X () -> X ()
    forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
    when (CInt
    ps CInt -> CInt -> Bool
    forall a. Eq a => a -> a -> Bool
    == CInt
    propertyNewValue Bool -> Bool -> Bool
    && Window
    a Window -> Window -> Bool
    forall a. Eq a => a -> a -> Bool
    == Window
    pa) (X () -> X ()) -> X () -> X ()
    forall a b. (a -> b) -> a -> b
    $ do
        WindowSet -> WindowSet
    g <- Endo WindowSet -> WindowSet -> WindowSet
    forall a. Endo a -> a -> a
    appEndo (Endo WindowSet -> WindowSet -> WindowSet)
    -> X (Endo WindowSet) -> X (WindowSet -> WindowSet)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Endo WindowSet -> X (Endo WindowSet) -> X (Endo WindowSet)
    forall a. a -> X a -> X a
    userCodeDef ((WindowSet -> WindowSet) -> Endo WindowSet
    forall a. (a -> a) -> Endo a
    Endo WindowSet -> WindowSet
    forall a. a -> a
    id) (ManageHook -> Window -> X (Endo WindowSet)
    forall a. Query a -> Window -> X a
    runQuery ManageHook
    hook Window
    w)
        (WindowSet -> WindowSet) -> X ()
    windows WindowSet -> WindowSet
    g
      All -> X All
    forall (m :: * -> *) a. Monad m => a -> m a
    return All
    forall a. Monoid a => a
    mempty -- so anything else also processes it
    dynamicPropertyChange _ _ _ = All -> X All
    forall (m :: * -> *) a. Monad m => a -> m a
    return All
    forall a. Monoid a => a
    mempty

    -- | A shorthand for the most common case, dynamic titles
    dynamicTitle :: ManageHook -> Event -> X All
    -- strictly, this should also check _NET_WM_NAME. practically, both will
    -- change and each gets its own PropertyEvent, so we'd need to record that
    -- we saw the event for that window and ignore the second one. Instead, just
    -- trust that nobody sets only _NET_WM_NAME. (I'm sure this will prove false,
    -- since there's always someone who can't bother being compliant.)
    dynamicTitle :: ManageHook -> Event -> X All
    dynamicTitle = String -> ManageHook -> Event -> X All
    dynamicPropertyChange "WM_NAME"