----------------------------------------------------------------------------- -- | -- 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 prop hook PropertyEvent { ev_window = w, ev_atom = a, ev_propstate = ps } = do pa <- getAtom prop when (ps == propertyNewValue && a == pa) $ do g <- appEndo <$> userCodeDef (Endo id) (runQuery hook w) windows g return (All False) -- so anything else also processes it dynamicPropertyChange _ _ _ = return (All False) -- | 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 = dynamicPropertyChange "WM_NAME"