-----------------------------------------------------------------------------
-- |
-- Module       : XMonad.Actions.FloatKeys
-- Copyright    : (c) Karsten Schoelzel <kuser@gmx.de>
-- License      : BSD
--
-- Maintainer   : Karsten Schoelzel <kuser@gmx.de>
-- Stability    : stable
-- Portability  : unportable
--
-- Move and resize floating windows.
-----------------------------------------------------------------------------

module XMonad.Actions.FloatKeys (
                -- * Usage
                -- $usage
                keysMoveWindow,
                keysMoveWindowTo,
                keysResizeWindow,
                keysAbsResizeWindow,
                P, G,
                ) where

import XMonad

-- $usage
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
--
-- >    import XMonad.Actions.FloatKeys
--
-- Then add appropriate key bindings, for example:
--
-- >  , ((modm,               xK_d     ), withFocused (keysResizeWindow (-10,-10) (1,1)))
-- >  , ((modm,               xK_s     ), withFocused (keysResizeWindow (10,10) (1,1)))
-- >  , ((modm .|. shiftMask, xK_d     ), withFocused (keysAbsResizeWindow (-10,-10) (1024,752)))
-- >  , ((modm .|. shiftMask, xK_s     ), withFocused (keysAbsResizeWindow (10,10) (1024,752)))
-- >  , ((modm,               xK_a     ), withFocused (keysMoveWindowTo (512,384) (1%2,1%2)))
--
-- For detailed instructions on editing your key bindings, see
-- "XMonad.Doc.Extending#Editing_key_bindings".

-- | @keysMoveWindow (dx, dy)@ moves the window by @dx@ pixels to the
--   right and @dy@ pixels down.
keysMoveWindow :: D -> Window -> X ()
keysMoveWindow :: D -> Window -> X ()
keysMoveWindow (dx :: Window
dx,dy :: Window
dy) w :: Window
w = X Bool -> X () -> X ()
whenX (Window -> X Bool
isClient Window
w) (X () -> X ()) -> X () -> X ()
forall a b. (a -> b) -> a -> b
$ (Display -> X ()) -> X ()
forall a. (Display -> X a) -> X a
withDisplay ((Display -> X ()) -> X ()) -> (Display -> X ()) -> X ()
forall a b. (a -> b) -> a -> b
$ \d :: Display
d -> do
    IO () -> X ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO () -> X ()) -> IO () -> X ()
forall a b. (a -> b) -> a -> b
$ Display -> Window -> IO ()
raiseWindow Display
d Window
w
    WindowAttributes
wa <- IO WindowAttributes -> X WindowAttributes
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO WindowAttributes -> X WindowAttributes)
-> IO WindowAttributes -> X WindowAttributes
forall a b. (a -> b) -> a -> b
$ Display -> Window -> IO WindowAttributes
getWindowAttributes Display
d Window
w
    IO () -> X ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO () -> X ()) -> IO () -> X ()
forall a b. (a -> b) -> a -> b
$ Display -> Window -> Position -> Position -> IO ()
moveWindow Display
d Window
w (Window -> Position
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CInt -> Window
forall a b. (Integral a, Num b) => a -> b
fromIntegral (WindowAttributes -> CInt
wa_x WindowAttributes
wa) Window -> Window -> Window
forall a. Num a => a -> a -> a
+ Window
dx))
                        (Window -> Position
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CInt -> Window
forall a b. (Integral a, Num b) => a -> b
fromIntegral (WindowAttributes -> CInt
wa_y WindowAttributes
wa) Window -> Window -> Window
forall a. Num a => a -> a -> a
+ Window
dy))
    Window -> X ()
float Window
w

