----------------------------------------------------------------------------- -- | -- Module : XMonadContrib.UpdatePointer -- Copyright : (c) Robert Marlow -- License : BSD3-style (see LICENSE) -- -- Maintainer : Robert Marlow -- Stability : stable -- Portability : portable -- -- Causes the pointer to follow whichever window focus changes to. Compliments -- the idea of switching focus as the mouse crosses window boundaries to -- keep the mouse near the currently focused window -- ----------------------------------------------------------------------------- module XMonad.Actions.UpdatePointer ( -- * Usage -- $usage updatePointer , PointerPosition (..) ) where import XMonad import Control.Monad import XMonad.StackSet (member, peek, screenDetail, current) import Data.Maybe -- $usage -- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@: -- -- > import XMonad -- > import XMonad.Actions.UpdatePointer -- -- Enable it by including it in your logHook definition. Eg: -- -- > logHook = updatePointer Nearest -- -- which will move the pointer to the nearest point of a newly focused window, or -- -- > logHook = updatePointer (Relative 0.5 0.5) -- -- which will move the pointer to the center of a newly focused window. -- -- To use this with an existing logHook, use >> : -- -- > logHook = dynamicLog -- > >> updatePointer (Relative 1 1) -- -- which moves the pointer to the bottom-right corner of the focused window. data PointerPosition = Nearest | Relative Rational Rational | TowardsCentre Rational Rational deriving (Read,Show) -- | Update the pointer's location to the currently focused -- window or empty screen unless it's already there, or unless the user was changing -- focus with the mouse updatePointer :: PointerPosition -> X () updatePointer p = do ws <- gets windowset dpy <- asks display rect <- case peek ws of Nothing -> return $ (screenRect . screenDetail .current) ws Just w -> windowAttributesToRectangle `fmap` io (getWindowAttributes dpy w) root <- asks theRoot mouseIsMoving <- asks mouseFocused (_sameRoot,_,currentWindow,rootx,rooty,_,_,_) <- io $ queryPointer dpy root drag <- gets dragging unless (pointWithin (fi rootx) (fi rooty) rect || mouseIsMoving || isJust drag || not (currentWindow `member` ws || currentWindow == none)) $ case p of Nearest -> do let x = moveWithin (fi rootx) (rect_x rect) (fi (rect_x rect) + fi (rect_width rect)) y = moveWithin (fi rooty) (rect_y rect) (fi (rect_y rect) + fi (rect_height rect)) io $ warpPointer dpy none root 0 0 0 0 x y TowardsCentre xfrc yfrc -> do let cx = fi (rect_width rect) / 2 + fi (rect_x rect) cy = fi (rect_height rect) / 2 + fi (rect_y rect) x,y,cx,cy :: Rational x = moveWithin (fi rootx) (fi $ rect_x rect) (fi (rect_x rect) + fi (rect_width rect)) y = moveWithin (fi rooty) (fi $ rect_y rect) (fi (rect_y rect) + fi (rect_height rect)) io $ warpPointer dpy none root 0 0 0 0 (round $ x + xfrc*(cx-x)) (round $ y + yfrc*(cy-y)) Relative h v -> io $ warpPointer dpy none root 0 0 0 0 (rect_x rect + fraction h (rect_width rect)) (rect_y rect + fraction v (rect_height rect)) where fraction x y = floor (x * fromIntegral y) windowAttributesToRectangle :: WindowAttributes -> Rectangle windowAttributesToRectangle wa = Rectangle (fi (wa_x wa)) (fi (wa_y wa)) (fi (wa_width wa)) (fi (wa_height wa)) moveWithin :: Ord a => a -> a -> a -> a moveWithin now lower upper = if now < lower then lower else if now > upper then upper else now fi :: (Num b, Integral a) => a -> b fi = fromIntegral