aboutsummaryrefslogtreecommitdiffstats
path: root/XMonad/Hooks/Place.hs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--XMonad/Hooks/Place.hs167
1 files changed, 107 insertions, 60 deletions
diff --git a/XMonad/Hooks/Place.hs b/XMonad/Hooks/Place.hs
index af2b3b8..c0b92ee 100644
--- a/XMonad/Hooks/Place.hs
+++ b/XMonad/Hooks/Place.hs
@@ -8,7 +8,7 @@
-- Stability : unstable
-- Portability : unportable
--
--- Automatic placement of floating and "WindowArranger" windows.
+-- Automatic placement of floating windows.
--
-----------------------------------------------------------------------------
@@ -40,15 +40,17 @@ import XMonad.Layout.WindowArranger
import XMonad.Actions.FloatKeys
import qualified Data.Map as M
-import Data.List (sortBy, minimumBy)
-import Data.Maybe (maybe)
+import Data.Ratio ((%))
+import Data.List (sortBy, minimumBy, partition)
+import Data.Maybe (maybe, fromMaybe, catMaybes)
import Data.Monoid (Endo(..))
-import Control.Monad.Trans (lift, liftIO)
+import Control.Monad (guard, join)
+import Control.Monad.Trans (lift)
-- $usage
--- This module provides a ManageHook that automatically places
+-- This module provides a 'ManageHook' that automatically places
-- floating windows at appropriate positions on the screen, as well
--- as an X action to manually trigger repositioning.
+-- as an 'X' action to manually trigger repositioning.
--
-- You can use this module by including the following in your @~\/.xmonad\/xmonad.hs@:
--
@@ -59,6 +61,10 @@ import Control.Monad.Trans (lift, liftIO)
-- > main = xmonad $ defaultConfig { manageHook = placeHook simpleSmart
-- > <+> manageHook defaultConfig }
--
+-- Note that 'placeHook' should be applied after most other hooks, especially hooks
+-- such as 'doFloat' and 'doShift'. Since hooks combined with '<+>' are applied from
+-- right to left, this means that 'placeHook' should be the /first/ hook in your chain.
+--
-- You can also define a key to manually trigger repositioning with 'placeFocused' by
-- adding the following to your keys definition:
--
@@ -73,8 +79,6 @@ import Control.Monad.Trans (lift, liftIO)
{- Placement policies -}
-- $placements
--- #Placement policies#
---
-- Placement policies determine how windows will be placed by 'placeFocused' and 'placeHook'.
--
-- A few examples:
@@ -146,20 +150,22 @@ withGaps = Bounds
-
-
{- Placement functions -}
--- | Repositions the focused window according to a placement policy.
+-- | Repositions the focused window according to a placement policy. Works for
+-- both \"real\" floating windows and windows in a 'WindowArranger'-based
+-- layout.
placeFocused :: Placement -> X ()
placeFocused p = withFocused $ \window -> do
- (s,r,rs,pointer) <- getNecessaryData window
-
- let r'@(Rectangle x' y' _ _) = purePlaceWindow p s rs pointer r
-
- fs <- getFloats
- case elem window fs of
+ info <- gets $ screenInfo . S.current . windowset
+ floats <- gets $ M.keys . S.floating . windowset
+
+ r'@(Rectangle x' y' _ _) <- placeWindow p window info floats
+
+ -- use X.A.FloatKeys if the window is floating, send
+ -- a WindowArranger message otherwise.
+ case elem window floats of
True -> keysMoveWindowTo (x', y') (0, 0) window
False -> sendMessage $ SetGeometry r'
@@ -167,19 +173,47 @@ placeFocused p = withFocused $ \window -> do
-- | Hook to automatically place windows when they are created.
placeHook :: Placement -> ManageHook
placeHook p = do window <- ask
- (s,r,rs,pointer) <- Query $ lift (getNecessaryData window)
-
- let (Rectangle x' y' _ _) = purePlaceWindow p s rs pointer r
-
- d <- Query $ lift $ asks display
- liftIO $ moveWindow d window x' y'
- -- Move window at the X level, and
- -- hope both the standard floating
- -- system and WindowArranger layouts
- -- will pick it up correctly.
- -- I'm not really satisfied with this though.
-
- return $ Endo id
+ r <- Query $ lift $ getWindowRectangle window
+ allRs <- Query $ lift $ getAllRectangles
+ pointer <- Query $ lift $ getPointer window
+
+ return $ Endo $ \theWS -> fromMaybe theWS $
+ do let currentRect = screenRect $ S.screenDetail $ S.current theWS
+ floats = M.keys $ S.floating theWS
+
+ guard(window `elem` floats )
+
+ -- Look for the workspace(s) on which the window is to be
+ -- spawned. Each of them also needs an associated screen
+ -- rectangle; for hidden workspaces, we use the current
+ -- workspace's screen.
+ let infos = filter ((window `elem`) . stackContents . S.stack . fst)
+ $ [screenInfo $ S.current theWS]
+ ++ (map screenInfo $ S.visible theWS)
+ ++ zip (S.hidden theWS) (repeat currentRect)
+
+ guard(not $ null infos)
+
+ let (workspace, screen) = head infos
+ rs = catMaybes $ map (flip M.lookup allRs)
+ $ organizeClients workspace window floats
+ r' = purePlaceWindow p screen rs pointer r
+ newRect = r2rr screen r'
+ newFloats = M.insert window newRect (S.floating theWS)
+
+ return $ theWS { S.floating = newFloats }
+
+
+placeWindow :: Placement -> Window
+ -> (S.Workspace WorkspaceId (Layout Window) Window, Rectangle)
+ -- ^ The workspace with reference to which the window should be placed,
+ -- and the screen's geometry.
+ -> [Window]
+ -- ^ The list of floating windows.
+ -> X Rectangle
+placeWindow p window (ws, s) floats
+ = do (r, rs, pointer) <- getNecessaryData window ws floats
+ return $ purePlaceWindow p s rs pointer r
-- | Compute the new position of a window according to a placement policy.
@@ -202,7 +236,7 @@ purePlaceWindow (Smart ratios) s rs _ w
= placeSmart ratios s rs (rect_width w) (rect_height w)
--- | Helper: Places a Rectangle at a fixed position indicated by two Rationals
+-- | Helper: Places a Rectangle at a fixed position indicated by two Rationals
-- inside another,
placeRatio :: (Rational, Rational) -> Rectangle -> Rectangle -> Rectangle
placeRatio (rx, ry) (Rectangle x1 y1 w1 h1) (Rectangle _ _ w2 h2)
@@ -231,19 +265,22 @@ scale r n1 n2 = truncate $ r * fi n2 + (1 - r) * fi n1
fi :: (Integral a, Num b) => a -> b
fi = fromIntegral
-
+r2rr :: Rectangle -> Rectangle -> S.RationalRect
+r2rr (Rectangle x0 y0 w0 h0) (Rectangle x y w h)
+ = S.RationalRect ((fi x-fi x0) % fi w0)
+ ((fi y-fi y0) % fi h0)
+ (fi w % fi w0)
+ (fi h % fi h0)
{- Querying stuff -}
-getScreenRect :: X Rectangle
-getScreenRect = gets $ screenRect . S.screenDetail
- . S.current . windowset
+stackContents :: Maybe (S.Stack w) -> [w]
+stackContents = maybe [] S.integrate
-getLayoutWindows :: X [Window]
-getLayoutWindows = gets $ maybe [] S.integrate . S.stack
- . S.workspace . S.current . windowset
+screenInfo :: S.Screen i l a sid ScreenDetail -> (S.Workspace i l a, Rectangle)
+screenInfo (S.Screen { S.workspace = ws, S.screenDetail = (SD s)}) = (ws, s)
getWindowRectangle :: Window -> X Rectangle
getWindowRectangle window
@@ -257,38 +294,48 @@ getWindowRectangle window
return $ Rectangle x y (w + 2*b) (h + 2*b)
-getFloats :: X [Window]
-getFloats = gets $ M.keys . S.floating . windowset
+getAllRectangles :: X (M.Map Window Rectangle)
+getAllRectangles = do ws <- gets windowset
+ let allWindows = join $ map (stackContents . S.stack)
+ $ (S.workspace . S.current) ws
+ : (map S.workspace . S.visible) ws
+ ++ S.hidden ws
+ allRects <- mapM getWindowRectangle allWindows
+
+ return $ M.fromList $ zip allWindows allRects
+
+organizeClients :: S.Workspace a b Window -> Window -> [Window] -> [Window]
+organizeClients ws w floats
+ = let (floatCs, layoutCs) = partition (`elem` floats) $ filter (/= w)
+ $ stackContents $ S.stack ws
+ in reverse layoutCs ++ reverse floatCs
+ -- About the ordering: the smart algorithm will overlap windows
+ -- starting ith the head of the list. So:
+ -- - we put the non-floating windows first since they'll
+ -- probably be below the floating ones,
+ -- - we reverse the lists, since the newer/more important
+ -- windows are usually near the head.
getPointer :: Window -> X (Position, Position)
getPointer window = do d <- asks display
(_,_,_,x,y,_,_,_) <- io $ queryPointer d window
return (fi x,fi y)
--- | Return values are, in order: screen's rectangle, window's rectangle,
+-- | Return values are, in order: window's rectangle,
-- other windows' rectangles and pointer's coordinates.
-getNecessaryData :: Window -> X (Rectangle, Rectangle, [Rectangle], (Position, Position))
-getNecessaryData window
- = do s <- getScreenRect
- r <- getWindowRectangle window
- -- The window to be place may or may not
- -- have a border depending on whether it
- -- is already mapped.
+getNecessaryData :: Window
+ -> S.Workspace WorkspaceId (Layout Window) Window
+ -> [Window]
+ -> X (Rectangle, [Rectangle], (Position, Position))
+getNecessaryData window ws floats
+ = do r <- getWindowRectangle window
- layoutRects <- fmap (filter (/= window)) getLayoutWindows
- >>= mapM getWindowRectangle
- floatRects <- fmap (filter (/= window)) getFloats
- >>= mapM getWindowRectangle
- let rs = reverse $ floatRects ++ layoutRects
- -- Clients inside of the layout
- -- will be ignored first when
- -- using smart placement.
- -- We also reverse the list because it seems
- -- the clients most recently added are at the front.
+ rs <- return (organizeClients ws window floats)
+ >>= mapM getWindowRectangle
+
pointer <- getPointer window
- return (s, r, rs, pointer)
-
+ return (r, rs, pointer)