-- | @keysMoveWindowTo (x, y) (gx, gy)@ moves the window relative
--   point @(gx, gy)@ to the point @(x,y)@, where @(gx,gy)@ gives a
--   position relative to the window border, i.e.  @gx = 0@ is the left
--   border, @gx = 1@ is the right border, @gy = 0@ is the top border, and
--   @gy = 1@ the bottom border.
--
--   For example, on a 1024x768 screen:
--
-- > keysMoveWindowTo (512,384) (1%2, 1%2) -- center the window on screen
-- > keysMoveWindowTo (1024,0) (1, 0)      -- put window in the top right corner
keysMoveWindowTo :: P -> G -> Window -> X ()
keysMoveWindowTo :: P -> G -> Window -> X ()
keysMoveWindowTo (x :: Position
x,y :: Position
y) (gx :: Rational
gx, gy :: Rational
gy) w :: Window
w = X Bool -> X () -> X ()
whenX (Window -> X Bool
isClient Window
w) (X () -> X ()) -> X () -> X ()
forall a b. (a -> b) -> a -> b
$ (Display -> X ()) -> X ()
forall a. (Display -> X a) -> X a
withDisplay ((Display -> X ()) -> X ()) -> (Display -> X ()) -> X ()
forall a b. (a -> b) -> a -> b
$ \d :: Display
d -> do
    IO () -> X ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO () -> X ()) -> IO () -> X ()
forall a b. (a -> b) -> a -> b
$ Display -> Window -> IO ()
raiseWindow Display
d Window
w
    WindowAttributes
wa <- IO WindowAttributes -> X WindowAttributes
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO WindowAttributes -> X WindowAttributes)
-> IO WindowAttributes -> X WindowAttributes
forall a b. (a -> b) -> a -> b
$ Display -> Window -> IO WindowAttributes
getWindowAttributes Display
d Window
w
    IO () -> X ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO () -> X ()) -> IO () -> X ()
forall a b. (a -> b) -> a -> b
$ Display -> Window -> Position -> Position -> IO ()
moveWindow Display
d Window
w (Position
x Position -> Position -> Position
forall a. Num a => a -> a -> a
- Rational -> Position
forall a b. (RealFrac a, Integral b) => a -> b
round (Rational
gx Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
* CInt -> Rational
forall a b. (Integral a, Num b) => a -> b
fromIntegral (WindowAttributes -> CInt
wa_width WindowAttributes
wa)))
                        (Position
y Position -> Position -> Position
forall a. Num a => a -> a -> a
- Rational -> Position
forall a b. (RealFrac a, Integral b) => a -> b
round (Rational
gy Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
* CInt -> Rational
forall a b. (Integral a, Num b) => a -> b
fromIntegral (WindowAttributes -> CInt
wa_height WindowAttributes
wa)))
    Window -> X ()
float Window
w

type G = (Rational, Rational)
type P = (Position, Position)

-- | @keysResizeWindow (dx, dy) (gx, gy)@ changes the width by @dx@
--   and the height by @dy@, leaving the window-relative point @(gx,
--   gy)@ fixed.
--
--   For example:
--
-- > keysResizeWindow (10, 0) (0, 0)      -- make the window 10 pixels larger to the right
-- > keysResizeWindow (10, 0) (0, 1%2)    -- does the same, unless sizeHints are applied
-- > keysResizeWindow (10, 10) (1%2, 1%2) -- add 5 pixels on each side
-- > keysResizeWindow (-10, -10) (0, 1)   -- shrink the window in direction of the bottom-left corner
keysResizeWindow :: D -> G -> Window -> X ()
keysResizeWindow :: D -> G -> Window -> X ()
keysResizeWindow = (SizeHints -> P -> D -> D -> G -> (P, D))
-> D -> G -> Window -> X ()
forall a b.
(SizeHints -> P -> D -> a -> b -> (P, D))
-> a -> b -> Window -> X ()
keysMoveResize SizeHints -> P -> D -> D -> G -> (P, D)
keysResizeWindow'

-- | @keysAbsResizeWindow (dx, dy) (ax, ay)@ changes the width by @dx@
--   and the height by @dy@, leaving the screen absolute point @(ax,
--   ay)@ fixed.
--
--   For example:
--
-- > keysAbsResizeWindow (10, 10) (0, 0)   -- enlarge the window; if it is not in the top-left corner it will also be moved down and to the right.
keysAbsResizeWindow :: D -> D -> Window -> X ()
keysAbsResizeWindow :: D -> D -> Window -> X ()
keysAbsResizeWindow = (SizeHints -> P -> D -> D -> D -> (P, D))
-> D -> D -> Window -> X ()
forall a b.
(SizeHints -> P -> D -> a -> b -> (P, D))
-> a -> b -> Window -> X ()
keysMoveResize SizeHints -> P -> D -> D -> D -> (P, D)
keysAbsResizeWindow'

