XMonad.Actions.UpdatePointer: generalise updatePointer

This commit is contained in:
Liyang HU 2013-07-30 07:10:07 +00:00
parent 2fe30c6730
commit fd23bd692b
2 changed files with 47 additions and 53 deletions

View File

@ -19,12 +19,12 @@ module XMonad.Actions.UpdatePointer
-- * Usage -- * Usage
-- $usage -- $usage
updatePointer updatePointer
, PointerPosition (..)
) )
where where
import XMonad import XMonad
import XMonad.Util.XUtils (fi) import XMonad.Util.XUtils (fi)
import Control.Arrow
import Control.Monad import Control.Monad
import XMonad.StackSet (member, peek, screenDetail, current) import XMonad.StackSet (member, peek, screenDetail, current)
import Data.Maybe import Data.Maybe
@ -35,36 +35,32 @@ import Data.Maybe
-- > import XMonad -- > import XMonad
-- > import XMonad.Actions.UpdatePointer -- > import XMonad.Actions.UpdatePointer
-- --
-- Enable it by including it in your logHook definition. Eg: -- Enable it by including it in your logHook definition, e.g.:
-- --
-- > logHook = updatePointer Nearest -- > logHook = updatePointer (0.5, 0.5) (1, 1)
-- --
-- which will move the pointer to the nearest point of a newly focused window, or -- which will move the pointer to the nearest point of a newly focused
-- window. The first argument establishes a reference point within the
-- newly-focused window, while the second argument linearly interpolates
-- between said reference point and the edges of the newly-focused window to
-- obtain a bounding box for the pointer.
-- --
-- > logHook = updatePointer (Relative 0.5 0.5) -- > logHook = updatePointer (0.5, 0.5) (0, 0) -- exact centre of window
-- -- > logHook = updatePointer (0.25, 0.25) (0.25, 0.25) -- near the top-left
-- which will move the pointer to the center of a newly focused window, or -- > logHook = updatePointer (0.5, 0.5) (1.1, 1.1) -- within 110% of the edge
--
-- > logHook = updatePointer (TowardsCentre 0.75 0.75)
--
-- which will linearly interpolate the pointer between 'Nearest' and ¾ of
-- the way towards the centre of the window.
-- --
-- To use this with an existing logHook, use >> : -- To use this with an existing logHook, use >> :
-- --
-- > logHook = dynamicLog -- > logHook = dynamicLog
-- > >> updatePointer (Relative 1 1) -- > >> updatePointer (1, 1) (0, 0)
-- --
-- which moves the pointer to the bottom-right corner of the focused window. -- 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 -- | Update the pointer's location to the currently focused
-- window or empty screen unless it's already there, or unless the user was changing -- window or empty screen unless it's already there, or unless the user was changing
-- focus with the mouse -- focus with the mouse
updatePointer :: PointerPosition -> X () updatePointer :: (Rational, Rational) -> (Rational, Rational) -> X ()
updatePointer p = do updatePointer refPos ratio = do
ws <- gets windowset ws <- gets windowset
dpy <- asks display dpy <- asks display
rect <- case peek ws of rect <- case peek ws of
@ -72,39 +68,37 @@ updatePointer p = do
Just w -> windowAttributesToRectangle `fmap` io (getWindowAttributes dpy w) Just w -> windowAttributesToRectangle `fmap` io (getWindowAttributes dpy w)
root <- asks theRoot root <- asks theRoot
mouseIsMoving <- asks mouseFocused mouseIsMoving <- asks mouseFocused
(_sameRoot,_,currentWindow,rootx,rooty,_,_,_) <- io $ queryPointer dpy root (_sameRoot,_,currentWindow,rootX,rootY,_,_,_) <- io $ queryPointer dpy root
drag <- gets dragging drag <- gets dragging
unless (pointWithin (fi rootx) (fi rooty) rect unless (pointWithin (fi rootX) (fi rootY) rect
|| mouseIsMoving || mouseIsMoving
|| isJust drag || isJust drag
|| not (currentWindow `member` ws || currentWindow == none)) $ || not (currentWindow `member` ws || currentWindow == none)) $ let
case p of -- focused rectangle
Nearest -> do (rectX, rectY) = (rect_x &&& rect_y) rect
let x = moveWithin (fi rootx) (rect_x rect) (fi (rect_x rect) + fi (rect_width rect)) (rectW, rectH) = (fi . rect_width &&& fi . rect_height) rect
y = moveWithin (fi rooty) (rect_y rect) (fi (rect_y rect) + fi (rect_height rect)) -- reference position, with (0,0) and (1,1) being top-left and bottom-right
io $ warpPointer dpy none root 0 0 0 0 x y refX = lerp (fst refPos) rectX (rectX + rectW)
TowardsCentre xfrc yfrc -> do refY = lerp (snd refPos) rectY (rectY + rectH)
let cx = fi (rect_width rect) / 2 + fi (rect_x rect) -- final pointer bounds, lerped *outwards* from reference position
cy = fi (rect_height rect) / 2 + fi (rect_y rect) boundsX = join (***) (lerp (fst ratio) refX) (rectX, rectX + rectW)
x,y,cx,cy :: Rational boundsY = join (***) (lerp (snd ratio) refY) (rectY, rectY + rectH)
x = moveWithin (fi rootx) (fi $ rect_x rect) (fi (rect_x rect) + fi (rect_width rect)) -- ideally we ought to move the pointer in a straight line towards the
y = moveWithin (fi rooty) (fi $ rect_y rect) (fi (rect_y rect) + fi (rect_height rect)) -- reference point until it is within the above bounds, but…
io $ warpPointer dpy none root 0 0 0 0 (round $ x + xfrc*(cx-x)) (round $ y + yfrc*(cy-y)) in io $ warpPointer dpy none root 0 0 0 0
Relative h v -> (round . clip boundsX $ fi rootX)
io $ warpPointer dpy none root 0 0 0 0 (round . clip boundsY $ fi rootY)
(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 :: WindowAttributes -> Rectangle
windowAttributesToRectangle wa = Rectangle (fi (wa_x wa)) windowAttributesToRectangle wa = Rectangle (fi (wa_x wa))
(fi (wa_y wa)) (fi (wa_y wa))
(fi (wa_width wa + 2 * wa_border_width wa)) (fi (wa_width wa + 2 * wa_border_width wa))
(fi (wa_height wa + 2 * wa_border_width wa)) (fi (wa_height wa + 2 * wa_border_width wa))
moveWithin :: Ord a => a -> a -> a -> a
moveWithin now lower upper = lerp :: (RealFrac r, Real a, Real b) => r -> a -> b -> r
if now < lower lerp r a b = (1 - r) * realToFrac a + r * realToFrac b
then lower
else if now > upper clip :: Ord a => (a, a) -> a -> a
then upper clip (lower, upper) x = if x < lower then lower
else now else if x > upper then upper else x

View File

@ -28,7 +28,7 @@ import XMonad
import qualified XMonad.StackSet as W import qualified XMonad.StackSet as W
import XMonad.Layout.LayoutModifier import XMonad.Layout.LayoutModifier
import XMonad.Actions.UpdatePointer(updatePointer, PointerPosition(TowardsCentre)) import XMonad.Actions.UpdatePointer (updatePointer)
import Data.Monoid(All(..)) import Data.Monoid(All(..))
import qualified Data.Map as M import qualified Data.Map as M
@ -75,12 +75,12 @@ swap (W.Stack f u d) focused
-- This eventHook does nothing when there are floating windows on the current -- This eventHook does nothing when there are floating windows on the current
-- workspace. -- workspace.
promoteWarp :: Event -> X All promoteWarp :: Event -> X All
promoteWarp = promoteWarp' (TowardsCentre 0.15 0.15) promoteWarp = promoteWarp' (0.5, 0.5) (0.85, 0.85)
-- | promoteWarp' allows you to specify an arbitrary PointerPosition to apply -- | promoteWarp' allows you to specify an arbitrary pair of arguments to
-- when the mouse enters another window. -- pass to 'updatePointer' when the mouse enters another window.
promoteWarp' :: PointerPosition -> Event -> X All promoteWarp' :: (Rational, Rational) -> (Rational, Rational) -> Event -> X All
promoteWarp' pos e@(CrossingEvent {ev_window = w, ev_event_type = t}) promoteWarp' refPos ratio e@(CrossingEvent {ev_window = w, ev_event_type = t})
| t == enterNotify && ev_mode e == notifyNormal = do | t == enterNotify && ev_mode e == notifyNormal = do
ws <- gets windowset ws <- gets windowset
let foc = W.peek ws let foc = W.peek ws
@ -88,10 +88,10 @@ promoteWarp' pos e@(CrossingEvent {ev_window = w, ev_event_type = t})
wsFloats = M.filterWithKey (\k _ -> k `elem` st) $ W.floating ws wsFloats = M.filterWithKey (\k _ -> k `elem` st) $ W.floating ws
if Just w /= foc && M.null wsFloats then do if Just w /= foc && M.null wsFloats then do
windows (W.swapMaster . W.focusWindow w) windows (W.swapMaster . W.focusWindow w)
updatePointer pos updatePointer refPos ratio
return $ All False return $ All False
else return $ All True else return $ All True
promoteWarp' _ _ = return $ All True promoteWarp' _ _ _ = return $ All True
-- | Another event hook to override the focusFollowsMouse and make the pointer -- | Another event hook to override the focusFollowsMouse and make the pointer
-- only follow if a given condition is satisfied. This could be used to disable -- only follow if a given condition is satisfied. This could be used to disable