{-# LANGUAGE TypeSynonymInstances, MultiParamTypeClasses, DeriveDataTypeable, PatternGuards #-} ----------------------------------------------------------------------------- -- | -- Module : XMonad.Layout.Monitor -- Copyright : (c) Roman Cheplyaka -- License : BSD-style (see LICENSE) -- -- Maintainer : Roman Cheplyaka -- Stability : unstable -- Portability : unportable -- -- Layout modfier for displaying some window (monitor) above other windows -- ----------------------------------------------------------------------------- module XMonad.Layout.Monitor ( -- * Usage -- $usage -- * Hints and issues -- $hints Monitor(..), monitor, Property(..), MonitorMessage(..), doHideIgnore, manageMonitor -- * TODO -- $todo ) where import XMonad import XMonad.Layout.LayoutModifier import XMonad.Util.WindowProperties import XMonad.Hooks.ManageHelpers (doHideIgnore) import XMonad.Hooks.FadeInactive (setOpacity) import Control.Monad -- $usage -- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@: -- -- > import XMonad.Layout.Monitor -- -- Define 'Monitor' record. 'monitor' can be used as a template. At least 'prop' -- and 'rect' should be set here. Also consider setting 'persistent' to True. -- -- Minimal example: -- -- > myMonitor = monitor -- > { prop = ClassName "SomeClass" -- > , rect = Rectangle 0 0 40 20 -- rectangle 40x20 in upper left corner -- > } -- -- More interesting example: -- -- > clock = monitor { -- > -- Cairo-clock creates 2 windows with the same classname, thus also using title -- > prop = ClassName "Cairo-clock" `And` Title "MacSlow's Cairo-Clock" -- > -- rectangle 150x150 in lower right corner, assuming 1280x800 resolution -- > , rect = Rectangle (1280-150) (800-150) 150 150 -- > -- avoid flickering -- > , persistent = True -- > -- make the window transparent -- > , opacity = 0.6 -- > -- hide on start -- > , visible = False -- > -- assign it a name to be able to toggle it independently of others -- > , name = "clock" -- > } -- -- Add ManageHook to de-manage monitor windows and apply opacity settings. -- -- > manageHook = myManageHook <+> manageMonitor clock -- -- Apply layout modifier. -- -- > myLayout = ModifiedLayout clock $ tall ||| Full ||| ... -- -- After that, if there exists a window with specified properties, it will be -- displayed on top of all /tiled/ (not floated) windows on specified -- position. -- -- It's also useful to add some keybinding to toggle monitor visibility: -- -- > , ((mod1Mask, xK_u ), broadcastMessage ToggleMonitor >> refresh) -- -- Screenshot: data Monitor a = Monitor { prop :: Property -- ^ property which uniquely identifies monitor window , rect :: Rectangle -- ^ specifies where to put monitor , visible :: Bool -- ^ is it visible by default? , name :: String -- ^ name of monitor (useful when we have many of them) , persistent :: Bool -- ^ is it shown on all layouts? , opacity :: Rational -- ^ opacity level } deriving (Read, Show) -- | Template for 'Monitor' record. At least 'prop' and 'rect' should be -- redefined. Default settings: 'visible' is 'True', 'persistent' is 'False'. monitor :: Monitor a monitor = Monitor { prop = Const False , rect = Rectangle 0 0 0 0 , visible = True , name = "" , persistent = False , opacity = 1 } -- | Messages without names affect all monitors. Messages with names affect only -- monitors whose names match. data MonitorMessage = ToggleMonitor | ShowMonitor | HideMonitor | ToggleMonitorNamed String | ShowMonitorNamed String | HideMonitorNamed String deriving (Read,Show,Eq,Typeable) instance Message MonitorMessage withMonitor :: Property -> a -> (Window -> X a) -> X a withMonitor p a fn = do monitorWindows <- allWithProperty p case monitorWindows of [] -> return a w:_ -> fn w instance LayoutModifier Monitor Window where redoLayout mon _ _ rects = withMonitor (prop mon) (rects, Nothing) $ \w -> if visible mon then do tileWindow w (rect mon) reveal w return ((w,rect mon):rects, Nothing) else do hide w return (rects, Nothing) handleMess mon mess | Just ToggleMonitor <- fromMessage mess = return $ Just $ mon { visible = not $ visible mon } | Just (ToggleMonitorNamed n) <- fromMessage mess = return $ if name mon == n then Just $ mon { visible = not $ visible mon } else Nothing | Just ShowMonitor <- fromMessage mess = return $ Just $ mon { visible = True } | Just (ShowMonitorNamed n) <- fromMessage mess = return $ if name mon == n then Just $ mon { visible = True } else Nothing | Just HideMonitor <- fromMessage mess = return $ Just $ mon { visible = False } | Just (HideMonitorNamed n) <- fromMessage mess = return $ if name mon == n then Just $ mon { visible = False } else Nothing | Just Hide <- fromMessage mess = do unless (persistent mon) $ withMonitor (prop mon) () hide; return Nothing | otherwise = return Nothing -- | ManageHook which demanages monitor window and applies opacity settings. manageMonitor :: Monitor a -> ManageHook manageMonitor mon = propertyToQuery (prop mon) --> do w <- ask liftX $ setOpacity w $ opacity mon if persistent mon then doIgnore else doHideIgnore -- $hints -- - This module assumes that there is only one window satisfying property exists. -- -- - If your monitor is available on /all/ layouts, set -- 'persistent' to 'True' to avoid unnecessary -- flickering. You can still toggle monitor with a keybinding. -- -- - You can use several monitors with nested modifiers. Give them names --- to be able to toggle them independently. -- -- - You can display monitor only on specific workspaces with -- "XMonad.Layout.PerWorkspace". -- $todo -- - make Monitor remember the window it manages -- -- - specify position relative to the screen