keysAbsResizeWindow' :: SizeHints -> P -> D -> D -> D -> (P,D)
keysAbsResizeWindow' :: SizeHints -> P -> D -> D -> D -> (P, D)
keysAbsResizeWindow' sh :: SizeHints
sh (x :: Position
x,y :: Position
y) (w :: Window
w,h :: Window
h) (dx :: Window
dx,dy :: Window
dy) (ax :: Window
ax, ay :: Window
ay) = ((Rational -> Position
forall a b. (RealFrac a, Integral b) => a -> b
round Rational
nx, Rational -> Position
forall a b. (RealFrac a, Integral b) => a -> b
round Rational
ny), (Window
nw, Window
nh))
    where
        (nw :: Window
nw, nh :: Window
nh) = SizeHints -> D -> D
forall a. Integral a => SizeHints -> (a, a) -> D
applySizeHintsContents SizeHints
sh (Window
w Window -> Window -> Window
forall a. Num a => a -> a -> a
+ Window
dx, Window
h Window -> Window -> Window
forall a. Num a => a -> a -> a
+ Window
dy)
        nx :: Rational
        nx :: Rational
nx = Window -> Rational
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Window
ax Window -> Window -> Window
forall a. Num a => a -> a -> a
* Window
w Window -> Window -> Window
forall a. Num a => a -> a -> a
+ Window
nw Window -> Window -> Window
forall a. Num a => a -> a -> a
* (Position -> Window
forall a b. (Integral a, Num b) => a -> b
fromIntegral Position
x Window -> Window -> Window
forall a. Num a => a -> a -> a
- Window
ax)) Rational -> Rational -> Rational
forall a. Fractional a => a -> a -> a
/ Window -> Rational
forall a b. (Integral a, Num b) => a -> b
fromIntegral Window
w
        ny :: Rational
        ny :: Rational
ny = Window -> Rational
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Window
ay Window -> Window -> Window
forall a. Num a => a -> a -> a
* Window
h Window -> Window -> Window
forall a. Num a => a -> a -> a
+ Window
nh Window -> Window -> Window
forall a. Num a => a -> a -> a
* (Position -> Window
forall a b. (Integral a, Num b) => a -> b
fromIntegral Position
y Window -> Window -> Window
forall a. Num a => a -> a -> a
- Window
ay)) Rational -> Rational -> Rational
forall a. Fractional a => a -> a -> a
/ Window -> Rational
forall a b. (Integral a, Num b) => a -> b
fromIntegral Window
h

keysResizeWindow' :: SizeHints -> P -> D -> D -> G -> (P,D)
keysResizeWindow' :: SizeHints -> P -> D -> D -> G -> (P, D)
keysResizeWindow' sh :: SizeHints
sh (x :: Position
x,y :: Position
y) (w :: Window
w,h :: Window
h) (dx :: Window
dx,dy :: Window
dy) (gx :: Rational
gx, gy :: Rational
gy) = ((Position
nx, Position
ny), (Window
nw, Window
nh))
    where
        (nw :: Window
nw, nh :: Window
nh) = SizeHints -> D -> D
forall a. Integral a => SizeHints -> (a, a) -> D
applySizeHintsContents SizeHints
sh (Window
w Window -> Window -> Window
forall a. Num a => a -> a -> a
+ Window
dx, Window
h Window -> Window -> Window
forall a. Num a => a -> a -> a
+ Window
dy)
        nx :: Position
nx = Rational -> Position
forall a b. (RealFrac a, Integral b) => a -> b
round (Rational -> Position) -> Rational -> Position
forall a b. (a -> b) -> a -> b
$ Position -> Rational
forall a b. (Integral a, Num b) => a -> b
fromIntegral Position
x Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
+ Rational
gx Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
* Window -> Rational
forall a b. (Integral a, Num b) => a -> b
fromIntegral Window
w Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
- Rational
gx Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
* Window -> Rational
forall a b. (Integral a, Num b) => a -> b
fromIntegral Window
nw
        ny :: Position
