---------------------------------------------------------------------------- -- | -- Module : XMonad.Actions.Minimize -- Copyright : (c) Bogdan Sinitsyn (2016) -- License : BSD3-style (see LICENSE) -- -- Maintainer : Bogdan Sinitsyn -- Stability : unstable -- Portability : not portable -- -- Adds actions for minimizing and maximizing windows -- -- This module should be used with "XMonad.Layout.Minimize". Add 'minimize' to your -- layout modifiers as described in "XMonad.Layout.Minimized" and use actions from -- this module -- -- Possible keybindings: -- -- > , ((modm, xK_m ), withFocused minimizeWindow) -- > , ((modm .|. shiftMask, xK_m ), withLastMinimized maximizeWindowAndFocus) -- ----------------------------------------------------------------------------- module XMonad.Actions.Minimize ( -- * Usage -- $usage minimizeWindow , maximizeWindow , maximizeWindowAndFocus , withLastMinimized , withLastMinimized' , withFirstMinimized , withFirstMinimized' , withMinimized ) where import XMonad import qualified XMonad.StackSet as W import qualified XMonad.Layout.BoringWindows as BW import qualified XMonad.Util.ExtensibleState as XS import XMonad.Util.Minimize import XMonad.Util.WindowProperties (getProp32) import Foreign.C.Types (CLong) import Control.Applicative((<$>)) import Control.Monad (join) import Data.Maybe (fromMaybe, listToMaybe) import qualified Data.List as L import qualified Data.Map as M -- $usage -- Import this module with "XMonad.Layout.Minimize" and "XMonad.Layout.BoringWindows": -- > import XMonad.Actions.Minimize -- > import XMonad.Layout.Minimize -- > import qualified XMonad.Layout.BoringWindows as BW -- -- Then apply 'minimize' and 'boringWindows' to your layout hook and use some -- actions from this module: -- > main = xmonad def { layoutHook = minimize . BW.boringWindows $ whatever } -- Example keybindings: -- > , ((modm, xK_m ), withFocused minimizeWindow ) -- > , ((modm .|. shiftMask, xK_m ), withLastMinimized maximizeWindow) setMinimizedState :: Window -> Int -> (CLong -> [CLong] -> [CLong]) -> X () setMinimizedState win st f = do setWMState win st withDisplay $ \dpy -> do wm_state <- getAtom "_NET_WM_STATE" hidden <- fromIntegral <$> getAtom "_NET_WM_STATE_HIDDEN" wstate <- fromMaybe [] <$> getProp32 wm_state win io $ changeProperty32 dpy win wm_state aTOM propModeReplace (f hidden wstate) setMinimized :: Window -> X () setMinimized win = setMinimizedState win iconicState (:) setNotMinimized :: Window -> X () setNotMinimized win = setMinimizedState win normalState L.delete -- It does not just set minimizedStack to newWindows because it should save -- order in which elements were added (newer first) modified :: (RectMap -> RectMap) -> X Bool modified f = XS.modified $ \Minimized { rectMap = oldRectMap, minimizedStack = oldStack } -> let newRectMap = f oldRectMap newWindows = M.keys newRectMap in Minimized { rectMap = newRectMap , minimizedStack = (newWindows L.\\ oldStack) ++ (oldStack `L.intersect` newWindows) } -- | Minimize a window minimizeWindow :: Window -> X () minimizeWindow w = withWindowSet $ \ws -> whenX (modified $ M.insert w (M.lookup w $ W.floating ws)) $ do setMinimized w windows $ W.sink w BW.focusDown -- | Maximize window and apply a function to maximized window and 'WindowSet' maximizeWindowAndChangeWSet :: (Window -> WindowSet -> WindowSet) -> Window -> X () maximizeWindowAndChangeWSet f w = do mrect <- XS.gets (join . M.lookup w . rectMap) whenX (modified $ M.delete w) $ do setNotMinimized w broadcastMessage BW.UpdateBoring windows $ f w . maybe id (W.float w) mrect -- | Just maximize a window without focusing maximizeWindow :: Window -> X () maximizeWindow = maximizeWindowAndChangeWSet $ const id -- | Maximize a window and then focus it maximizeWindowAndFocus :: Window -> X () maximizeWindowAndFocus = maximizeWindowAndChangeWSet W.focusWindow -- | Perform an action with first minimized window on current workspace -- or do nothing if there is no minimized windows on current workspace withFirstMinimized :: (Window -> X ()) -> X () withFirstMinimized action = withFirstMinimized' (flip whenJust action) -- | Like withFirstMinimized but the provided action is always invoked with a -- 'Maybe Window', that will be nothing if there is no first minimized window. withFirstMinimized' :: (Maybe Window -> X ()) -> X () withFirstMinimized' action = withMinimized (action . listToMaybe . reverse) -- | Perform an action with last minimized window on current workspace -- or do nothing if there is no minimized windows on current workspace withLastMinimized :: (Window -> X ()) -> X () withLastMinimized action = withLastMinimized' (flip whenJust action) -- | Like withLastMinimized but the provided action is always invoked with a -- 'Maybe Window', that will be nothing if there is no last minimized window. withLastMinimized' :: (Maybe Window -> X ()) -> X () withLastMinimized' action = withMinimized (action . listToMaybe) withMinimized :: ([Window] -> X a) -> X a withMinimized action = do minimized <- XS.gets minimizedStack currentStack <- withWindowSet $ return . W.index action $ minimized `L.intersect` currentStack