diff --git a/CHANGES.md b/CHANGES.md index 64ad7ef6..49768dd4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,11 @@ ### Breaking Changes + * `XMonad.Hooks.DynamicProperty` + + - Deprecated the module in favour of the more aptly named + `XMonad.Hooks.OnPropertyChange`. + * `XMonad.Util.Scratchpad`: - Deprecated the module; use `XMonad.Util.NamedScratchpad` instead. @@ -52,6 +57,11 @@ ### New Modules +* `XMonad.Hooks.OnPropertyChange`: + + - A new module replicating the functionality of + `XMonad.Hooks.DynamicProperty`, but with more discoverable names. + ### Bug Fixes and Minor Changes * `XMonad.Layout.BorderResize` diff --git a/XMonad/Hooks/DynamicProperty.hs b/XMonad/Hooks/DynamicProperty.hs index 9cf81ee5..94330587 100644 --- a/XMonad/Hooks/DynamicProperty.hs +++ b/XMonad/Hooks/DynamicProperty.hs @@ -1,113 +1,24 @@ ------------------------------------------------------------------------------ -- | -- Module : XMonad.Hooks.DynamicProperty -- Description : Apply a ManageHook to an already-mapped window. -- 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. --- --- This module could also be useful for Electron applications like Spotify --- which sets its WM_CLASS too late for window manager to map it properly. --- ------------------------------------------------------------------------------ - -module XMonad.Hooks.DynamicProperty ( -- * Usage - -- $usage - - -- * Documentation - dynamicPropertyChange +module XMonad.Hooks.DynamicProperty {-# DEPRECATED "Use \"XMonad.Hooks.OnPropertyChange\" instead." #-} + ( module XMonad.Hooks.OnPropertyChange + , dynamicPropertyChange , dynamicTitle ) where import XMonad +import XMonad.Hooks.OnPropertyChange import XMonad.Prelude --- $usage --- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@: --- --- > import XMonad.Hooks.DynamicProperty --- --- Enable it by including in you handleEventHook definition: --- --- > main = xmonad $ def --- > { ... --- > , handleEventHook = dynamicPropertyChange "WM_NAME" (title =? "Spotify" --> doShift "5")) --- > , ... --- > } --- --- Or you could create a dynamicManageHook as below: --- --- > myDynamicManageHook :: ManageHook --- > myDynamicManageHook = --- > composeAll --- > [ className =? "Spotify" --> doShift (myWorkspaces !! 4), --- > title =? "maybe_special_terminal" <||> title =? "special_terminal" --> doCenterFloat, --- > className =? "dynamicApp" <&&> title =? "dynamic_app" --> doCenterFloat --- > ] --- --- And then use it in your handleEventHookDefinition: --- --- > main = xmonad $ def --- > { ... --- > , handleEventHook = dynamicPropertyChange "WM_NAME" myDynamicManageHook <> handleEventHook baseConfig --- > , ... --- > } --- - --- | --- 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 'ManageHook' 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': --- --- > main = xmonad $ def --- > { ... --- > , handleEventHook = dynamicPropertyChange "WM_NAME" myDynHook <> handleEventHook baseConfig --- > , ... --- > } --- > --- > myDynHook = composeAll [...] --- +-- | 'dynamicPropertyChange' = 'onXPropertyChange' 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 mempty -- so anything else also processes it -dynamicPropertyChange _ _ _ = return mempty +dynamicPropertyChange = onXPropertyChange --- | A shorthand for the most common case, dynamic titles +-- | 'dynamicTitle' = 'onTitleChange' 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" +dynamicTitle = onTitleChange diff --git a/XMonad/Hooks/OnPropertyChange.hs b/XMonad/Hooks/OnPropertyChange.hs new file mode 100644 index 00000000..bfd6f162 --- /dev/null +++ b/XMonad/Hooks/OnPropertyChange.hs @@ -0,0 +1,119 @@ +----------------------------------------------------------------------------- +-- | +-- Module : XMonad.Hooks.OnPropertyChange +-- Description : Apply a manageHook on a property (e.g., @WM_CLASS@) change +-- 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. +-- +-- This module could also be useful for Electron applications like Spotify +-- which sets its WM_CLASS too late for window manager to map it properly. +-- +----------------------------------------------------------------------------- + +module XMonad.Hooks.OnPropertyChange ( + -- * Usage + -- $usage + onXPropertyChange, + onTitleChange, + onClassChange, +) where + +import XMonad +import XMonad.Prelude + +-- $usage +-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@: +-- +-- > import XMonad.Hooks.DynamicProperty +-- +-- Enable it by including in you handleEventHook definition: +-- +-- > main = xmonad $ def +-- > { ... +-- > , handleEventHook = onXPropertyChange "WM_NAME" (title =? "Spotify" --> doShift "5")) +-- > , ... +-- > } +-- +-- Or you could create a dynamicManageHook as below: +-- +-- > myDynamicManageHook :: ManageHook +-- > myDynamicManageHook = +-- > composeAll +-- > [ className =? "Spotify" --> doShift (myWorkspaces !! 4), +-- > title =? "maybe_special_terminal" <||> title =? "special_terminal" --> doCenterFloat, +-- > className =? "dynamicApp" <&&> title =? "dynamic_app" --> doCenterFloat +-- > ] +-- +-- And then use it in your handleEventHookDefinition: +-- +-- > main = xmonad $ def +-- > { ... +-- > , handleEventHook = onXPropertyChange "WM_NAME" myDynamicManageHook <> handleEventHook baseConfig +-- > , ... +-- > } +-- + +-- | +-- 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 'ManageHook' matching (lots of windows change +-- their titles on the fly!): +-- +-- > onXPropertyChange "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. +-- +-- > onXPropertyChange "WM_NAME" $ title =? "Foo" --> doFloat -- won't work! +-- +-- Consider instead phrasing it like any +-- other 'ManageHook': +-- +-- > main = xmonad $ def +-- > { ... +-- > , handleEventHook = onXPropertyChange "WM_NAME" myDynHook <> handleEventHook baseConfig +-- > , ... +-- > } +-- > +-- > myDynHook = composeAll [...] +-- +onXPropertyChange :: String -> ManageHook -> Event -> X All +onXPropertyChange 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 mempty -- so anything else also processes it +onXPropertyChange _ _ _ = return mempty + +-- | A shorthand for dynamic titles; i.e., applications changing their +-- @WM_NAME@ property. +onTitleChange :: 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.) +onTitleChange = onXPropertyChange "WM_NAME" + +-- | A shorthand for dynamic resource and class names; i.e., +-- applications changing their @WM_CLASS@ property. +onClassChange :: ManageHook -> Event -> X All +onClassChange = onXPropertyChange "WM_CLASS" diff --git a/xmonad-contrib.cabal b/xmonad-contrib.cabal index c25ffcb3..09c636f2 100644 --- a/xmonad-contrib.cabal +++ b/xmonad-contrib.cabal @@ -196,6 +196,7 @@ library XMonad.Hooks.ManageHelpers XMonad.Hooks.Minimize XMonad.Hooks.Modal + XMonad.Hooks.OnPropertyChange XMonad.Hooks.Place XMonad.Hooks.PositionStoreHooks XMonad.Hooks.RefocusLast