ny = Rational -> Position
forall a b. (RealFrac a, Integral b) => a -> b
round (Rational -> Position) -> Rational -> Position
forall a b. (a -> b) -> a -> b
$ Position -> Rational
forall a b. (Integral a, Num b) => a -> b
fromIntegral Position
y Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
+ Rational
gy Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
* Window -> Rational
forall a b. (Integral a, Num b) => a -> b
fromIntegral Window
h Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
- Rational
gy Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
* Window -> Rational
forall a b. (Integral a, Num b) => a -> b
fromIntegral Window
nh

keysMoveResize :: (SizeHints -> P -> D -> a -> b -> (P,D)) -> a -> b -> Window -> X ()
keysMoveResize :: (SizeHints -> P -> D -> a -> b -> (P, D))
-> a -> b -> Window -> X ()
keysMoveResize f :: SizeHints -> P -> D -> a -> b -> (P, D)
f move :: a
move resize :: b
resize w :: Window
w = X Bool -> X () -> X ()
whenX (Window -> X Bool
isClient Window
w) (X () -> X ()) -> X () -> X ()
forall a b. (a -> b) -> a -> b
$ (Display -> X ()) -> X ()
forall a. (Display -> X a) -> X a
withDisplay ((Display -> X ()) -> X ()) -> (Display -> X ()) -> X ()
forall a b. (a -> b) -> a -> b
$ \d :: Display
d -> do
    IO () -> X ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO () -> X ()) -> IO () -> X ()
forall a b. (a -> b) -> a -> b
$ Display -> Window -> IO ()
raiseWindow Display
d Window
w
    WindowAttributes
wa <- IO WindowAttributes -> X WindowAttributes
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO WindowAttributes -> X WindowAttributes)
-> IO WindowAttributes -> X WindowAttributes
forall a b. (a -> b) -> a -> b
$ Display -> Window -> IO WindowAttributes
getWindowAttributes Display
d Window
w
    SizeHints
sh <- IO SizeHints -> X SizeHints
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO SizeHints -> X SizeHints) -> IO SizeHints -> X SizeHints
forall a b. (a -> b) -> a -> b
$ Display -> Window -> IO SizeHints
getWMNormalHints Display
d Window
w
    let wa_dim :: D
wa_dim = (CInt -> Window
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CInt -> Window) -> CInt -> Window
forall a b. (a -> b) -> a -> b
$ WindowAttributes -> CInt
wa_width WindowAttributes
wa, CInt -> Window
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CInt -> Window) -> CInt -> Window
forall a b. (a -> b) -> a -> b
$ WindowAttributes -> CInt
wa_height WindowAttributes
wa)
        wa_pos :: P
wa_pos = (CInt -> Position
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CInt -> Position) -> CInt -> Position
forall a b. (a -> b) -> a -> b
$ WindowAttributes -> CInt
wa_x WindowAttributes
wa, CInt -> Position
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CInt -> Position) -> CInt -> Position
forall a b. (a -> b) -> a -> b
$ WindowAttributes -> CInt
wa_y WindowAttributes
wa)
        (wn_pos :: P
wn_pos, wn_dim :: D
wn_dim) = SizeHints -> P -> D -> a -> b -> (P, D)
f SizeHints
sh P
wa_pos D
wa_dim a
move b
resize
    IO () -> X ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO () -> X ()) -> IO () -> X ()
forall a b. (a -> b) -> a -> b
$ Display -> Window -> Window -> Window -> IO ()
resizeWindow Display
d Window
w (Window -> Window -> IO ()) -> D -> IO ()
forall a b c. (a -> b -> c) -> (a, b) -> c
`uncurry` D
wn_dim
    IO () -> X ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO () -> X ()) -> IO () -> X ()
forall a b. (a -> b) -> a -> b
$ Display -> Window -> Position -> Position -> IO ()
moveWindow Display
d Window
w (Position -> Position -> IO ()) -> P -> IO ()
forall a b c. (a -> b -> c) -> (a, b) -> c
`uncurry` P
wn_pos
    Window -> X ()
float Window
w