mirror of
https://github.com/xmonad/xmonad-contrib.git
synced 2025-07-26 09:41:52 -07:00
Compare commits
313 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
6f6e9692c2 | ||
|
beda65a760 | ||
|
79ab1d5de1 | ||
|
06a1322366 | ||
|
5417286d6a | ||
|
4832f0fc7d | ||
|
74a03cd8fb | ||
|
9f16a427e6 | ||
|
fc1da0d701 | ||
|
6e91396fa5 | ||
|
0e713d57c1 | ||
|
8f8e650537 | ||
|
00bae8bafa | ||
|
be635001de | ||
|
3513c8386b | ||
|
6d4ad7f431 | ||
|
4abbb620a4 | ||
|
37a0dba16e | ||
|
bcab2509d3 | ||
|
9ac6c9a24d | ||
|
45db2ebfbe | ||
|
721cda38cc | ||
|
f655307a1c | ||
|
8d4c0a5e13 | ||
|
f61ac3a174 | ||
|
f28f32f7ed | ||
|
0e2aef4deb | ||
|
41a53e7d15 | ||
|
ca29a33f56 | ||
|
d330dcae24 | ||
|
6ef8fd353d | ||
|
2b31698e15 | ||
|
79eb2582c4 | ||
|
70e968c354 | ||
|
adc029566e | ||
|
cd43a200bf | ||
|
f0e835ebe2 | ||
|
831168d701 | ||
|
064f117018 | ||
|
1edc2752c7 | ||
|
06998efa45 | ||
|
ec87f7d62d | ||
|
5e0a65ea63 | ||
|
bf36bb785a | ||
|
4b67243cac | ||
|
a08fd578ee | ||
|
3e0be7dd1b | ||
|
f983084b63 | ||
|
dc07d902d9 | ||
|
e0a5d16e40 | ||
|
97537c8ad3 | ||
|
a8677c001a | ||
|
19b55d74a7 | ||
|
9ba60f1952 | ||
|
eb10d679e6 | ||
|
f0925b5a28 | ||
|
63b6d7c225 | ||
|
1ef2eb63b9 | ||
|
e9a432298c | ||
|
4509a8b696 | ||
|
fb7539d74b | ||
|
ed43c38519 | ||
|
697f387a39 | ||
|
0bb00440dc | ||
|
097d7367bb | ||
|
104cc6ba25 | ||
|
f6fa7e509f | ||
|
563266f3a5 | ||
|
df9655c662 | ||
|
27a7bcbd6e | ||
|
77f52bc84d | ||
|
832d435dee | ||
|
066db410b0 | ||
|
66b8ad46d0 | ||
|
bf2fc75035 | ||
|
e6158615cb | ||
|
cce2c7c839 | ||
|
41b58ed499 | ||
|
3bc9c11d97 | ||
|
8aa3450f83 | ||
|
2d193a4304 | ||
|
5d949197b2 | ||
|
2b9b770e12 | ||
|
b5b9a3dc67 | ||
|
5aa7d3635e | ||
|
218b041fa9 | ||
|
2bebf54795 | ||
|
bc00f63b79 | ||
|
02eed22659 | ||
|
2733268980 | ||
|
eac4b6a8d2 | ||
|
d783e96352 | ||
|
f98c0c83a1 | ||
|
e2113acd35 | ||
|
33046439d6 | ||
|
e8e6cfcc3a | ||
|
857bf537b5 | ||
|
832ea65e75 | ||
|
c4cae4839d | ||
|
cbd978c3b5 | ||
|
a9cb7bf67a | ||
|
63e5b222b8 | ||
|
c91f54b2fb | ||
|
e80f1df518 | ||
|
afecca6561 | ||
|
28e0adcde7 | ||
|
9a207f0512 | ||
|
491e21b3b9 | ||
|
0ec25c9fee | ||
|
8b6135f868 | ||
|
8fe80758a8 | ||
|
cc2fb2c10d | ||
|
5daaf583b7 | ||
|
8be4946bcd | ||
|
15acd55553 | ||
|
4f375b20bf | ||
|
cb9ab874ce | ||
|
be2bbc9202 | ||
|
3bce490813 | ||
|
d13551a49c | ||
|
d38696bcd5 | ||
|
bf398ff356 | ||
|
41a63a5743 | ||
|
a4d5d7ff9b | ||
|
aca86af08a | ||
|
d13dc2ff48 | ||
|
b89dc9da44 | ||
|
a3ba5f1503 | ||
|
f22c4624a3 | ||
|
b09827c2bc | ||
|
218595881f | ||
|
f1ce4e5876 | ||
|
c884dbb74b | ||
|
fdaeaa18de | ||
|
4e3a4a2c8b | ||
|
dd1dc7f2bc | ||
|
8addbabe49 | ||
|
b805a6fa42 | ||
|
2a73df7a45 | ||
|
5cd48cac7c | ||
|
d65e40f09d | ||
|
f0c0f4d5c3 | ||
|
f8a4dd9503 | ||
|
1f13242164 | ||
|
48e74031f9 | ||
|
99788c8780 | ||
|
96a63b1dfa | ||
|
9c3b472470 | ||
|
7e6fed9bf0 | ||
|
02671904e1 | ||
|
7aafd381d3 | ||
|
a6c4f7659a | ||
|
141c3cba96 | ||
|
1e1f2c6770 | ||
|
1893d67d09 | ||
|
a0ae1e8bba | ||
|
32debd47e8 | ||
|
54f030faf8 | ||
|
8362a5b81b | ||
|
6bd66b885a | ||
|
dce0b17420 | ||
|
0cb6ac2910 | ||
|
2d84da7fdd | ||
|
9847e0da5e | ||
|
0f15f2fa7e | ||
|
5733601ad3 | ||
|
10c984ce44 | ||
|
65b8c39fe7 | ||
|
04f440d804 | ||
|
d4d17e6576 | ||
|
e582ae5a4e | ||
|
41d8b2b22a | ||
|
708b8a7d96 | ||
|
f541602f0b | ||
|
be2be3f2f6 | ||
|
54dc0ac732 | ||
|
d823d9e68c | ||
|
1b521f0064 | ||
|
00c3775a06 | ||
|
cc84480c99 | ||
|
6d0b082eb7 | ||
|
67cdbb0ba8 | ||
|
fc984af98d | ||
|
9b7125f965 | ||
|
85913caf2e | ||
|
aa435aa5c8 | ||
|
72e2c5d0b4 | ||
|
eddd655b49 | ||
|
c5b5db500b | ||
|
f6479ee0a8 | ||
|
451ced82d9 | ||
|
3f3aff573a | ||
|
49bb2655ff | ||
|
6703453f26 | ||
|
1fd4489ae6 | ||
|
bc425079f2 | ||
|
bd7e2004a0 | ||
|
160d961951 | ||
|
75cf2c1e30 | ||
|
50c22cc05b | ||
|
9d27bdb17d | ||
|
2eb98c1437 | ||
|
a3f931262b | ||
|
af22761a10 | ||
|
823ea115ae | ||
|
ea61ad0f47 | ||
|
071257d475 | ||
|
a5e11a14b6 | ||
|
99e839228e | ||
|
cc0e18af8a | ||
|
7410b9826c | ||
|
71139c1fb3 | ||
|
561855133c | ||
|
4a8c4b74ac | ||
|
552b4f91ab | ||
|
89c2ecb448 | ||
|
06a997aaf9 | ||
|
115cd5af95 | ||
|
0dd1cf1ea8 | ||
|
24b39a40cb | ||
|
b60cdb60f0 | ||
|
cc7d2140b7 | ||
|
e780b6f81e | ||
|
5ce50a3cc0 | ||
|
6bcbd5b871 | ||
|
761b884555 | ||
|
3ad7661897 | ||
|
6d205dda20 | ||
|
e0ca57557b | ||
|
1c0d227c44 | ||
|
32b1e5bdfa | ||
|
13d8b17f97 | ||
|
6686d32f56 | ||
|
3184fd00c7 | ||
|
e431c38c5e | ||
|
156917209e | ||
|
34147551ce | ||
|
e772528912 | ||
|
43edb3a151 | ||
|
1b82ccd21f | ||
|
af526ae23e | ||
|
0234f94be3 | ||
|
5ad49783f2 | ||
|
4cac1c6ebd | ||
|
93ed1c6019 | ||
|
521bb48ad9 | ||
|
15df01705f | ||
|
ac8c6ab633 | ||
|
e8d80d552c | ||
|
15217548ab | ||
|
fb7a2d4ae3 | ||
|
75a3e2e290 | ||
|
a2846292bf | ||
|
92aa7079ea | ||
|
aa7dce7085 | ||
|
ac60c04cb4 | ||
|
2a9c77aa71 | ||
|
98030cb415 | ||
|
1b682cbceb | ||
|
edcba03217 | ||
|
04a5f4392e | ||
|
db4f731f4e | ||
|
57a62ce871 | ||
|
cf07f9caca | ||
|
efc2f1160f | ||
|
ffe5f0cf6f | ||
|
d6d03e84af | ||
|
ed1d90d322 | ||
|
3285ac8bb4 | ||
|
27bbeff92c | ||
|
35c5e80c32 | ||
|
a4f4c5bd39 | ||
|
bbb0e97e61 | ||
|
c1c5b8d08e | ||
|
cd86241cc9 | ||
|
6037b0db21 | ||
|
7e1db48875 | ||
|
509e345947 | ||
|
12939b6c1d | ||
|
aa1581b3d0 | ||
|
b66d1aae33 | ||
|
07793c1bc3 | ||
|
bd3c36d62e | ||
|
f80d3eb250 | ||
|
af08bec754 | ||
|
9fc46d0dfd | ||
|
c1b9f308d9 | ||
|
fbb9605d76 | ||
|
54122382ed | ||
|
9a7dcbbabb | ||
|
4700b44c2c | ||
|
b88f22fc36 | ||
|
d4a0bbbe2c | ||
|
31110d1b45 | ||
|
3c0f793e4a | ||
|
794f70fb04 | ||
|
cfbee439a1 | ||
|
869a3d4a1e | ||
|
d1e2991ccf | ||
|
3022e3c937 | ||
|
01e0a42c17 | ||
|
eed05efeab | ||
|
b39dfa1917 | ||
|
b3822c6faa | ||
|
9826ade99e | ||
|
f22c6aa144 | ||
|
b7872f77f7 | ||
|
c1f1f27da0 | ||
|
70e9b9b2d6 | ||
|
22a7c39107 | ||
|
5c849c1fc6 | ||
|
e78cad6f90 | ||
|
69a75d863e |
84
README
84
README
@@ -1,21 +1,78 @@
|
||||
3rd party xmonad extensions and contributions.
|
||||
xmonad-contrib : third party extensions to the xmonad window manager
|
||||
|
||||
Build and install through Cabal as for other Haskell packages:
|
||||
http://xmonad.org
|
||||
|
||||
runhaskell Setup configure --user --prefix=$HOME
|
||||
runhaskell Setup build
|
||||
runhaskell Setup install --user
|
||||
You need the ghc compiler and xmonad window manager installed in
|
||||
order to use these extensions.
|
||||
|
||||
(You may want to remove the --user flag when installing as root.)
|
||||
For installation and configuration instructions, please see the
|
||||
xmonad website, the documents included with the xmonad source
|
||||
distribution, and online haddock documentation:
|
||||
|
||||
scripts/ contains further external programs useful with xmonad.
|
||||
http://www.xmonad.org/xmonad-docs
|
||||
|
||||
Haskell code contributed to this repo should live under the
|
||||
appropriate subdivision of the 'XMonad.' namespace (currently includes
|
||||
Actions, Config, Hooks, Layout, Prompt, and Util). For example, to use
|
||||
the Mosaic layout, one would import:
|
||||
------------------------------------------------------------------------
|
||||
|
||||
XMonad.Layout.Mosaic
|
||||
Changelogs
|
||||
|
||||
For a list of changes since the 0.8.x releases, see:
|
||||
|
||||
http://www.haskell.org/haskellwiki/Xmonad/Notable_changes_since_0.8
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Updates to XMonadContrib-0.9 that may Require Changes to ~/.xmonad/xmonad.hs
|
||||
|
||||
Please see the Changelogs and xmonad-contrib haddock documentation
|
||||
links for further details regarding the following changes.
|
||||
|
||||
* XMonad.Hooks.EwmhDesktops no longer uses layoutHook, the
|
||||
ewmhDesktopsLayout modifier has been removed from xmonad-contrib. It
|
||||
uses logHook, handleEventHook, and startupHook instead and provides
|
||||
a convenient function 'ewmh' to add EWMH support to a defaultConfig.
|
||||
|
||||
* Most DynamicLog users can continue with configs unchanged, but users
|
||||
of the quickbar functions 'xmobar' or 'dzen' will need to change
|
||||
xmonad.hs: their types have changed to allow easier composition with
|
||||
other XConfig modifiers. The 'dynamicLogDzen' and 'dynamicLogXmobar'
|
||||
functions have been removed.
|
||||
|
||||
* WindowGo or safeSpawn users may need to change command lines due to
|
||||
safeSpawn changes.
|
||||
|
||||
* People explicitly referencing the "SP" scratchpad workspace should
|
||||
change it to "NSP" which is also used by the new Util.NamedScratchpad.
|
||||
|
||||
* (Optional) People who explicitly use swapMaster in key or mouse
|
||||
bindings should change it to shiftMaster. It's the current default
|
||||
used where swapMaster had been used previously. It works better than
|
||||
swapMaster when using floating and tiled windows together on the
|
||||
same workspace.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Getting or updating XMonadContrib
|
||||
|
||||
latest release: http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmonad-contrib
|
||||
|
||||
darcs version: darcs get http://code.haskell.org/XMonadContrib
|
||||
|
||||
(To use darcs xmonad-contrib you must also use the darcs version
|
||||
of xmonad.)
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Contributing
|
||||
|
||||
Haskell code contributed to this repo should live under the
|
||||
appropriate subdivision of the 'XMonad.' namespace (currently
|
||||
includes Actions, Config, Hooks, Layout, Prompt, and Util). For
|
||||
example, to use the Grid layout, one would import:
|
||||
|
||||
XMonad.Layout.Grid
|
||||
|
||||
For further details, see the documentation for the
|
||||
XMonad.Doc.Developing module and http://xmonad.org website.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
@@ -23,6 +80,3 @@ Code submitted to the contrib repo is licensed under the same license as
|
||||
xmonad itself, with copyright held by the authors.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Documentation for the extensions and configuration system is available
|
||||
in Haddock form in the XMonad.Doc module and submodules.
|
||||
|
@@ -41,7 +41,7 @@ import Data.Maybe
|
||||
--
|
||||
-- Then add a keybinding to the runCommand action:
|
||||
--
|
||||
-- > , ((modMask x .|. controlMask, xK_y), commands >>= runCommand)
|
||||
-- > , ((modm .|. controlMask, xK_y), commands >>= runCommand)
|
||||
--
|
||||
-- and define the list of commands you want to use:
|
||||
--
|
||||
@@ -82,23 +82,22 @@ defaultCommands = do
|
||||
wscmds <- workspaceCommands
|
||||
return $ wscmds ++ screenCommands ++ otherCommands
|
||||
where
|
||||
sr = broadcastMessage ReleaseResources
|
||||
otherCommands =
|
||||
[ ("shrink" , sendMessage Shrink )
|
||||
, ("expand" , sendMessage Expand )
|
||||
, ("next-layout" , sendMessage NextLayout )
|
||||
, ("default-layout" , asks (layoutHook . config) >>= setLayout )
|
||||
, ("restart-wm" , sr >> restart "xmonad" True )
|
||||
, ("restart-wm-no-resume", sr >> restart "xmonad" False )
|
||||
, ("restart-wm" , restart "xmonad" True )
|
||||
, ("restart-wm-no-resume", restart "xmonad" False )
|
||||
, ("xterm" , spawn =<< asks (terminal . config) )
|
||||
, ("run" , spawn "exe=`dmenu_path | dmenu -b` && exec $exe" )
|
||||
, ("kill" , kill )
|
||||
, ("refresh" , refresh )
|
||||
, ("focus-up" , windows $ focusUp )
|
||||
, ("focus-down" , windows $ focusDown )
|
||||
, ("swap-up" , windows $ swapUp )
|
||||
, ("swap-down" , windows $ swapDown )
|
||||
, ("swap-master" , windows $ swapMaster )
|
||||
, ("focus-up" , windows focusUp )
|
||||
, ("focus-down" , windows focusDown )
|
||||
, ("swap-up" , windows swapUp )
|
||||
, ("swap-down" , windows swapDown )
|
||||
, ("swap-master" , windows swapMaster )
|
||||
, ("sink" , withFocused $ windows . sink )
|
||||
, ("quit-wm" , io $ exitWith ExitSuccess )
|
||||
]
|
||||
|
@@ -5,7 +5,7 @@
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : <dougal@dougalstanton.net>
|
||||
-- Stability : unstable
|
||||
-- Stability : stable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Lets you constrain the aspect ratio of a floating
|
||||
@@ -31,8 +31,8 @@ import XMonad
|
||||
--
|
||||
-- Then add something like the following to your mouse bindings:
|
||||
--
|
||||
-- > , ((modMask x, button3), (\w -> focus w >> Sqr.mouseResizeWindow w False))
|
||||
-- > , ((modMask x .|. shiftMask, button3), (\w -> focus w >> Sqr.mouseResizeWindow w True ))
|
||||
-- > , ((modm, button3), (\w -> focus w >> Sqr.mouseResizeWindow w False))
|
||||
-- > , ((modm .|. shiftMask, button3), (\w -> focus w >> Sqr.mouseResizeWindow w True ))
|
||||
--
|
||||
-- The line without the shiftMask replaces the standard mouse resize
|
||||
-- function call, so it's not completely necessary but seems neater
|
||||
|
@@ -2,14 +2,14 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.CopyWindow
|
||||
-- Copyright : (c) David Roundy <droundy@darcs.net>, Ivan Veselov <veselov@gmail.com>
|
||||
-- Copyright : (c) David Roundy <droundy@darcs.net>, Ivan Veselov <veselov@gmail.com>, Lanny Ripple <lan3ny@gmail.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : ???
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Provides a binding to duplicate a window on multiple workspaces,
|
||||
-- Provides bindings to duplicate a window on multiple workspaces,
|
||||
-- providing dwm-like tagging functionality.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
@@ -17,13 +17,20 @@
|
||||
module XMonad.Actions.CopyWindow (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
copy, copyToAll, copyWindow, killAllOtherCopies, kill1
|
||||
copy, copyToAll, copyWindow, runOrCopy
|
||||
, killAllOtherCopies, kill1
|
||||
-- * Highlight workspaces containing copies in logHook
|
||||
-- $logHook
|
||||
, wsContainingCopies
|
||||
) where
|
||||
|
||||
import Prelude hiding (filter)
|
||||
import XMonad
|
||||
import Control.Arrow ((&&&))
|
||||
import Control.Monad
|
||||
import qualified Data.List as L
|
||||
import XMonad hiding (modify, workspaces)
|
||||
import XMonad.StackSet
|
||||
|
||||
import XMonad.Actions.WindowGo
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
-- $usage
|
||||
--
|
||||
@@ -36,7 +43,7 @@ import XMonad.StackSet
|
||||
-- > -- mod-[1..9] @@ Switch to workspace N
|
||||
-- > -- mod-shift-[1..9] @@ Move client to workspace N
|
||||
-- > -- mod-control-shift-[1..9] @@ Copy client to workspace N
|
||||
-- > [((m .|. modMask x, k), windows $ f i)
|
||||
-- > [((m .|. modm, k), windows $ f i)
|
||||
-- > | (i, k) <- zip (workspaces x) [xK_1 ..]
|
||||
-- > , (f, m) <- [(W.view, 0), (W.shift, shiftMask), (copy, shiftMask .|. controlMask)]]
|
||||
--
|
||||
@@ -48,7 +55,12 @@ import XMonad.StackSet
|
||||
-- You may also wish to redefine the binding to kill a window so it only
|
||||
-- removes it from the current workspace, if it's present elsewhere:
|
||||
--
|
||||
-- > , ((modMask x .|. shiftMask, xK_c ), kill1) -- @@ Close the focused window
|
||||
-- > , ((modm .|. shiftMask, xK_c ), kill1) -- @@ Close the focused window
|
||||
--
|
||||
-- Instead of copying a window from one workspace to another maybe you don't
|
||||
-- want to have to remember where you placed it. For that consider:
|
||||
--
|
||||
-- > , ((modm, xK_b ), runOrCopy "firefox" (className =? "Firefox")) -- @@ run or copy firefox
|
||||
--
|
||||
-- Another possibility which this extension provides is 'making window
|
||||
-- always visible' (i.e. always on current workspace), similar to corresponding
|
||||
@@ -58,59 +70,95 @@ import XMonad.StackSet
|
||||
--
|
||||
-- Here is the example of keybindings which provide these actions:
|
||||
--
|
||||
-- > , ((modMask x, xK_v )", windows copyToAll) -- @@ Make focused window always visible
|
||||
-- > , ((modMask x .|. shiftMask, xK_v ), killAllOtherCopies) -- @@ Toggle window state back
|
||||
-- > , ((modm, xK_v ), windows copyToAll) -- @@ Make focused window always visible
|
||||
-- > , ((modm .|. shiftMask, xK_v ), killAllOtherCopies) -- @@ Toggle window state back
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
||||
-- | copy. Copy the focussed window to a new workspace.
|
||||
copy :: (Eq s, Eq i, Eq a) => i -> StackSet i l a s sd -> StackSet i l a s sd
|
||||
copy n s | Just w <- peek s = copyWindow w n s
|
||||
-- $logHook
|
||||
-- To distinguish workspaces containing copies of the focused window use
|
||||
-- something like:
|
||||
--
|
||||
-- > sampleLogHook h = do
|
||||
-- > copies <- wsContainingCopies
|
||||
-- > let check ws | ws `elem` copies = pad . xmobarColor "red" "black" $ ws
|
||||
-- > | otherwise = pad ws
|
||||
-- > dynamicLogWithPP myPP {ppHidden = check, ppOutput = hPutStrLn h}
|
||||
-- >
|
||||
-- > main = do
|
||||
-- > h <- spawnPipe "xmobar"
|
||||
-- > xmonad defaultConfig { logHook = sampleLogHook h }
|
||||
|
||||
-- | Copy the focused window to a workspace.
|
||||
copy :: (Eq s, Eq i, Eq a) => i -> W.StackSet i l a s sd -> W.StackSet i l a s sd
|
||||
copy n s | Just w <- W.peek s = copyWindow w n s
|
||||
| otherwise = s
|
||||
|
||||
-- | copyToAll. Copy the focused window to all of workspaces.
|
||||
copyToAll :: (Eq s, Eq i, Eq a) => StackSet i l a s sd -> StackSet i l a s sd
|
||||
copyToAll s = foldr copy s $ map tag (workspaces s)
|
||||
-- | Copy the focused window to all workspaces.
|
||||
copyToAll :: (Eq s, Eq i, Eq a) => W.StackSet i l a s sd -> W.StackSet i l a s sd
|
||||
copyToAll s = foldr copy s $ map W.tag (W.workspaces s)
|
||||
|
||||
-- | copyWindow. Copy a window to a new workspace
|
||||
copyWindow :: (Eq a, Eq i, Eq s) => a -> i -> StackSet i l a s sd -> StackSet i l a s sd
|
||||
-- | Copy an arbitrary window to a workspace.
|
||||
copyWindow :: (Eq a, Eq i, Eq s) => a -> i -> W.StackSet i l a s sd -> W.StackSet i l a s sd
|
||||
copyWindow w n = copy'
|
||||
where copy' s = if n `tagMember` s
|
||||
then view (currentTag s) $ insertUp' w $ view n s
|
||||
where copy' s = if n `W.tagMember` s
|
||||
then W.view (W.currentTag s) $ insertUp' w $ W.view n s
|
||||
else s
|
||||
insertUp' a s = modify (Just $ Stack a [] [])
|
||||
(\(Stack t l r) -> if a `elem` t:l++r
|
||||
then Just $ Stack t l r
|
||||
else Just $ Stack a (L.delete a l) (L.delete a (t:r))) s
|
||||
insertUp' a s = W.modify (Just $ W.Stack a [] [])
|
||||
(\(W.Stack t l r) -> if a `elem` t:l++r
|
||||
then Just $ W.Stack t l r
|
||||
else Just $ W.Stack a (L.delete a l) (L.delete a (t:r))) s
|
||||
|
||||
|
||||
-- | runOrCopy will run the provided shell command unless it can
|
||||
-- find a specified window in which case it will copy the window to
|
||||
-- the current workspace. Similar to (i.e., stolen from) "XMonad.Actions.WindowGo".
|
||||
runOrCopy :: String -> Query Bool -> X ()
|
||||
runOrCopy = copyMaybe . spawn
|
||||
|
||||
-- | Copy a window if it exists, run the first argument otherwise.
|
||||
copyMaybe :: X () -> Query Bool -> X ()
|
||||
copyMaybe f qry = ifWindow qry copyWin f
|
||||
where copyWin = ask >>= \w -> doF (\ws -> copyWindow w (W.currentTag ws) ws)
|
||||
|
||||
-- | Remove the focused window from this workspace. If it's present in no
|
||||
-- other workspace, then kill it instead. If we do kill it, we'll get a
|
||||
-- delete notify back from X.
|
||||
--
|
||||
-- There are two ways to delete a window. Either just kill it, or if it
|
||||
-- supports the delete protocol, send a delete event (e.g. firefox)
|
||||
--
|
||||
-- supports the delete protocol, send a delete event (e.g. firefox).
|
||||
kill1 :: X ()
|
||||
kill1 = do ss <- gets windowset
|
||||
whenJust (peek ss) $ \w -> if member w $ delete'' w ss
|
||||
whenJust (W.peek ss) $ \w -> if W.member w $ delete'' w ss
|
||||
then windows $ delete'' w
|
||||
else kill
|
||||
where delete'' w = modify Nothing (filter (/= w))
|
||||
where delete'' w = W.modify Nothing (W.filter (/= w))
|
||||
|
||||
-- | Kill all other copies of focused window (if they're present)
|
||||
-- 'All other' means here 'copies, which are not on current workspace'
|
||||
--
|
||||
-- Consider calling this function after copyToAll
|
||||
--
|
||||
-- | Kill all other copies of focused window (if they're present).
|
||||
-- 'All other' means here 'copies which are not on the current workspace'.
|
||||
killAllOtherCopies :: X ()
|
||||
killAllOtherCopies = do ss <- gets windowset
|
||||
whenJust (peek ss) $ \w -> windows $
|
||||
view (currentTag ss) .
|
||||
whenJust (W.peek ss) $ \w -> windows $
|
||||
W.view (W.currentTag ss) .
|
||||
delFromAllButCurrent w
|
||||
where
|
||||
delFromAllButCurrent w ss = foldr ($) ss $
|
||||
map (delWinFromWorkspace w . tag) $
|
||||
hidden ss ++ map workspace (visible ss)
|
||||
delWinFromWorkspace w wid ss = modify Nothing (filter (/= w)) $ view wid ss
|
||||
map (delWinFromWorkspace w . W.tag) $
|
||||
W.hidden ss ++ map W.workspace (W.visible ss)
|
||||
delWinFromWorkspace w wid = W.modify Nothing (W.filter (/= w)) . W.view wid
|
||||
|
||||
-- | A list of hidden workspaces containing a copy of the focused window.
|
||||
wsContainingCopies :: X [WorkspaceId]
|
||||
wsContainingCopies = do
|
||||
ws <- gets windowset
|
||||
return $ copiesOfOn (W.peek ws) (taggedWindows $ W.hidden ws)
|
||||
|
||||
-- | Get a list of tuples (tag, [Window]) for each workspace.
|
||||
taggedWindows :: [W.Workspace i l a] -> [(i, [a])]
|
||||
taggedWindows = map $ W.tag &&& W.integrate' . W.stack
|
||||
|
||||
-- | Get tags with copies of the focused window (if present.)
|
||||
copiesOfOn :: (Eq a) => Maybe a -> [(i, [a])] -> [i]
|
||||
copiesOfOn foc tw = maybe [] hasCopyOf foc
|
||||
where hasCopyOf f = map fst $ filter ((f `elem` ) . snd) tw
|
||||
|
@@ -30,7 +30,7 @@ import XMonad.StackSet
|
||||
--
|
||||
-- > import XMonad.Actions.CycleRecentWS
|
||||
-- >
|
||||
-- > , ((modMask x, xK_Tab), cycleRecentWS [xK_Alt_L] xK_Tab xK_grave)
|
||||
-- > , ((modm, xK_Tab), cycleRecentWS [xK_Alt_L] xK_Tab xK_grave)
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
@@ -25,12 +25,12 @@ import qualified XMonad.StackSet as S
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
--
|
||||
-- > import XMonad hiding ((|||))
|
||||
-- > import XMonad.Layout.LayoutCombinators ((|||))
|
||||
-- > import XMonad.Actions.CycleSelectedLayouts
|
||||
--
|
||||
-- > , ((modMask x, xK_t ), cycleThroughLayouts ["Tall", "Mirror Tall"])
|
||||
-- > , ((modm, xK_t ), cycleThroughLayouts ["Tall", "Mirror Tall"])
|
||||
--
|
||||
-- Make sure you are using NewSelect from XMonad.Layout.LayoutCombinators,
|
||||
-- rather than the Select defined in xmonad core.
|
||||
|
@@ -42,7 +42,11 @@ module XMonad.Actions.CycleWS (
|
||||
, prevWS
|
||||
, shiftToNext
|
||||
, shiftToPrev
|
||||
|
||||
-- * Toggling the previous workspace
|
||||
-- $toggling
|
||||
, toggleWS
|
||||
, toggleOrView
|
||||
|
||||
-- * Moving between screens (xinerama)
|
||||
|
||||
@@ -56,7 +60,7 @@ module XMonad.Actions.CycleWS (
|
||||
-- * Moving between workspaces, take two!
|
||||
-- $taketwo
|
||||
|
||||
, WSDirection(..)
|
||||
, Direction1D(..)
|
||||
, WSType(..)
|
||||
|
||||
, shiftTo
|
||||
@@ -65,14 +69,18 @@ module XMonad.Actions.CycleWS (
|
||||
-- * The mother-combinator
|
||||
|
||||
, findWorkspace
|
||||
, toggleOrDoSkip
|
||||
, skipTags
|
||||
|
||||
) where
|
||||
|
||||
import Control.Monad ( unless )
|
||||
import Data.List ( findIndex )
|
||||
import Data.Maybe ( isNothing, isJust )
|
||||
|
||||
import XMonad hiding (workspaces)
|
||||
import XMonad.StackSet hiding (filter)
|
||||
import XMonad.Util.Types
|
||||
import XMonad.Util.WorkspaceCompare
|
||||
|
||||
-- $usage
|
||||
@@ -82,27 +90,27 @@ import XMonad.Util.WorkspaceCompare
|
||||
-- >
|
||||
-- > -- a basic CycleWS setup
|
||||
-- >
|
||||
-- > , ((modMask x, xK_Down), nextWS)
|
||||
-- > , ((modMask x, xK_Up), prevWS)
|
||||
-- > , ((modMask x .|. shiftMask, xK_Down), shiftToNext)
|
||||
-- > , ((modMask x .|. shiftMask, xK_Up), shiftToPrev)
|
||||
-- > , ((modMask x, xK_Right), nextScreen)
|
||||
-- > , ((modMask x, xK_Left), prevScreen)
|
||||
-- > , ((modMask x .|. shiftMask, xK_Right), shiftNextScreen)
|
||||
-- > , ((modMask x .|. shiftMask, xK_Left), shiftPrevScreen)
|
||||
-- > , ((modMask x, xK_z), toggleWS)
|
||||
-- > , ((modm, xK_Down), nextWS)
|
||||
-- > , ((modm, xK_Up), prevWS)
|
||||
-- > , ((modm .|. shiftMask, xK_Down), shiftToNext)
|
||||
-- > , ((modm .|. shiftMask, xK_Up), shiftToPrev)
|
||||
-- > , ((modm, xK_Right), nextScreen)
|
||||
-- > , ((modm, xK_Left), prevScreen)
|
||||
-- > , ((modm .|. shiftMask, xK_Right), shiftNextScreen)
|
||||
-- > , ((modm .|. shiftMask, xK_Left), shiftPrevScreen)
|
||||
-- > , ((modm, xK_z), toggleWS)
|
||||
--
|
||||
-- If you want to follow the moved window, you can use both actions:
|
||||
--
|
||||
-- > , ((modMask x .|. shiftMask, xK_Down), shiftToNext >> nextWS)
|
||||
-- > , ((modMask x .|. shiftMask, xK_Up), shiftToPrev >> prevWS)
|
||||
-- > , ((modm .|. shiftMask, xK_Down), shiftToNext >> nextWS)
|
||||
-- > , ((modm .|. shiftMask, xK_Up), shiftToPrev >> prevWS)
|
||||
--
|
||||
-- You can also get fancier with 'moveTo', 'shiftTo', and 'findWorkspace'.
|
||||
-- For example:
|
||||
--
|
||||
-- > , ((modMask x , xK_f), moveTo Next EmptyWS) -- find a free workspace
|
||||
-- > , ((modMask x .|. controlMask, xK_Right), -- a crazy keybinding!
|
||||
-- > do t <- findWorkspace getXineramaWsCompare Next NonEmptyWS 2
|
||||
-- > , ((modm , xK_f), moveTo Next EmptyWS) -- find a free workspace
|
||||
-- > , ((modm .|. controlMask, xK_Right), -- a crazy keybinding!
|
||||
-- > do t <- findWorkspace getSortByXineramaRule Next NonEmptyWS 2
|
||||
-- > windows . view $ t )
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
@@ -135,9 +143,47 @@ shiftToNext = shiftBy 1
|
||||
shiftToPrev :: X ()
|
||||
shiftToPrev = shiftBy (-1)
|
||||
|
||||
-- $toggling
|
||||
|
||||
-- | Toggle to the workspace displayed previously.
|
||||
toggleWS :: X ()
|
||||
toggleWS = windows $ view =<< tag . head . hidden
|
||||
toggleWS = do
|
||||
hs <- gets (hidden . windowset)
|
||||
unless (null hs) (windows . view . tag $ head hs)
|
||||
|
||||
-- | 'XMonad.StackSet.greedyView' a workspace, or if already there, view
|
||||
-- the previously displayed workspace ala weechat. Change @greedyView@ to
|
||||
-- @toggleOrView@ in your workspace bindings as in the 'XMonad.StackSet.view'
|
||||
-- faq at <http://haskell.org/haskellwiki/Xmonad/Frequently_asked_questions>.
|
||||
-- For more flexibility see 'toggleOrDoSkip'.
|
||||
toggleOrView :: WorkspaceId -> X ()
|
||||
toggleOrView = toggleOrDoSkip [] greedyView
|
||||
|
||||
-- | Allows ignoring listed workspace tags (such as scratchpad's \"NSP\") while
|
||||
-- finding the previously displayed workspace, or choice of different actions,
|
||||
-- like view, shift, etc. For example:
|
||||
--
|
||||
-- > import qualified XMonad.StackSet as W
|
||||
-- > import XMonad.Actions.CycleWS
|
||||
-- >
|
||||
-- > -- toggleOrView for people who prefer view to greedyView
|
||||
-- > toggleOrView' = toggleOrDoSkip [] W.view
|
||||
-- >
|
||||
-- > -- toggleOrView ignoring scratchpad and named scratchpad workspace
|
||||
-- > toggleOrViewNoSP = toggleOrDoSkip ["NSP"] W.greedyView
|
||||
toggleOrDoSkip :: [WorkspaceId] -> (WorkspaceId -> WindowSet -> WindowSet)
|
||||
-> WorkspaceId -> X ()
|
||||
toggleOrDoSkip skips f toWS = do
|
||||
ws <- gets windowset
|
||||
let hs' = hidden ws `skipTags` skips
|
||||
if toWS == (tag . workspace $ current ws)
|
||||
then unless (null hs') (windows . f . tag $ head hs')
|
||||
else windows (f toWS)
|
||||
|
||||
-- | List difference ('\\') for workspaces and tags. Removes workspaces
|
||||
-- matching listed tags from the given workspace list.
|
||||
skipTags :: (Eq i) => [Workspace i l a] -> [i] -> [Workspace i l a]
|
||||
skipTags wss ids = filter ((`notElem` ids) . tag) wss
|
||||
|
||||
switchWorkspace :: Int -> X ()
|
||||
switchWorkspace d = wsBy d >>= windows . greedyView
|
||||
@@ -166,13 +212,10 @@ the letter 'p' in its name. =)
|
||||
|
||||
-}
|
||||
|
||||
-- | Direction to cycle through the sort order.
|
||||
data WSDirection = Next | Prev
|
||||
|
||||
-- | What type of workspaces should be included in the cycle?
|
||||
data WSType = EmptyWS -- ^ cycle through empty workspaces
|
||||
| NonEmptyWS -- ^ cycle through non-empty workspaces
|
||||
| HiddenWS -- ^ cycle through non-visible workspaces
|
||||
| HiddenWS -- ^ cycle through non-visible workspaces
|
||||
| HiddenNonEmptyWS -- ^ cycle through non-empty non-visible workspaces
|
||||
| AnyWS -- ^ cycle through all workspaces
|
||||
| WSIs (X (WindowSpace -> Bool))
|
||||
@@ -186,19 +229,19 @@ wsTypeToPred NonEmptyWS = return (isJust . stack)
|
||||
wsTypeToPred HiddenWS = do hs <- gets (map tag . hidden . windowset)
|
||||
return (\w -> tag w `elem` hs)
|
||||
wsTypeToPred HiddenNonEmptyWS = do ne <- wsTypeToPred NonEmptyWS
|
||||
hi <- wsTypeToPred HiddenWS
|
||||
hi <- wsTypeToPred HiddenWS
|
||||
return (\w -> hi w && ne w)
|
||||
wsTypeToPred AnyWS = return (const True)
|
||||
wsTypeToPred (WSIs p) = p
|
||||
|
||||
-- | View the next workspace in the given direction that satisfies
|
||||
-- the given condition.
|
||||
moveTo :: WSDirection -> WSType -> X ()
|
||||
moveTo :: Direction1D -> WSType -> X ()
|
||||
moveTo dir t = findWorkspace getSortByIndex dir t 1 >>= windows . greedyView
|
||||
|
||||
-- | Move the currently focused window to the next workspace in the
|
||||
-- given direction that satisfies the given condition.
|
||||
shiftTo :: WSDirection -> WSType -> X ()
|
||||
shiftTo :: Direction1D -> WSType -> X ()
|
||||
shiftTo dir t = findWorkspace getSortByIndex dir t 1 >>= windows . shift
|
||||
|
||||
-- | Given a function @s@ to sort workspaces, a direction @dir@, a
|
||||
@@ -214,7 +257,7 @@ shiftTo dir t = findWorkspace getSortByIndex dir t 1 >>= windows . shift
|
||||
-- that 'moveTo' and 'shiftTo' are implemented by applying @(>>=
|
||||
-- (windows . greedyView))@ and @(>>= (windows . shift))@, respectively,
|
||||
-- to the output of 'findWorkspace'.
|
||||
findWorkspace :: X WorkspaceSort -> WSDirection -> WSType -> Int -> X WorkspaceId
|
||||
findWorkspace :: X WorkspaceSort -> Direction1D -> WSType -> Int -> X WorkspaceId
|
||||
findWorkspace s dir t n = findWorkspaceGen s (wsTypeToPred t) (maybeNegate dir n)
|
||||
where
|
||||
maybeNegate Next d = d
|
||||
@@ -229,7 +272,7 @@ findWorkspaceGen sortX wsPredX d = do
|
||||
let cur = workspace (current ws)
|
||||
sorted = sort (workspaces ws)
|
||||
pivoted = let (a,b) = span ((/= (tag cur)) . tag) sorted in b ++ a
|
||||
ws' = filter wsPred $ pivoted
|
||||
ws' = filter wsPred pivoted
|
||||
mCurIx = findWsIndex cur ws'
|
||||
d' = if d > 0 then d - 1 else d
|
||||
next = if null ws'
|
||||
|
233
XMonad/Actions/CycleWindows.hs
Normal file
233
XMonad/Actions/CycleWindows.hs
Normal file
@@ -0,0 +1,233 @@
|
||||
--------------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.CycleWindows
|
||||
-- Copyright : (c) Wirt Wolff <wirtwolff@gmail.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Wirt Wolff <wirtwolff@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Provides bindings to cycle windows up or down on the current workspace
|
||||
-- stack while maintaining focus in place.
|
||||
--
|
||||
-- Bindings are available to:
|
||||
--
|
||||
-- * Cycle nearby or nth windows into the focused frame
|
||||
--
|
||||
-- * Cycle a window halfway around the stack
|
||||
--
|
||||
-- * Cycle windows through the focused position.
|
||||
--
|
||||
-- * Cycle unfocused windows.
|
||||
--
|
||||
-- These bindings are especially useful with layouts that hide some of
|
||||
-- the windows in the stack, such as Full, "XMonad.Layout.TwoPane" or
|
||||
-- when using "XMonad.Layout.LimitWindows" to only show three or four
|
||||
-- panes. See also "XMonad.Actions.RotSlaves" for related actions.
|
||||
-----------------------------------------------------------------------------
|
||||
module XMonad.Actions.CycleWindows (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
|
||||
-- * Cycling nearby or nth window into current frame
|
||||
-- $cycle
|
||||
cycleRecentWindows,
|
||||
cycleStacks',
|
||||
-- * Cycling half the stack to get rid of a boring window
|
||||
-- $opposite
|
||||
rotOpposite', rotOpposite,
|
||||
-- * Cycling windows through the current frame
|
||||
-- $focused
|
||||
rotFocused', rotFocusedUp, rotFocusedDown, shiftToFocus',
|
||||
-- * Cycling windows through other frames
|
||||
-- $unfocused
|
||||
rotUnfocused', rotUnfocusedUp, rotUnfocusedDown,
|
||||
-- * Updating the mouse pointer
|
||||
-- $pointer
|
||||
|
||||
-- * Generic list rotations
|
||||
-- $generic
|
||||
rotUp, rotDown
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad.Actions.RotSlaves
|
||||
|
||||
import Control.Arrow (second)
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@ file:
|
||||
--
|
||||
-- > import XMonad.Actions.CycleWindows
|
||||
-- > -- config
|
||||
-- > -- other key bindings with x here your config
|
||||
-- >
|
||||
-- > -- make sure mod matches keysym
|
||||
-- > , ((mod4Mask, xK_s), cycleRecentWindows [xK_Super_L] xK_s xK_w)
|
||||
-- > , ((modm, xK_z), rotOpposite)
|
||||
-- > , ((modm , xK_i), rotUnfocusedUp)
|
||||
-- > , ((modm , xK_u), rotUnfocusedDown)
|
||||
-- > , ((modm .|. controlMask, xK_i), rotFocusedUp)
|
||||
-- > , ((modm .|. controlMask, xK_u), rotFocusedDown)
|
||||
--
|
||||
-- Also, if you use focus follows mouse, you will want to read the section
|
||||
-- on updating the mouse pointer below. For detailed instructions on
|
||||
-- editing your key bindings, see "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
{- $pointer
|
||||
With FocusFollowsMouse == True, the focus is updated after binding
|
||||
actions, possibly focusing a window you didn't intend to focus. Most
|
||||
people using TwoPane probably already have a logHook causing the mouse
|
||||
to follow focus. (See "XMonad.Actions.UpdatePointer", or "XMonad.Actions.Warp")
|
||||
|
||||
If you want this built into the key binding instead, use the appropriate
|
||||
action from one of those modules to also have your bindings move the pointer
|
||||
to the point of your choice on the current window:
|
||||
|
||||
> import XMonad.Actions.UpdatePointer -- or Actions.Warp
|
||||
|
||||
and either
|
||||
|
||||
> -- modify the window rotation bindings
|
||||
> , ((modm .|. controlMask, xK_i ), rotFocusedUp
|
||||
> >> updatePointer (Relative 1 1))
|
||||
> , ((modm .|. controlMask, xK_u ), rotFocusedDown
|
||||
> >> updatePointer (Relative 1 1))
|
||||
>
|
||||
> -- or add to xmonad's logHook
|
||||
> , logHook = dynamicLogWithPP xmobarPP
|
||||
> >> updatePointer Nearest -- or your preference
|
||||
|
||||
-}
|
||||
|
||||
-- $cycle
|
||||
-- Cycle windows into focus from below or above the focused pane by pressing
|
||||
-- a key while one or more modifier keys is held down. The window order isn't
|
||||
-- changed until a modifier is released, leaving the previously focused window
|
||||
-- just below the new one, (or above if the window just above is chosen.) For
|
||||
-- best results use the same modifier + key combination as the one used to invoke
|
||||
-- the \"bring from below\" action. Also, once cycling, pressing a number key n
|
||||
-- will focus the nth window, with 0 being the one originally focused.
|
||||
cycleRecentWindows :: [KeySym] -- ^ A list of modifier keys used when invoking this action.
|
||||
-- As soon as one of them is released, the final switch is made.
|
||||
-> KeySym -- ^ Key used to shift windows from below the current choice into the current frame.
|
||||
-> KeySym -- ^ Key used to shift windows from above the current choice into the current frame.
|
||||
-- If it's the same as the first key, it is effectively ignored.
|
||||
-> X ()
|
||||
cycleRecentWindows = cycleStacks' stacks where
|
||||
stacks s = map (shiftToFocus' `flip` s) (wins s)
|
||||
wins (W.Stack t l r) = t : r ++ reverse l
|
||||
|
||||
|
||||
-- | Cycle through a /finite/ list of window stacks with repeated presses
|
||||
-- of a key while a modifier key is held down. For best results use the same
|
||||
-- mod key + key combination as the one used to invoke the \"bring from below\"
|
||||
-- action. You could use cycleStacks' with a different stack permutations
|
||||
-- function to, for example, cycle from one below to one above to two below,
|
||||
-- etc. instead of in order. You are responsible for having it generate a
|
||||
-- finite list, though, or xmonad may hang seeking its length.
|
||||
cycleStacks' :: (W.Stack Window -> [W.Stack Window]) -- ^ A function to a finite list of permutations of a given stack.
|
||||
-> [KeySym] -- ^ A list of modifier keys used to invoke 'cycleStacks''.
|
||||
-- As soon as any is released, we're no longer cycling on the [Stack Window]
|
||||
-> KeySym -- ^ Key used to select a \"next\" stack.
|
||||
-> KeySym -- ^ Key used to select a \"previous\" stack.
|
||||
-> X ()
|
||||
cycleStacks' filteredPerms mods keyNext keyPrev = do
|
||||
XConf {theRoot = root, display = d} <- ask
|
||||
stacks <- gets $ maybe [] filteredPerms . W.stack . W.workspace . W.current . windowset
|
||||
|
||||
let evt = allocaXEvent $
|
||||
\p -> do maskEvent d (keyPressMask .|. keyReleaseMask) p
|
||||
KeyEvent {ev_event_type = t, ev_keycode = c} <- getEvent p
|
||||
s <- keycodeToKeysym d c 0
|
||||
return (t, s)
|
||||
choose n (t, s)
|
||||
| t == keyPress && s == keyNext = io evt >>= choose (n+1)
|
||||
| t == keyPress && s == keyPrev = io evt >>= choose (n-1)
|
||||
| t == keyPress && s `elem` [xK_0..xK_9] = io evt >>= choose (numKeyToN s)
|
||||
| t == keyRelease && s `elem` mods = return ()
|
||||
| otherwise = doStack n >> io evt >>= choose n
|
||||
doStack n = windows . W.modify' . const $ stacks `cycref` n
|
||||
|
||||
io $ grabKeyboard d root False grabModeAsync grabModeAsync currentTime
|
||||
io evt >>= choose 1
|
||||
io $ ungrabKeyboard d currentTime
|
||||
where cycref l i = l !! (i `mod` length l) -- modify' ensures l is never [], but must also be finite
|
||||
numKeyToN = subtract 48 . read . show
|
||||
|
||||
-- | Given a stack element and a stack, shift or insert the element (window)
|
||||
-- at the currently focused position.
|
||||
shiftToFocus' :: (Eq a, Show a, Read a) => a -> W.Stack a -> W.Stack a
|
||||
shiftToFocus' w s@(W.Stack _ ls _) = W.Stack w (reverse revls') rs'
|
||||
where (revls', rs') = splitAt (length ls) . filter (/= w) $ W.integrate s
|
||||
|
||||
|
||||
-- $opposite
|
||||
-- Shifts the focused window as far as possible from the current focus,
|
||||
-- i.e. halfway around the stack. Windows above the focus up to the \"opposite\"
|
||||
-- position remain in place, while those above the insertion shift toward
|
||||
-- the current focus. This is useful for people who use lots of windows in Full,
|
||||
-- TwoPane, etc., to get rid of boring windows while cycling and swapping
|
||||
-- near the focus.
|
||||
rotOpposite :: X()
|
||||
rotOpposite = windows $ W.modify' rotOpposite'
|
||||
|
||||
-- | The opposite rotation on a Stack.
|
||||
rotOpposite' :: W.Stack a -> W.Stack a
|
||||
rotOpposite' (W.Stack t l r) = W.Stack t' l' r'
|
||||
where rrvl = r ++ reverse l
|
||||
part = (length rrvl + 1) `div` 2
|
||||
(l',t':r') = second reverse . splitAt (length l) $
|
||||
reverse (take part rrvl ++ t : drop part rrvl)
|
||||
|
||||
|
||||
-- $focused
|
||||
-- Most people will want the @rotAllUp@ or @rotAllDown@ actions from
|
||||
-- "XMonad.Actions.RotSlaves" to cycle all windows in the stack.
|
||||
--
|
||||
-- The following actions keep the \"next\" window stable, which is
|
||||
-- mostly useful in two window layouts, or when you have a log viewer or
|
||||
-- buffer window you want to keep next to the cycled window.
|
||||
|
||||
-- | Rotate windows through the focused frame, excluding the \"next\" window.
|
||||
-- With, e.g. TwoPane, this allows cycling windows through either the
|
||||
-- master or slave pane, without changing the other frame. When the master
|
||||
-- is focused, the window below is skipped, when a non-master window is
|
||||
-- focused, the master is skipped.
|
||||
rotFocusedUp :: X ()
|
||||
rotFocusedUp = windows . W.modify' $ rotFocused' rotUp
|
||||
rotFocusedDown :: X ()
|
||||
rotFocusedDown = windows . W.modify' $ rotFocused' rotDown
|
||||
|
||||
-- | The focused rotation on a stack.
|
||||
rotFocused' :: ([a] -> [a]) -> W.Stack a -> W.Stack a
|
||||
rotFocused' _ s@(W.Stack _ [] []) = s
|
||||
rotFocused' f (W.Stack t [] (r:rs)) = W.Stack t' [] (r:rs') -- Master has focus
|
||||
where (t':rs') = f (t:rs)
|
||||
rotFocused' f s@(W.Stack _ _ _) = rotSlaves' f s -- otherwise
|
||||
|
||||
|
||||
-- $unfocused
|
||||
-- Rotate windows through the unfocused frames. This is similar to
|
||||
-- @rotSlaves@, from "XMonad.Actions.RotSlaves", but excludes the current
|
||||
-- frame rather than master.
|
||||
rotUnfocusedUp :: X ()
|
||||
rotUnfocusedUp = windows . W.modify' $ rotUnfocused' rotUp
|
||||
rotUnfocusedDown :: X ()
|
||||
rotUnfocusedDown = windows . W.modify' $ rotUnfocused' rotDown
|
||||
|
||||
-- | The unfocused rotation on a stack.
|
||||
rotUnfocused' :: ([a] -> [a]) -> W.Stack a -> W.Stack a
|
||||
rotUnfocused' _ s@(W.Stack _ [] []) = s
|
||||
rotUnfocused' f s@(W.Stack _ [] _ ) = rotSlaves' f s -- Master has focus
|
||||
rotUnfocused' f (W.Stack t ls rs) = W.Stack t (reverse revls') rs' -- otherwise
|
||||
where (master:revls) = reverse ls
|
||||
(revls',rs') = splitAt (length ls) (f $ master:revls ++ rs)
|
||||
|
||||
-- $generic
|
||||
-- Generic list rotations
|
||||
rotUp :: [a] -> [a]
|
||||
rotUp l = tail l ++ [head l]
|
||||
rotDown :: [a] -> [a]
|
||||
rotDown l = last l : init l
|
@@ -5,12 +5,13 @@
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Spencer Janssen <spencerjanssen@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Stability : stable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- This module provides a method to cease management of a window
|
||||
-- without unmapping it. This is especially useful for applications
|
||||
-- like kicker and gnome-panel.
|
||||
-- like kicker and gnome-panel. See also "XMonad.Hooks.ManageDocks" for
|
||||
-- more a more automated solution.
|
||||
--
|
||||
-- To make a panel display correctly with xmonad:
|
||||
--
|
||||
@@ -43,7 +44,7 @@ import XMonad
|
||||
--
|
||||
-- And add a keybinding, such as:
|
||||
--
|
||||
-- > , ((modMask x, xK_d ), withFocused demanage)
|
||||
-- > , ((modm, xK_d ), withFocused demanage)
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
@@ -5,7 +5,7 @@
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : arcatan@kapsi.fi
|
||||
-- Stability : unstable
|
||||
-- Stability : stable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Dwm-like swap function for xmonad.
|
||||
@@ -33,7 +33,7 @@ import XMonad.StackSet
|
||||
--
|
||||
-- then add a keybinding or substitute 'dwmpromote' in place of promote:
|
||||
--
|
||||
-- > , ((modMask x, xK_Return), dwmpromote)
|
||||
-- > , ((modm, xK_Return), dwmpromote)
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
@@ -36,18 +36,18 @@ import XMonad.Util.WorkspaceCompare ( getSortByIndex )
|
||||
--
|
||||
-- Then add keybindings like the following:
|
||||
--
|
||||
-- > , ((modMask x .|. shiftMask, xK_BackSpace), removeWorkspace)
|
||||
-- > , ((modMask x .|. shiftMask, xK_v ), selectWorkspace defaultXPConfig)
|
||||
-- > , ((modMask x, xK_m ), withWorkspace defaultXPConfig (windows . W.shift))
|
||||
-- > , ((modMask x .|. shiftMask, xK_m ), withWorkspace defaultXPConfig (windows . copy))
|
||||
-- > , ((modMask x .|. shiftMask, xK_r ), renameWorkspace defaultXPConfig)
|
||||
-- > , ((modm .|. shiftMask, xK_BackSpace), removeWorkspace)
|
||||
-- > , ((modm .|. shiftMask, xK_v ), selectWorkspace defaultXPConfig)
|
||||
-- > , ((modm, xK_m ), withWorkspace defaultXPConfig (windows . W.shift))
|
||||
-- > , ((modm .|. shiftMask, xK_m ), withWorkspace defaultXPConfig (windows . copy))
|
||||
-- > , ((modm .|. shiftMask, xK_r ), renameWorkspace defaultXPConfig)
|
||||
--
|
||||
-- > -- mod-[1..9] %! Switch to workspace N
|
||||
-- > -- mod-shift-[1..9] %! Move client to workspace N
|
||||
-- > ++
|
||||
-- > zip (zip (repeat (modMask x)) [xK_1..xK_9]) (map (withNthWorkspace W.greedyView) [0..])
|
||||
-- > zip (zip (repeat (modm)) [xK_1..xK_9]) (map (withNthWorkspace W.greedyView) [0..])
|
||||
-- > ++
|
||||
-- > zip (zip (repeat (modMask x .|. shiftMask)) [xK_1..xK_9]) (map (withNthWorkspace W.shift) [0..])
|
||||
-- > zip (zip (repeat (modm .|. shiftMask)) [xK_1..xK_9]) (map (withNthWorkspace W.shift) [0..])
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
@@ -5,7 +5,7 @@
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : arcatan@kapsi.fi
|
||||
-- Stability : unstable
|
||||
-- Stability : stable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Find an empty workspace.
|
||||
@@ -32,8 +32,8 @@ import XMonad.StackSet
|
||||
--
|
||||
-- and add the desired keybindings, for example:
|
||||
--
|
||||
-- > , ((modMask x, xK_m ), viewEmptyWorkspace)
|
||||
-- > , ((modMask x .|. shiftMask, xK_m ), tagToEmptyWorkspace)
|
||||
-- > , ((modm, xK_m ), viewEmptyWorkspace)
|
||||
-- > , ((modm .|. shiftMask, xK_m ), tagToEmptyWorkspace)
|
||||
--
|
||||
-- Now you can jump to an empty workspace with @mod-m@. @Mod-shift-m@
|
||||
-- will tag the current window to an empty workspace and view it.
|
||||
|
@@ -7,7 +7,7 @@
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : <mgsloan@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Stability : stable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Move and resize floating windows without warping the mouse.
|
||||
@@ -31,7 +31,7 @@ import XMonad
|
||||
--
|
||||
-- Now set up the desired mouse binding, for example:
|
||||
--
|
||||
-- > , ((modMask x, button1), (\w -> focus w >> Flex.mouseWindow Flex.linear w))
|
||||
-- > , ((modm, button1), (\w -> focus w >> Flex.mouseWindow Flex.linear w))
|
||||
--
|
||||
-- * Flex.'linear' indicates that positions between the edges and the
|
||||
-- middle indicate a combination scale\/position.
|
||||
|
@@ -15,7 +15,8 @@
|
||||
module XMonad.Actions.FlexibleResize (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
XMonad.Actions.FlexibleResize.mouseResizeWindow
|
||||
XMonad.Actions.FlexibleResize.mouseResizeWindow,
|
||||
XMonad.Actions.FlexibleResize.mouseResizeEdgeWindow
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
@@ -28,40 +29,53 @@ import Foreign.C.Types
|
||||
--
|
||||
-- Then add an appropriate mouse binding:
|
||||
--
|
||||
-- > , ((modMask x, button3), (\w -> focus w >> Flex.mouseResizeWindow w))
|
||||
-- > , ((modm, button3), (\w -> focus w >> Flex.mouseResizeWindow w))
|
||||
--
|
||||
-- For detailed instructions on editing your mouse bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_mouse_bindings".
|
||||
|
||||
-- | Resize a floating window from whichever corner the mouse is
|
||||
-- closest to.
|
||||
mouseResizeWindow :: Window -> X ()
|
||||
mouseResizeWindow w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
mouseResizeWindow
|
||||
:: Window -- ^ The window to resize.
|
||||
-> X ()
|
||||
mouseResizeWindow = mouseResizeEdgeWindow 0
|
||||
|
||||
|
||||
-- | Resize a floating window from whichever corner or edge the mouse is
|
||||
-- closest to.
|
||||
mouseResizeEdgeWindow
|
||||
:: Rational -- ^ The size of the area where only one edge is resized.
|
||||
-> Window -- ^ The window to resize.
|
||||
-> X ()
|
||||
mouseResizeEdgeWindow edge w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
io $ raiseWindow d w
|
||||
wa <- io $ getWindowAttributes d w
|
||||
sh <- io $ getWMNormalHints d w
|
||||
(_, _, _, _, _, ix, iy, _) <- io $ queryPointer d w
|
||||
let
|
||||
[pos_x, pos_y, width, height] = map (fromIntegral . ($ wa)) [wa_x, wa_y, wa_width, wa_height]
|
||||
west = firstHalf ix width
|
||||
north = firstHalf iy height
|
||||
[pos_x, pos_y, width, height] = map (fi . ($ wa)) [wa_x, wa_y, wa_width, wa_height]
|
||||
west = findPos ix width
|
||||
north = findPos iy height
|
||||
(cx, fx, gx) = mkSel west width pos_x
|
||||
(cy, fy, gy) = mkSel north height pos_y
|
||||
io $ warpPointer d none w 0 0 0 0 cx cy
|
||||
mouseDrag (\ex ey -> do
|
||||
wa' <- io $ getWindowAttributes d w
|
||||
let [px, py] = map (fromIntegral . ($ wa')) [wa_x, wa_y]
|
||||
io $ moveResizeWindow d w (fx px (fromIntegral ex))
|
||||
(fy py (fromIntegral ey))
|
||||
`uncurry` applySizeHintsContents sh (gx $ fromIntegral ex, gy $ fromIntegral ey))
|
||||
mouseDrag (\ex ey -> do let (nw,nh) = applySizeHintsContents sh (gx ex, gy ey)
|
||||
io $ moveResizeWindow d w (fx nw) (fy nh) nw nh)
|
||||
(float w)
|
||||
where
|
||||
firstHalf :: CInt -> Position -> Bool
|
||||
firstHalf a b = fromIntegral a * 2 <= b
|
||||
cfst = curry fst
|
||||
csnd = curry snd
|
||||
mkSel :: Bool -> Position -> Position -> (Position, a -> a -> a, CInt -> Position)
|
||||
mkSel b k p =
|
||||
if b
|
||||
then (0, csnd, ((k + p) -) . fromIntegral)
|
||||
else (k, cfst, subtract p . fromIntegral)
|
||||
findPos :: CInt -> Position -> Maybe Bool
|
||||
findPos m s = if p < 0.5 - edge/2
|
||||
then Just True
|
||||
else if p < 0.5 + edge/2
|
||||
then Nothing
|
||||
else Just False
|
||||
where p = fi m / fi s
|
||||
mkSel :: Maybe Bool -> Position -> Position -> (Position, Dimension -> Position, Position -> Dimension)
|
||||
mkSel b k p = case b of
|
||||
Just True -> (0, (fi k + fi p -).fi, (fi k + fi p -).fi)
|
||||
Nothing -> (k `div` 2, const p, const $ fi k)
|
||||
Just False -> (k, const p, subtract (fi p) . fi)
|
||||
|
||||
fi :: (Num b, Integral a) => a -> b
|
||||
fi = fromIntegral
|
||||
|
@@ -5,7 +5,7 @@
|
||||
-- License : BSD
|
||||
--
|
||||
-- Maintainer : Karsten Schoelzel <kuser@gmx.de>
|
||||
-- Stability : unstable
|
||||
-- Stability : stable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Move and resize floating windows.
|
||||
@@ -28,11 +28,11 @@ import XMonad
|
||||
--
|
||||
-- Then add appropriate key bindings, for example:
|
||||
--
|
||||
-- > , ((modMask x, xK_d ), withFocused (keysResizeWindow (-10,-10) (1,1)))
|
||||
-- > , ((modMask x, xK_s ), withFocused (keysResizeWindow (10,10) (1,1)))
|
||||
-- > , ((modMask x .|. shiftMask, xK_d ), withFocused (keysAbsResizeWindow (-10,-10) (1024,752)))
|
||||
-- > , ((modMask x .|. shiftMask, xK_s ), withFocused (keysAbsResizeWindow (10,10) (1024,752)))
|
||||
-- > , ((modMask x, xK_a ), withFocused (keysMoveWindowTo (512,384) (1%2,1%2)))
|
||||
-- > , ((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".
|
||||
|
328
XMonad/Actions/FloatSnap.hs
Normal file
328
XMonad/Actions/FloatSnap.hs
Normal file
@@ -0,0 +1,328 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.FloatSnap
|
||||
-- Copyright : (c) 2009 Anders Engstrom <ankaan@gmail.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Anders Engstrom <ankaan@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Move and resize floating windows using other windows and the edge of the
|
||||
-- screen as guidelines.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.FloatSnap (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
Direction2D(..),
|
||||
snapMove,
|
||||
snapGrow,
|
||||
snapShrink,
|
||||
snapMagicMove,
|
||||
snapMagicResize,
|
||||
snapMagicMouseResize) where
|
||||
|
||||
import XMonad
|
||||
import Control.Applicative((<$>))
|
||||
import Data.List (sort)
|
||||
import Data.Maybe (listToMaybe,fromJust,isNothing)
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import XMonad.Hooks.ManageDocks (calcGap)
|
||||
import XMonad.Util.Types (Direction2D(..))
|
||||
|
||||
import qualified Data.Set as S
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Actions.FloatSnap
|
||||
--
|
||||
-- Then add appropriate key bindings, for example:
|
||||
--
|
||||
-- > , ((modm, xK_Left), withFocused $ snapMove L Nothing)
|
||||
-- > , ((modm, xK_Right), withFocused $ snapMove R Nothing)
|
||||
-- > , ((modm, xK_Up), withFocused $ snapMove U Nothing)
|
||||
-- > , ((modm, xK_Down), withFocused $ snapMove D Nothing)
|
||||
-- > , ((modm .|. shiftMask, xK_Left), withFocused $ snapShrink R Nothing)
|
||||
-- > , ((modm .|. shiftMask, xK_Right), withFocused $ snapGrow R Nothing)
|
||||
-- > , ((modm .|. shiftMask, xK_Up), withFocused $ snapShrink D Nothing)
|
||||
-- > , ((modm .|. shiftMask, xK_Down), withFocused $ snapGrow D Nothing)
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
--
|
||||
-- And possibly add an appropriate mouse binding, for example:
|
||||
--
|
||||
-- > , ((modm, button1), (\w -> focus w >> mouseMoveWindow w >> snapMagicMove (Just 50) (Just 50) w))
|
||||
-- > , ((modm .|. shiftMask, button1), (\w -> focus w >> mouseMoveWindow w >> snapMagicResize [L,R,U,D] (Just 50) (Just 50) w))
|
||||
-- > , ((modm, button3), (\w -> focus w >> mouseResizeWindow w >> snapMagicResize [R,D] (Just 50) (Just 50) w))
|
||||
--
|
||||
-- For detailed instructions on editing your mouse bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_mouse_bindings".
|
||||
--
|
||||
-- Using these mouse bindings, it will not snap while moving, but allow you to click the window once after it has been moved or resized to snap it into place.
|
||||
-- Note that the order in which the commands are applied in the mouse bindings are important.
|
||||
--
|
||||
-- Interesting values for the distance to look for window in the orthogonal axis are Nothing (to snap against every window), Just 0 (to only snap
|
||||
-- against windows that we should collide with geometrically while moving) and Just 1 (to also snap against windows we brush against).
|
||||
--
|
||||
-- For 'snapMagicMove', 'snapMagicResize' and 'snapMagicMouseResize', try instead setting it to the same as the maximum snapping distance.
|
||||
--
|
||||
-- When a value is specified it can be geometrically conceived as adding a border with the specified width around the window and then checking which
|
||||
-- windows it should collide with.
|
||||
|
||||
-- | Resize the window by each edge independently to snap against the closest part of other windows or the edge of the screen. Use the location of the
|
||||
-- mouse over the window to decide which edges to snap. In corners, the two adjoining edges will be snapped, along the middle of an edge only that edge
|
||||
-- will be snapped. In the center of the window all edges will snap. Intended to be used together with "XMonad.Actions.FlexibleResize" or
|
||||
-- "XMonad.Actions.FlexibleManipulate".
|
||||
snapMagicMouseResize
|
||||
:: Rational -- ^ How big the middle snap area of each axis should be.
|
||||
-> Maybe Int -- ^ The distance in the orthogonal axis to look for windows to snap against. Use Nothing to snap against every window.
|
||||
-> Maybe Int -- ^ The maximum distance to snap. Use Nothing to not impose any boundary.
|
||||
-> Window -- ^ The window to move and resize.
|
||||
-> X ()
|
||||
snapMagicMouseResize middle collidedist snapdist w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
wa <- io $ getWindowAttributes d w
|
||||
(_, _, _, px, py, _, _, _) <- io $ queryPointer d w
|
||||
let x = (fromIntegral px - wx wa)/(ww wa)
|
||||
y = (fromIntegral py - wy wa)/(wh wa)
|
||||
ml = if x <= (0.5 - middle/2) then [L] else []
|
||||
mr = if x > (0.5 + middle/2) then [R] else []
|
||||
mu = if y <= (0.5 - middle/2) then [U] else []
|
||||
md = if y > (0.5 + middle/2) then [D] else []
|
||||
mdir = ml++mr++mu++md
|
||||
dir = if mdir == []
|
||||
then [L,R,U,D]
|
||||
else mdir
|
||||
snapMagicResize dir collidedist snapdist w
|
||||
where
|
||||
wx = fromIntegral.wa_x
|
||||
wy = fromIntegral.wa_y
|
||||
ww = fromIntegral.wa_width
|
||||
wh = fromIntegral.wa_height
|
||||
|
||||
-- | Resize the window by each edge independently to snap against the closest part of other windows or the edge of the screen.
|
||||
snapMagicResize
|
||||
:: [Direction2D] -- ^ The edges to snap.
|
||||
-> Maybe Int -- ^ The distance in the orthogonal axis to look for windows to snap against. Use Nothing to snap against every window.
|
||||
-> Maybe Int -- ^ The maximum distance to snap. Use Nothing to not impose any boundary.
|
||||
-> Window -- ^ The window to move and resize.
|
||||
-> X ()
|
||||
snapMagicResize dir collidedist snapdist w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
io $ raiseWindow d w
|
||||
wa <- io $ getWindowAttributes d w
|
||||
|
||||
(xbegin,xend) <- handleAxis True d wa
|
||||
(ybegin,yend) <- handleAxis False d wa
|
||||
|
||||
let xbegin' = if L `elem` dir then xbegin else (wx wa)
|
||||
xend' = if R `elem` dir then xend else (wx wa + ww wa)
|
||||
ybegin' = if U `elem` dir then ybegin else (wy wa)
|
||||
yend' = if D `elem` dir then yend else (wy wa + wh wa)
|
||||
|
||||
io $ moveWindow d w (fromIntegral $ xbegin') (fromIntegral $ ybegin')
|
||||
io $ resizeWindow d w (fromIntegral $ xend' - xbegin') (fromIntegral $ yend' - ybegin')
|
||||
float w
|
||||
where
|
||||
wx = fromIntegral.wa_x
|
||||
wy = fromIntegral.wa_y
|
||||
ww = fromIntegral.wa_width
|
||||
wh = fromIntegral.wa_height
|
||||
|
||||
handleAxis horiz d wa = do
|
||||
((mbl,mbr,bs),(mfl,mfr,fs)) <- getSnap horiz collidedist d w
|
||||
let begin = if bs
|
||||
then wpos wa
|
||||
else case (mbl,mbr) of
|
||||
(Just bl,Just br) -> if wpos wa - bl < br - wpos wa then bl else br
|
||||
(Just bl,Nothing) -> bl
|
||||
(Nothing,Just br) -> br
|
||||
(Nothing,Nothing) -> wpos wa
|
||||
end = if fs
|
||||
then wpos wa + wdim wa
|
||||
else case (if mfl==(Just begin) then Nothing else mfl,mfr) of
|
||||
(Just fl,Just fr) -> if wpos wa + wdim wa - fl < fr - wpos wa - wdim wa then fl else fr
|
||||
(Just fl,Nothing) -> fl
|
||||
(Nothing,Just fr) -> fr
|
||||
(Nothing,Nothing) -> wpos wa + wdim wa
|
||||
begin' = if isNothing snapdist || abs (begin - wpos wa) <= fromJust snapdist then begin else (wpos wa)
|
||||
end' = if isNothing snapdist || abs (end - wpos wa - wdim wa) <= fromJust snapdist then end else (wpos wa + wdim wa)
|
||||
return (begin',end')
|
||||
where
|
||||
(wpos, wdim, _, _) = constructors horiz
|
||||
|
||||
|
||||
-- | Move a window by both axises in any direction to snap against the closest part of other windows or the edge of the screen.
|
||||
snapMagicMove
|
||||
:: Maybe Int -- ^ The distance in the orthogonal axis to look for windows to snap against. Use Nothing to snap against every window.
|
||||
-> Maybe Int -- ^ The maximum distance to snap. Use Nothing to not impose any boundary.
|
||||
-> Window -- ^ The window to move.
|
||||
-> X ()
|
||||
snapMagicMove collidedist snapdist w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
io $ raiseWindow d w
|
||||
wa <- io $ getWindowAttributes d w
|
||||
|
||||
nx <- handleAxis True d wa
|
||||
ny <- handleAxis False d wa
|
||||
|
||||
io $ moveWindow d w (fromIntegral nx) (fromIntegral ny)
|
||||
float w
|
||||
where
|
||||
handleAxis horiz d wa = do
|
||||
((mbl,mbr,bs),(mfl,mfr,fs)) <- getSnap horiz collidedist d w
|
||||
return $ if bs || fs
|
||||
then wpos wa
|
||||
else let b = case (mbl,mbr) of
|
||||
(Just bl,Just br) -> if wpos wa - bl < br - wpos wa then bl else br
|
||||
(Just bl,Nothing) -> bl
|
||||
(Nothing,Just br) -> br
|
||||
(Nothing,Nothing) -> wpos wa
|
||||
f = case (mfl,mfr) of
|
||||
(Just fl,Just fr) -> if wpos wa + wdim wa - fl < fr - wpos wa - wdim wa then fl else fr
|
||||
(Just fl,Nothing) -> fl
|
||||
(Nothing,Just fr) -> fr
|
||||
(Nothing,Nothing) -> wpos wa
|
||||
newpos = if abs (b - wpos wa) <= abs (f - wpos wa - wdim wa) then b else (f - wdim wa)
|
||||
in if isNothing snapdist || abs (newpos - wpos wa) <= fromJust snapdist then newpos else (wpos wa)
|
||||
where
|
||||
(wpos, wdim, _, _) = constructors horiz
|
||||
|
||||
-- | Move a window in the specified direction until it snaps against another window or the edge of the screen.
|
||||
snapMove
|
||||
:: Direction2D -- ^ What direction to move the window in.
|
||||
-> Maybe Int -- ^ The distance in the orthogonal axis to look for windows to snap against. Use Nothing to snap against every window.
|
||||
-> Window -- ^ The window to move.
|
||||
-> X ()
|
||||
snapMove L = doSnapMove True True
|
||||
snapMove R = doSnapMove True False
|
||||
snapMove U = doSnapMove False True
|
||||
snapMove D = doSnapMove False False
|
||||
|
||||
doSnapMove :: Bool -> Bool -> Maybe Int -> Window -> X ()
|
||||
doSnapMove horiz rev collidedist w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
io $ raiseWindow d w
|
||||
wa <- io $ getWindowAttributes d w
|
||||
((bl,br,_),(fl,fr,_)) <- getSnap horiz collidedist d w
|
||||
|
||||
let (mb,mf) = if rev then (bl,fl)
|
||||
else (br,fr)
|
||||
|
||||
newpos = fromIntegral $ case (mb,mf) of
|
||||
(Just b,Nothing) -> b
|
||||
(Nothing,Just f) -> f - wdim wa
|
||||
(Just b,Just f) -> if rev /= (b < f - wdim wa)
|
||||
then b
|
||||
else f - wdim wa
|
||||
_ -> wpos wa
|
||||
|
||||
if horiz then io $ moveWindow d w newpos (fromIntegral $ wa_y wa)
|
||||
else io $ moveWindow d w (fromIntegral $ wa_x wa) newpos
|
||||
float w
|
||||
|
||||
where
|
||||
(wpos, wdim, _, _) = constructors horiz
|
||||
|
||||
-- | Grow the specified edge of a window until it snaps against another window or the edge of the screen.
|
||||
snapGrow
|
||||
:: Direction2D -- ^ What edge of the window to grow.
|
||||
-> Maybe Int -- ^ The distance in the orthogonal axis to look for windows to snap against. Use Nothing to snap against every window.
|
||||
-> Window -- ^ The window to grow.
|
||||
-> X ()
|
||||
snapGrow = snapResize True
|
||||
|
||||
-- | Shrink the specified edge of a window until it snaps against another window or the edge of the screen.
|
||||
snapShrink
|
||||
:: Direction2D -- ^ What edge of the window to shrink.
|
||||
-> Maybe Int -- ^ The distance in the orthogonal axis to look for windows to snap against. Use Nothing to snap against every window.
|
||||
-> Window -- ^ The window to shrink.
|
||||
-> X ()
|
||||
snapShrink = snapResize False
|
||||
|
||||
snapResize :: Bool -> Direction2D -> Maybe Int -> Window -> X ()
|
||||
snapResize grow dir collidedist w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
io $ raiseWindow d w
|
||||
wa <- io $ getWindowAttributes d w
|
||||
mr <- case dir of
|
||||
L -> do ((mg,ms,_),(_,_,_)) <- getSnap True collidedist d w
|
||||
return $ case (if grow then mg else ms) of
|
||||
Just v -> Just (v, wy wa, ww wa + wx wa - v, wh wa)
|
||||
_ -> Nothing
|
||||
R -> do ((_,_,_),(ms,mg,_)) <- getSnap True collidedist d w
|
||||
return $ case (if grow then mg else ms) of
|
||||
Just v -> Just (wx wa, wy wa, v - wx wa, wh wa)
|
||||
_ -> Nothing
|
||||
U -> do ((mg,ms,_),(_,_,_)) <- getSnap False collidedist d w
|
||||
return $ case (if grow then mg else ms) of
|
||||
Just v -> Just (wx wa, v, ww wa, wh wa + wy wa - v)
|
||||
_ -> Nothing
|
||||
D -> do ((_,_,_),(ms,mg,_)) <- getSnap False collidedist d w
|
||||
return $ case (if grow then mg else ms) of
|
||||
Just v -> Just (wx wa, wy wa, ww wa, v - wy wa)
|
||||
_ -> Nothing
|
||||
|
||||
case mr of
|
||||
Nothing -> return ()
|
||||
Just (nx,ny,nw,nh) -> if nw>0 && nh>0 then do io $ moveWindow d w (fromIntegral nx) (fromIntegral ny)
|
||||
io $ resizeWindow d w (fromIntegral nw) (fromIntegral nh)
|
||||
else return ()
|
||||
float w
|
||||
where
|
||||
wx = fromIntegral.wa_x
|
||||
wy = fromIntegral.wa_y
|
||||
ww = fromIntegral.wa_width
|
||||
wh = fromIntegral.wa_height
|
||||
|
||||
|
||||
getSnap :: Bool -> Maybe Int -> Display -> Window -> X ((Maybe Int,Maybe Int,Bool),(Maybe Int,Maybe Int,Bool))
|
||||
getSnap horiz collidedist d w = do
|
||||
wa <- io $ getWindowAttributes d w
|
||||
screen <- W.current <$> gets windowset
|
||||
let sr = screenRect $ W.screenDetail screen
|
||||
wl = W.integrate' . W.stack $ W.workspace screen
|
||||
gr <- fmap ($sr) $ calcGap $ S.fromList [minBound .. maxBound]
|
||||
wla <- filter (collides wa) `fmap` (io $ mapM (getWindowAttributes d) $ filter (/=w) wl)
|
||||
|
||||
return ( neighbours (back wa sr gr wla) (wpos wa)
|
||||
, neighbours (front wa sr gr wla) (wpos wa + wdim wa)
|
||||
)
|
||||
|
||||
where
|
||||
wborder = fromIntegral.wa_border_width
|
||||
|
||||
(wpos, wdim, rpos, rdim) = constructors horiz
|
||||
(refwpos, refwdim, _, _) = constructors $ not horiz
|
||||
|
||||
back wa sr gr wla = dropWhile (< rpos sr) $
|
||||
takeWhile (< rpos sr + rdim sr) $
|
||||
sort $ (rpos sr):(rpos gr):(rpos gr + rdim gr):
|
||||
foldr (\a as -> (wpos a):(wpos a + wdim a + wborder a + wborder wa):as) [] wla
|
||||
|
||||
front wa sr gr wla = dropWhile (<= rpos sr) $
|
||||
takeWhile (<= rpos sr + rdim sr) $
|
||||
sort $ (rpos gr - 2*wborder wa):(rpos gr + rdim gr - 2*wborder wa):(rpos sr + rdim sr - 2*wborder wa):
|
||||
foldr (\a as -> (wpos a - wborder a - wborder wa):(wpos a + wdim a):as) [] wla
|
||||
|
||||
neighbours l v = ( listToMaybe $ reverse $ takeWhile (< v) l
|
||||
, listToMaybe $ dropWhile (<= v) l
|
||||
, v `elem` l
|
||||
)
|
||||
|
||||
collides wa oa = case collidedist of
|
||||
Nothing -> True
|
||||
Just dist -> ( refwpos oa - wborder oa < refwpos wa + refwdim wa + wborder wa + dist
|
||||
&& refwpos wa - wborder wa - dist < refwpos oa + refwdim oa + wborder oa )
|
||||
|
||||
|
||||
constructors :: Bool -> (WindowAttributes -> Int, WindowAttributes -> Int, Rectangle -> Int, Rectangle -> Int)
|
||||
constructors True = ( fromIntegral.wa_x
|
||||
, fromIntegral.wa_width
|
||||
, fromIntegral.rect_x
|
||||
, fromIntegral.rect_width
|
||||
)
|
||||
constructors False = ( fromIntegral.wa_y
|
||||
, fromIntegral.wa_height
|
||||
, fromIntegral.rect_y
|
||||
, fromIntegral.rect_height
|
||||
)
|
@@ -5,7 +5,7 @@
|
||||
-- License : BSD
|
||||
--
|
||||
-- Maintainer : Karsten Schoelzel <kuser@gmx.de>
|
||||
-- Stability : unstable
|
||||
-- Stability : stable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Focus the nth window of the current workspace.
|
||||
@@ -14,7 +14,7 @@
|
||||
module XMonad.Actions.FocusNth (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
focusNth) where
|
||||
focusNth,focusNth') where
|
||||
|
||||
import XMonad.StackSet
|
||||
import XMonad
|
||||
@@ -27,7 +27,7 @@ import XMonad
|
||||
-- Then add appropriate keybindings, for example:
|
||||
--
|
||||
-- > -- mod4-[1..9] @@ Switch to window N
|
||||
-- > ++ [((modMask x, k), focusNth i)
|
||||
-- > ++ [((modm, k), focusNth i)
|
||||
-- > | (i, k) <- zip [0 .. 8] [xK_1 ..]]
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
|
@@ -1,3 +1,4 @@
|
||||
{-# LANGUAGE ScopedTypeVariables, GeneralizedNewtypeDeriving, TypeSynonymInstances, FlexibleInstances, OverlappingInstances #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.GridSelect
|
||||
@@ -8,28 +9,55 @@
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- GridSelect displays a 2D grid of windows to navigate with cursor
|
||||
-- keys and to select with return.
|
||||
-- GridSelect displays items(e.g. the opened windows) in a 2D grid and lets
|
||||
-- the user select from it with the cursor/hjkl keys or the mouse.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.GridSelect (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
|
||||
-- ** Customizing
|
||||
-- *** Using a common GSConfig
|
||||
-- $commonGSConfig
|
||||
|
||||
-- *** Custom keybindings
|
||||
-- $keybindings
|
||||
|
||||
-- * Configuration
|
||||
GSConfig(..),
|
||||
defaultGSConfig,
|
||||
NavigateMap,
|
||||
TwoDPosition,
|
||||
buildDefaultGSConfig,
|
||||
|
||||
-- * Variations on 'gridselect'
|
||||
gridselect,
|
||||
gridselectWindow,
|
||||
withSelectedWindow,
|
||||
bringSelected,
|
||||
goToSelected,
|
||||
default_colorizer
|
||||
spawnSelected,
|
||||
runSelectedAction,
|
||||
|
||||
-- * Colorizers
|
||||
HasColorizer(defaultColorizer),
|
||||
fromClassName,
|
||||
stringColorizer,
|
||||
colorRangeFromClassName
|
||||
|
||||
-- * Screenshots
|
||||
-- $screenshots
|
||||
) where
|
||||
import Data.Maybe
|
||||
import Data.Bits
|
||||
import Control.Applicative
|
||||
import Control.Monad.State
|
||||
import Control.Arrow
|
||||
import Data.List as L
|
||||
import XMonad
|
||||
import qualified Data.Map as M
|
||||
import XMonad hiding (liftX)
|
||||
import XMonad.Util.Font
|
||||
import XMonad.Prompt (mkUnmanagedWindow)
|
||||
import XMonad.StackSet as W
|
||||
@@ -37,6 +65,8 @@ import XMonad.Layout.Decoration
|
||||
import XMonad.Util.NamedWindows
|
||||
import XMonad.Actions.WindowBringer (bringWindow)
|
||||
import Text.Printf
|
||||
import System.Random (mkStdGen, genRange, next)
|
||||
import Data.Word (Word8)
|
||||
|
||||
-- $usage
|
||||
--
|
||||
@@ -46,33 +76,150 @@ import Text.Printf
|
||||
--
|
||||
-- Then add a keybinding, e.g.
|
||||
--
|
||||
-- > , ((modMask x, xK_g), goToSelected defaultGSConfig)
|
||||
-- > , ((modm, xK_g), goToSelected defaultGSConfig)
|
||||
--
|
||||
-- Screenshot: <http://clemens.endorphin.org/gridselect.png>
|
||||
-- This module also supports displaying arbitrary information in a grid and letting
|
||||
-- the user select from it. E.g. to spawn an application from a given list, you
|
||||
-- can use the following:
|
||||
--
|
||||
-- > , ((modm, xK_s), spawnSelected defaultGSConfig ["xterm","gmplayer","gvim"])
|
||||
|
||||
data GSConfig = GSConfig {
|
||||
-- $commonGSConfig
|
||||
--
|
||||
-- It is possible to bind a @gsconfig@ at top-level in your configuration. Like so:
|
||||
--
|
||||
-- > -- the top of your config
|
||||
-- > {-# LANGUAGE NoMonomorphismRestriction #-}
|
||||
-- > import XMonad
|
||||
-- > ...
|
||||
-- > gsconfig1 = defaultGSConfig { gs_cellheight = 30, gs_cellWidth = 100 }
|
||||
--
|
||||
-- An example where 'buildDefaultGSConfig' is used instead of 'defaultGSConfig'
|
||||
-- in order to specify a custom colorizer is @gsconfig2@ (found in
|
||||
-- "XMonad.Actions.GridSelect#Colorizers"):
|
||||
--
|
||||
-- > gsconfig2 colorizer = (buildDefaultGSConfig colorizer) { gs_cellheight = 30, gs_cellWidth = 100 }
|
||||
--
|
||||
-- > -- | A green monochrome colorizer based on window class
|
||||
-- > greenColorizer = colorRangeFromClassName
|
||||
-- > black -- lowest inactive bg
|
||||
-- > (0x70,0xFF,0x70) -- highest inactive bg
|
||||
-- > black -- active bg
|
||||
-- > white -- inactive fg
|
||||
-- > white -- active fg
|
||||
-- > where black = minBound
|
||||
-- > white = maxBound
|
||||
--
|
||||
-- Then you can bind to:
|
||||
--
|
||||
-- > ,((modm, xK_g), goToSelected $ gsconfig2 myWinColorizer)
|
||||
-- > ,((modm, xK_p), spawnSelected $ spawnSelected defaultColorizer)
|
||||
|
||||
-- $keybindings
|
||||
--
|
||||
-- Adding more keybindings for gridselect to listen to is similar:
|
||||
--
|
||||
-- At the top of your config:
|
||||
--
|
||||
-- > {-# LANGAUGE NoMonomorphismRestriction #-}
|
||||
-- > import XMonad
|
||||
-- > import qualified Data.Map as M
|
||||
--
|
||||
-- Then define @gsconfig3@ which may be used in exactly the same manner as @gsconfig1@:
|
||||
--
|
||||
-- > gsconfig3 = defaultGSConfig
|
||||
-- > { gs_cellheight = 30
|
||||
-- > , gs_cellwidth = 100
|
||||
-- > , gs_navigate = M.unions
|
||||
-- > [reset
|
||||
-- > ,nethackKeys
|
||||
-- > ,gs_navigate -- get the default navigation bindings
|
||||
-- > $ defaultGSConfig `asTypeOf` gsconfig3 -- needed to fix an ambiguous type variable
|
||||
-- > ]
|
||||
-- > }
|
||||
-- > where addPair (a,b) (x,y) = (a+x,b+y)
|
||||
-- > nethackKeys = M.map addPair $ M.fromList
|
||||
-- > [((0,xK_y),(-1,-1))
|
||||
-- > ,((0,xK_i),(1,-1))
|
||||
-- > ,((0,xK_n),(-1,1))
|
||||
-- > ,((0,xK_m),(1,1))
|
||||
-- > ]
|
||||
-- > -- jump back to the center with the spacebar, regardless of the current position.
|
||||
-- > reset = M.singleton (0,xK_space) (const (0,0))
|
||||
|
||||
-- $screenshots
|
||||
--
|
||||
-- Selecting a workspace:
|
||||
--
|
||||
-- <<http://haskell.org/sitewiki/images/a/a9/Xmonad-gridselect-workspace.png>>
|
||||
--
|
||||
-- Selecting a window by title:
|
||||
--
|
||||
-- <<http://haskell.org/sitewiki/images/3/35/Xmonad-gridselect-window-aavogt.png>>
|
||||
|
||||
data GSConfig a = GSConfig {
|
||||
gs_cellheight :: Integer,
|
||||
gs_cellwidth :: Integer,
|
||||
gs_cellpadding :: Integer,
|
||||
gs_colorizer :: Window -> Bool -> X (String, String),
|
||||
gs_font :: String
|
||||
gs_colorizer :: a -> Bool -> X (String, String),
|
||||
gs_font :: String,
|
||||
gs_navigate :: NavigateMap,
|
||||
gs_originFractX :: Double,
|
||||
gs_originFractY :: Double
|
||||
}
|
||||
|
||||
-- | That is 'fromClassName' if you are selecting a 'Window', or
|
||||
-- 'defaultColorizer' if you are selecting a 'String'. The catch-all instance
|
||||
-- @HasColorizer a@ uses the 'focusedBorderColor' and 'normalBorderColor'
|
||||
-- colors.
|
||||
class HasColorizer a where
|
||||
defaultColorizer :: a -> Bool -> X (String, String)
|
||||
|
||||
instance HasColorizer Window where
|
||||
defaultColorizer = fromClassName
|
||||
|
||||
instance HasColorizer String where
|
||||
defaultColorizer = stringColorizer
|
||||
|
||||
instance HasColorizer a where
|
||||
defaultColorizer _ isFg =
|
||||
let getColor = if isFg then focusedBorderColor else normalBorderColor
|
||||
in asks $ flip (,) "black" . getColor . config
|
||||
|
||||
-- | A basic configuration for 'gridselect', with the colorizer chosen based on the type.
|
||||
--
|
||||
-- If you want to replace the 'gs_colorizer' field, use 'buildDefaultGSConfig'
|
||||
-- instead, to avoid ambiguous type variables.
|
||||
defaultGSConfig :: HasColorizer a => GSConfig a
|
||||
defaultGSConfig = buildDefaultGSConfig defaultColorizer
|
||||
|
||||
type NavigateMap = M.Map (KeyMask,KeySym) (TwoDPosition -> TwoDPosition)
|
||||
|
||||
type TwoDPosition = (Integer, Integer)
|
||||
|
||||
type TwoDWindowMap = [(TwoDPosition,(String,Window))]
|
||||
type TwoDElementMap a = [(TwoDPosition,(String,a))]
|
||||
|
||||
data TwoDState = TwoDState { td_curpos :: TwoDPosition,
|
||||
td_windowmap :: [(TwoDPosition,(String,Window))],
|
||||
td_gsconfig :: GSConfig,
|
||||
td_font :: XMonadFont,
|
||||
td_paneX :: Integer,
|
||||
td_paneY :: Integer,
|
||||
td_drawingWin :: Window
|
||||
}
|
||||
data TwoDState a = TwoDState { td_curpos :: TwoDPosition
|
||||
, td_elementmap :: TwoDElementMap a
|
||||
, td_gsconfig :: GSConfig a
|
||||
, td_font :: XMonadFont
|
||||
, td_paneX :: Integer
|
||||
, td_paneY :: Integer
|
||||
, td_drawingWin :: Window
|
||||
}
|
||||
|
||||
newtype TwoD a b = TwoD { unTwoD :: StateT (TwoDState a) X b }
|
||||
deriving (Monad,Functor,MonadState (TwoDState a))
|
||||
|
||||
type TwoD a = StateT TwoDState X a
|
||||
instance Applicative (TwoD a) where
|
||||
(<*>) = ap
|
||||
pure = return
|
||||
|
||||
liftX :: X a1 -> TwoD a a1
|
||||
liftX = TwoD . lift
|
||||
|
||||
evalTwoD :: TwoD a1 a -> TwoDState a1 -> X a
|
||||
evalTwoD m s = flip evalStateT s $ unTwoD m
|
||||
|
||||
diamondLayer :: (Enum b', Num b') => b' -> [(b', b')]
|
||||
-- FIXME remove nub
|
||||
@@ -84,15 +231,17 @@ diamondLayer n = let ul = [ (x,n-x) | x <- [0..n] ]
|
||||
diamond :: (Enum a, Num a) => [(a, a)]
|
||||
diamond = concatMap diamondLayer [0..]
|
||||
|
||||
diamondRestrict :: (Enum t, Num t, Ord t) => t -> t -> [(t, t)]
|
||||
diamondRestrict x y = L.filter (\(x',y') -> abs x' <= x && abs y' <= y) .
|
||||
L.takeWhile (\(x',y') -> abs x' + abs y' <= x+y) $ diamond
|
||||
diamondRestrict :: Integer -> Integer -> Integer -> Integer -> [(Integer, Integer)]
|
||||
diamondRestrict x y originX originY =
|
||||
L.filter (\(x',y') -> abs x' <= x && abs y' <= y) .
|
||||
map (\(x', y') -> (x' + fromInteger originX, y' + fromInteger originY)) .
|
||||
take 1000 $ diamond
|
||||
|
||||
tupadd :: (Num t1, Num t) => (t, t1) -> (t, t1) -> (t, t1)
|
||||
tupadd (a,b) (c,d) = (a+c,b+d)
|
||||
|
||||
findInWindowMap :: (Eq a) => a -> [(a, b)] -> Maybe (a, b)
|
||||
findInWindowMap pos = find ((== pos) . fst)
|
||||
findInElementMap :: (Eq a) => a -> [(a, b)] -> Maybe (a, b)
|
||||
findInElementMap pos = find ((== pos) . fst)
|
||||
|
||||
drawWinBox :: Window -> XMonadFont -> (String, String) -> Integer -> Integer -> String -> Integer -> Integer -> Integer -> X ()
|
||||
drawWinBox win font (fg,bg) ch cw text x y cp =
|
||||
@@ -116,14 +265,14 @@ drawWinBox win font (fg,bg) ch cw text x y cp =
|
||||
liftIO $ freeGC dpy gc
|
||||
liftIO $ freeGC dpy bordergc
|
||||
|
||||
updateAllWindows :: TwoD ()
|
||||
updateAllWindows =
|
||||
updateAllElements :: TwoD a ()
|
||||
updateAllElements =
|
||||
do
|
||||
TwoDState { td_windowmap = wins } <- get
|
||||
updateWindows wins
|
||||
TwoDState { td_elementmap = els } <- get
|
||||
updateElements els
|
||||
|
||||
updateWindows :: TwoDWindowMap -> TwoD ()
|
||||
updateWindows windowmap = do
|
||||
updateElements :: TwoDElementMap a -> TwoD a ()
|
||||
updateElements elementmap = do
|
||||
TwoDState { td_curpos = curpos,
|
||||
td_drawingWin = win,
|
||||
td_gsconfig = gsconfig,
|
||||
@@ -134,8 +283,8 @@ updateWindows windowmap = do
|
||||
cellheight = gs_cellheight gsconfig
|
||||
paneX' = div (paneX-cellwidth) 2
|
||||
paneY' = div (paneY-cellheight) 2
|
||||
updateWindow (pos@(x,y),(text, clientwindow)) = lift $ do
|
||||
colors <- (gs_colorizer gsconfig) clientwindow (pos == curpos)
|
||||
updateElement (pos@(x,y),(text, element)) = liftX $ do
|
||||
colors <- gs_colorizer gsconfig element (pos == curpos)
|
||||
drawWinBox win font
|
||||
colors
|
||||
cellheight
|
||||
@@ -144,13 +293,12 @@ updateWindows windowmap = do
|
||||
(paneX'+x*cellwidth)
|
||||
(paneY'+y*cellheight)
|
||||
(gs_cellpadding gsconfig)
|
||||
mapM updateWindow windowmap
|
||||
return ()
|
||||
mapM_ updateElement elementmap
|
||||
|
||||
eventLoop :: TwoD (Maybe Window)
|
||||
eventLoop :: TwoD a (Maybe a)
|
||||
eventLoop = do
|
||||
(keysym,string,event) <- lift $ withDisplay $ \d -> liftIO $ allocaXEvent $ \e -> do
|
||||
nextEvent d e
|
||||
(keysym,string,event) <- liftX $ withDisplay $ \d -> liftIO $ allocaXEvent $ \e -> do
|
||||
maskEvent d (exposureMask .|. keyPressMask .|. buttonReleaseMask) e
|
||||
ev <- getEvent e
|
||||
(ks,s) <- if ev_event_type ev == keyPress
|
||||
then lookupString $ asKeyEvent e
|
||||
@@ -158,35 +306,41 @@ eventLoop = do
|
||||
return (ks,s,ev)
|
||||
handle (fromMaybe xK_VoidSymbol keysym,string) event
|
||||
|
||||
handle :: (KeySym, String)
|
||||
-> Event
|
||||
-> StateT TwoDState X (Maybe Window)
|
||||
handle (ks,_) (KeyEvent {ev_event_type = t})
|
||||
handle :: (KeySym, t) -> Event -> TwoD a (Maybe a)
|
||||
handle (ks,_) (KeyEvent {ev_event_type = t, ev_state = m })
|
||||
| t == keyPress && ks == xK_Escape = return Nothing
|
||||
| t == keyPress && (ks == xK_Left || ks == xK_h) = diffAndRefresh (-1,0)
|
||||
| t == keyPress && (ks == xK_Right || ks == xK_l) = diffAndRefresh (1,0)
|
||||
| t == keyPress && (ks == xK_Down || ks == xK_j) = diffAndRefresh (0,1)
|
||||
| t == keyPress && (ks == xK_Up || ks == xK_k) = diffAndRefresh (0,-1)
|
||||
| t == keyPress && ks == xK_Return = do
|
||||
(TwoDState { td_curpos = pos, td_windowmap = winmap }) <- get
|
||||
return $ fmap (snd . snd) $ findInWindowMap pos winmap
|
||||
(TwoDState { td_curpos = pos, td_elementmap = elmap }) <- get
|
||||
return $ fmap (snd . snd) $ findInElementMap pos elmap
|
||||
| t == keyPress = do
|
||||
m' <- liftX (cleanMask m)
|
||||
keymap <- gets (gs_navigate . td_gsconfig)
|
||||
maybe eventLoop diffAndRefresh . M.lookup (m',ks) $ keymap
|
||||
where diffAndRefresh diff = do
|
||||
state <- get
|
||||
let windowmap = td_windowmap state
|
||||
let elmap = td_elementmap state
|
||||
oldPos = td_curpos state
|
||||
newPos = oldPos `tupadd` diff
|
||||
newSelectedWin = findInWindowMap newPos windowmap
|
||||
when (isJust newSelectedWin) $ do
|
||||
newPos = diff oldPos
|
||||
newSelectedEl = findInElementMap newPos elmap
|
||||
when (isJust newSelectedEl) $ do
|
||||
put state { td_curpos = newPos }
|
||||
updateWindows (catMaybes [(findInWindowMap oldPos windowmap), newSelectedWin])
|
||||
updateElements (catMaybes [(findInElementMap oldPos elmap), newSelectedEl])
|
||||
eventLoop
|
||||
|
||||
handle _ (ExposeEvent { }) = do
|
||||
updateAllWindows
|
||||
eventLoop
|
||||
handle _ (ButtonEvent { ev_event_type = t, ev_x = x, ev_y = y })
|
||||
| t == buttonRelease = do
|
||||
(TwoDState { td_elementmap = elmap, td_paneX = px, td_paneY = py,
|
||||
td_gsconfig = (GSConfig ch cw _ _ _ _ _ _) }) <- get
|
||||
let gridX = (fi x - (px - cw) `div` 2) `div` cw
|
||||
gridY = (fi y - (py - ch) `div` 2) `div` ch
|
||||
case lookup (gridX,gridY) elmap of
|
||||
Just (_,el) -> return (Just el)
|
||||
Nothing -> eventLoop
|
||||
| otherwise = eventLoop
|
||||
|
||||
handle _ _ = do
|
||||
eventLoop
|
||||
handle _ (ExposeEvent { }) = updateAllElements >> eventLoop
|
||||
|
||||
handle _ _ = eventLoop
|
||||
|
||||
-- FIXME probably move that into Utils?
|
||||
-- Conversion scheme as in http://en.wikipedia.org/wiki/HSV_color_space
|
||||
@@ -206,52 +360,94 @@ hsv2rgb (h,s,v) =
|
||||
5 -> (v,p,q)
|
||||
_ -> error "The world is ending. x mod a >= a."
|
||||
|
||||
default_colorizer :: Window -> Bool -> X (String, String)
|
||||
default_colorizer w active = do
|
||||
classname <- runQuery className w
|
||||
let seed x = toInteger (sum $ map ((*x).fromEnum) classname) :: Integer
|
||||
-- | Default colorizer for Strings
|
||||
stringColorizer :: String -> Bool -> X (String, String)
|
||||
stringColorizer s active =
|
||||
let seed x = toInteger (sum $ map ((*x).fromEnum) s) :: Integer
|
||||
(r,g,b) = hsv2rgb ((seed 83) `mod` 360,
|
||||
(fromInteger ((seed 191) `mod` 1000))/2500+0.4,
|
||||
(fromInteger ((seed 121) `mod` 1000))/2500+0.4)
|
||||
if active
|
||||
then return ("#faff69", "black")
|
||||
else return ("#" ++ concat (map (twodigitHex.(round :: Double -> Integer).(*256)) [r, g, b] ), "white")
|
||||
where
|
||||
twodigitHex :: Integer -> String
|
||||
twodigitHex a = printf "%02x" a
|
||||
in if active
|
||||
then return ("#faff69", "black")
|
||||
else return ("#" ++ concat (map (twodigitHex.(round :: Double -> Word8).(*256)) [r, g, b] ), "white")
|
||||
|
||||
-- | Brings up a 2D grid of windows in the center of the screen, and one can
|
||||
-- select a window with cursors keys. The selected window is returned.
|
||||
gridselect :: GSConfig -> X (Maybe Window)
|
||||
gridselect gsconfig =
|
||||
-- | Colorize a window depending on it's className.
|
||||
fromClassName :: Window -> Bool -> X (String, String)
|
||||
fromClassName w active = runQuery className w >>= flip defaultColorizer active
|
||||
|
||||
twodigitHex :: Word8 -> String
|
||||
twodigitHex a = printf "%02x" a
|
||||
|
||||
-- | A colorizer that picks a color inside a range,
|
||||
-- and depending on the window's class.
|
||||
colorRangeFromClassName :: (Word8, Word8, Word8) -- ^ Beginning of the color range
|
||||
-> (Word8, Word8, Word8) -- ^ End of the color range
|
||||
-> (Word8, Word8, Word8) -- ^ Background of the active window
|
||||
-> (Word8, Word8, Word8) -- ^ Inactive text color
|
||||
-> (Word8, Word8, Word8) -- ^ Active text color
|
||||
-> Window -> Bool -> X (String, String)
|
||||
colorRangeFromClassName startC endC activeC inactiveT activeT w active =
|
||||
do classname <- runQuery className w
|
||||
if active
|
||||
then return (rgbToHex activeC, rgbToHex activeT)
|
||||
else return (rgbToHex $ mix startC endC
|
||||
$ stringToRatio classname, rgbToHex inactiveT)
|
||||
where rgbToHex :: (Word8, Word8, Word8) -> String
|
||||
rgbToHex (r, g, b) = '#':twodigitHex r
|
||||
++twodigitHex g++twodigitHex b
|
||||
|
||||
-- | Creates a mix of two colors according to a ratio
|
||||
-- (1 -> first color, 0 -> second color).
|
||||
mix :: (Word8, Word8, Word8) -> (Word8, Word8, Word8)
|
||||
-> Double -> (Word8, Word8, Word8)
|
||||
mix (r1, g1, b1) (r2, g2, b2) r = (mix' r1 r2, mix' g1 g2, mix' b1 b2)
|
||||
where mix' a b = truncate $ (fi a * r) + (fi b * (1 - r))
|
||||
|
||||
-- | Generates a Double from a string, trying to
|
||||
-- achieve a random distribution.
|
||||
-- We create a random seed from the sum of all characters
|
||||
-- in the string, and use it to generate a ratio between 0 and 1
|
||||
stringToRatio :: String -> Double
|
||||
stringToRatio "" = 0
|
||||
stringToRatio s = let gen = mkStdGen $ sum $ map fromEnum s
|
||||
range = (\(a, b) -> b - a) $ genRange gen
|
||||
randomInt = foldr1 combine $ replicate 20 next
|
||||
combine f1 f2 g = let (_, g') = f1 g in f2 g'
|
||||
in fi (fst $ randomInt gen) / fi range
|
||||
|
||||
-- | Brings up a 2D grid of elements in the center of the screen, and one can
|
||||
-- select an element with cursors keys. The selected element is returned.
|
||||
gridselect :: GSConfig a -> [(String,a)] -> X (Maybe a)
|
||||
gridselect gsconfig elmap =
|
||||
withDisplay $ \dpy -> do
|
||||
rootw <- liftIO $ rootWindow dpy (defaultScreen dpy)
|
||||
rootw <- asks theRoot
|
||||
s <- gets $ screenRect . W.screenDetail . W.current . windowset
|
||||
windowList <- windowMap
|
||||
win <- liftIO $ mkUnmanagedWindow dpy (defaultScreenOfDisplay dpy) rootw
|
||||
(rect_x s) (rect_y s) (rect_width s) (rect_height s)
|
||||
liftIO $ mapWindow dpy win
|
||||
liftIO $ selectInput dpy win (exposureMask .|. keyPressMask)
|
||||
liftIO $ selectInput dpy win (exposureMask .|. keyPressMask .|. buttonReleaseMask)
|
||||
status <- io $ grabKeyboard dpy win True grabModeAsync grabModeAsync currentTime
|
||||
io $ grabButton dpy button1 anyModifier win True buttonReleaseMask grabModeAsync grabModeAsync none none
|
||||
font <- initXMF (gs_font gsconfig)
|
||||
let screenWidth = toInteger $ rect_width s;
|
||||
screenHeight = toInteger $ rect_height s;
|
||||
selectedWindow <- if (status == grabSuccess) then
|
||||
do
|
||||
let restriction :: Integer -> (GSConfig -> Integer) -> Double
|
||||
restriction ss cs = ((fromInteger ss)/(fromInteger $ cs gsconfig)-1)/2
|
||||
selectedElement <- if (status == grabSuccess) then do
|
||||
let restriction ss cs = (fromInteger ss/fromInteger (cs gsconfig)-1)/2 :: Double
|
||||
restrictX = floor $ restriction screenWidth gs_cellwidth
|
||||
restrictY = floor $ restriction screenHeight gs_cellheight
|
||||
winmap = zipWith (,) (diamondRestrict restrictX restrictY) windowList
|
||||
selectedWindow <- evalStateT (do updateAllWindows; eventLoop)
|
||||
(TwoDState (0,0)
|
||||
winmap
|
||||
gsconfig
|
||||
font
|
||||
screenWidth
|
||||
screenHeight
|
||||
win)
|
||||
return selectedWindow
|
||||
originPosX = floor $ ((gs_originFractX gsconfig) - (1/2)) * 2 * fromIntegral restrictX
|
||||
originPosY = floor $ ((gs_originFractY gsconfig) - (1/2)) * 2 * fromIntegral restrictY
|
||||
coords = diamondRestrict restrictX restrictY originPosX originPosY
|
||||
elmap' = zip coords elmap
|
||||
|
||||
evalTwoD (updateAllElements >> eventLoop)
|
||||
(TwoDState (head coords)
|
||||
elmap'
|
||||
gsconfig
|
||||
font
|
||||
screenWidth
|
||||
screenHeight
|
||||
win)
|
||||
else
|
||||
return Nothing
|
||||
liftIO $ do
|
||||
@@ -259,19 +455,22 @@ gridselect gsconfig =
|
||||
destroyWindow dpy win
|
||||
sync dpy False
|
||||
releaseXMF font
|
||||
return selectedWindow
|
||||
return selectedElement
|
||||
|
||||
-- | Like `gridSelect' but with the current windows and their titles as elements
|
||||
gridselectWindow :: GSConfig Window -> X (Maybe Window)
|
||||
gridselectWindow gsconf = windowMap >>= gridselect gsconf
|
||||
|
||||
-- | Brings up a 2D grid of windows in the center of the screen, and one can
|
||||
-- select a window with cursors keys. The selected window is then passed to
|
||||
-- a callback function.
|
||||
withSelectedWindow :: (Window -> X ()) -> GSConfig -> X ()
|
||||
withSelectedWindow :: (Window -> X ()) -> GSConfig Window -> X ()
|
||||
withSelectedWindow callback conf = do
|
||||
mbWindow <- gridselect conf
|
||||
mbWindow <- gridselectWindow conf
|
||||
case mbWindow of
|
||||
Just w -> callback w
|
||||
Nothing -> return ()
|
||||
|
||||
|
||||
windowMap :: X [(String,Window)]
|
||||
windowMap = do
|
||||
ws <- gets windowset
|
||||
@@ -283,20 +482,44 @@ decorateName' :: Window -> X String
|
||||
decorateName' w = do
|
||||
fmap show $ getName w
|
||||
|
||||
defaultGSConfig :: GSConfig
|
||||
defaultGSConfig = GSConfig 50 130 10 default_colorizer "xft:Sans-8"
|
||||
-- | Builds a default gs config from a colorizer function.
|
||||
buildDefaultGSConfig :: (a -> Bool -> X (String,String)) -> GSConfig a
|
||||
buildDefaultGSConfig col = GSConfig 50 130 10 col "xft:Sans-8" defaultGSNav (1/2) (1/2)
|
||||
|
||||
defaultGSNav :: NavigateMap
|
||||
defaultGSNav = M.map tupadd $ M.fromList
|
||||
[((0,xK_Left) ,(-1,0))
|
||||
,((0,xK_h) ,(-1,0))
|
||||
,((0,xK_Right),(1,0))
|
||||
,((0,xK_l) ,(1,0))
|
||||
,((0,xK_Down) ,(0,1))
|
||||
,((0,xK_j) ,(0,1))
|
||||
,((0,xK_Up) ,(0,-1))
|
||||
,((0,xK_k) ,(0,-1))
|
||||
]
|
||||
|
||||
borderColor :: String
|
||||
borderColor = "white"
|
||||
|
||||
-- | Brings selected window to the current workspace.
|
||||
bringSelected :: GSConfig -> X ()
|
||||
bringSelected :: GSConfig Window -> X ()
|
||||
bringSelected = withSelectedWindow $ \w -> do
|
||||
windows (bringWindow w)
|
||||
XMonad.focus w
|
||||
windows W.shiftMaster
|
||||
|
||||
-- | Switches to selected window's workspace and focuses that window.
|
||||
goToSelected :: GSConfig -> X ()
|
||||
goToSelected :: GSConfig Window -> X ()
|
||||
goToSelected = withSelectedWindow $ windows . W.focusWindow
|
||||
|
||||
-- | Select an application to spawn from a given list
|
||||
spawnSelected :: GSConfig String -> [String] -> X ()
|
||||
spawnSelected conf lst = gridselect conf (zip lst lst) >>= flip whenJust spawn
|
||||
|
||||
-- | Select an action and run it in the X monad
|
||||
runSelectedAction :: GSConfig (X ()) -> [(String, X ())] -> X ()
|
||||
runSelectedAction conf actions = do
|
||||
selectedActionM <- gridselect conf actions
|
||||
case selectedActionM of
|
||||
Just selectedAction -> selectedAction
|
||||
Nothing -> return ()
|
||||
|
99
XMonad/Actions/MessageFeedback.hs
Normal file
99
XMonad/Actions/MessageFeedback.hs
Normal file
@@ -0,0 +1,99 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.MessageFeedback
|
||||
-- Copyright : (c) Quentin Moser <quentin.moser@unifr.ch>
|
||||
-- License : BSD3
|
||||
--
|
||||
-- Maintainer : None
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Alternative to 'XMonad.Operations.sendMessage' that provides knowledge
|
||||
-- of whether the message was handled, and utility functions based on
|
||||
-- this facility.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.MessageFeedback (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
|
||||
send
|
||||
, tryMessage
|
||||
, tryMessage_
|
||||
, tryInOrder
|
||||
, tryInOrder_
|
||||
, sm
|
||||
, sendSM
|
||||
, sendSM_
|
||||
) where
|
||||
|
||||
import XMonad.Core ( X (), Message, SomeMessage(..), LayoutClass(..), windowset, catchX )
|
||||
import XMonad.StackSet ( current, workspace, layout, tag )
|
||||
import XMonad.Operations ( updateLayout )
|
||||
|
||||
import Control.Monad.State ( gets )
|
||||
import Data.Maybe ( isJust )
|
||||
import Control.Applicative ((<$>))
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Actions.MessageFeedback
|
||||
--
|
||||
-- You can then use this module's functions wherever an action is expected.
|
||||
--
|
||||
-- Note that most functions in this module have a return type of @X Bool@
|
||||
-- whereas configuration options will expect a @X ()@ action.
|
||||
-- For example, the key binding
|
||||
--
|
||||
-- > -- Shrink the master area of a tiled layout, or move the focused window
|
||||
-- > -- to the left in a WindowArranger-based layout
|
||||
-- > ((modKey, xK_Left), tryMessage Shrink (MoveLeft 50))
|
||||
--
|
||||
-- is mis-typed. For this reason, this module provides alternatives (ending with
|
||||
-- an underscore, e.g. tryMessage_) that discard their result and return an @X ()@.
|
||||
-- For example, to correct the previous example:
|
||||
--
|
||||
-- > ((modKey, xK_Left), tryMessage_ Shrink (MoveLeft 50))
|
||||
--
|
||||
|
||||
|
||||
-- | Behaves like 'XMonad.Operations.sendMessage', but returns True of the
|
||||
-- message was handled by the layout, False otherwise.
|
||||
send :: Message a => a -> X Bool
|
||||
send = sendSM . sm
|
||||
|
||||
-- | Sends the first message, and if it was not handled, sends the second.
|
||||
-- Returns True if either message was handled, False otherwise.
|
||||
tryMessage :: (Message a, Message b) => a -> b -> X Bool
|
||||
tryMessage m1 m2 = do b <- send m1
|
||||
if b then return True else send m2
|
||||
|
||||
tryMessage_ :: (Message a, Message b) => a -> b -> X ()
|
||||
tryMessage_ m1 m2 = tryMessage m1 m2 >> return ()
|
||||
|
||||
-- | Tries sending every message of the list in order until one of them
|
||||
-- is handled. Returns True if one of the messages was handled, False otherwise.
|
||||
tryInOrder :: [SomeMessage] -> X Bool
|
||||
tryInOrder [] = return False
|
||||
tryInOrder (m:ms) = do b <- sendSM m
|
||||
if b then return True else tryInOrder ms
|
||||
|
||||
tryInOrder_ :: [SomeMessage] -> X ()
|
||||
tryInOrder_ ms = tryInOrder ms >> return ()
|
||||
|
||||
|
||||
-- | Convenience shorthand for 'XMonad.Core.SomeMessage'.
|
||||
sm :: Message a => a -> SomeMessage
|
||||
sm = SomeMessage
|
||||
|
||||
|
||||
sendSM :: SomeMessage -> X Bool
|
||||
sendSM m = do w <- workspace . current <$> gets windowset
|
||||
ml' <- handleMessage (layout w) m `catchX` return Nothing
|
||||
updateLayout (tag w) ml'
|
||||
return $ isJust ml'
|
||||
|
||||
|
||||
sendSM_ :: SomeMessage -> X ()
|
||||
sendSM_ m = sendSM m >> return ()
|
@@ -15,14 +15,14 @@
|
||||
module XMonad.Actions.MouseGestures (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
Direction(..),
|
||||
Direction2D(..),
|
||||
mouseGestureH,
|
||||
mouseGesture,
|
||||
mkCollect
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Hooks.ManageDocks (Direction(..))
|
||||
import XMonad.Util.Types (Direction2D(..))
|
||||
|
||||
import Data.IORef
|
||||
import qualified Data.Map as M
|
||||
@@ -39,7 +39,7 @@ import Control.Monad
|
||||
--
|
||||
-- then add an appropriate mouse binding:
|
||||
--
|
||||
-- > , ((modMask x .|. shiftMask, button3), mouseGesture gestures)
|
||||
-- > , ((modm .|. shiftMask, button3), mouseGesture gestures)
|
||||
--
|
||||
-- where @gestures@ is a 'Data.Map.Map' from gestures to actions on
|
||||
-- windows, for example:
|
||||
@@ -64,10 +64,10 @@ delta (ax, ay) (bx, by) = max (d ax bx) (d ay by)
|
||||
where
|
||||
d a b = abs (a - b)
|
||||
|
||||
dir :: Pos -> Pos -> Direction
|
||||
dir :: Pos -> Pos -> Direction2D
|
||||
dir (ax, ay) (bx, by) = trans . (/ pi) $ atan2 (fromIntegral $ ay - by) (fromIntegral $ bx - ax)
|
||||
where
|
||||
trans :: Double -> Direction
|
||||
trans :: Double -> Direction2D
|
||||
trans x
|
||||
| rg (-3/4) (-1/4) x = D
|
||||
| rg (-1/4) (1/4) x = R
|
||||
@@ -75,7 +75,7 @@ dir (ax, ay) (bx, by) = trans . (/ pi) $ atan2 (fromIntegral $ ay - by) (fromInt
|
||||
| otherwise = L
|
||||
rg a z x = a <= x && x < z
|
||||
|
||||
gauge :: (Direction -> X ()) -> Pos -> IORef (Maybe (Direction, Pos)) -> Position -> Position -> X ()
|
||||
gauge :: (Direction2D -> X ()) -> Pos -> IORef (Maybe (Direction2D, Pos)) -> Position -> Position -> X ()
|
||||
gauge hook op st nx ny = do
|
||||
let np = (nx, ny)
|
||||
stx <- io $ readIORef st
|
||||
@@ -96,7 +96,7 @@ gauge hook op st nx ny = do
|
||||
-- | @'mouseGestureH' moveHook endHook@ is a mouse button
|
||||
-- event handler. It collects mouse movements, calling @moveHook@ for each
|
||||
-- update; when the button is released, it calls @endHook@.
|
||||
mouseGestureH :: (Direction -> X ()) -> X () -> X ()
|
||||
mouseGestureH :: (Direction2D -> X ()) -> X () -> X ()
|
||||
mouseGestureH moveHook endHook = do
|
||||
dpy <- asks display
|
||||
root <- asks theRoot
|
||||
@@ -108,7 +108,7 @@ mouseGestureH moveHook endHook = do
|
||||
|
||||
-- | A utility function on top of 'mouseGestureH'. It uses a 'Data.Map.Map' to
|
||||
-- look up the mouse gesture, then executes the corresponding action (if any).
|
||||
mouseGesture :: Map [Direction] (Window -> X ()) -> Window -> X ()
|
||||
mouseGesture :: Map [Direction2D] (Window -> X ()) -> Window -> X ()
|
||||
mouseGesture tbl win = do
|
||||
(mov, end) <- mkCollect
|
||||
mouseGestureH (\d -> mov d >> return ()) $ end >>= \gest ->
|
||||
@@ -121,7 +121,7 @@ mouseGesture tbl win = do
|
||||
-- collect mouse movements (and return the current gesture as a list); the end
|
||||
-- hook will return a list of the completed gesture, which you can access with
|
||||
-- 'Control.Monad.>>='.
|
||||
mkCollect :: (MonadIO m, MonadIO m') => m (Direction -> m' [Direction], m' [Direction])
|
||||
mkCollect :: (MonadIO m, MonadIO m') => m (Direction2D -> m' [Direction2D], m' [Direction2D])
|
||||
mkCollect = liftIO $ do
|
||||
acc <- newIORef []
|
||||
let
|
||||
|
@@ -47,11 +47,11 @@ import XMonad.Util.XUtils
|
||||
--
|
||||
-- Then edit your @layoutHook@ by modifying a given layout:
|
||||
--
|
||||
-- > myLayouts = mouseResize $ windowArrange $ layoutHook defaultConfig
|
||||
-- > myLayout = mouseResize $ windowArrange $ layoutHook defaultConfig
|
||||
--
|
||||
-- and then:
|
||||
--
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayouts }
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayout }
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
|
@@ -5,7 +5,7 @@
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Lukas Mai <l.mai@web.de>
|
||||
-- Stability : unstable
|
||||
-- Stability : stable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- This module provides helper functions for dealing with window borders.
|
||||
@@ -21,7 +21,7 @@ import XMonad
|
||||
-- | Toggle the border of the currently focused window. To use it, add a
|
||||
-- keybinding like so:
|
||||
--
|
||||
-- > , ((modMask x, xK_g ), withFocused toggleBorder)
|
||||
-- > , ((modm, xK_g ), withFocused toggleBorder)
|
||||
--
|
||||
toggleBorder :: Window -> X ()
|
||||
toggleBorder w = do
|
||||
|
115
XMonad/Actions/OnScreen.hs
Normal file
115
XMonad/Actions/OnScreen.hs
Normal file
@@ -0,0 +1,115 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.OnScreen
|
||||
-- Copyright : (c) 2009 Nils Schweinsberg
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Nils Schweinsberg <mail@n-sch.de>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Control workspaces on different screens (in xinerama mode).
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.OnScreen (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
onScreen
|
||||
, viewOnScreen
|
||||
, greedyViewOnScreen
|
||||
, onlyOnScreen
|
||||
) where
|
||||
|
||||
import XMonad.StackSet
|
||||
import Control.Monad(guard)
|
||||
import Data.List
|
||||
import Data.Maybe(fromMaybe)
|
||||
import Data.Function(on)
|
||||
|
||||
-- $usage
|
||||
--
|
||||
-- This module provides an easy way to control, what you see on other screens in
|
||||
-- xinerama mode without having to focus them. Put this into your
|
||||
-- @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Actions.OnScreen
|
||||
--
|
||||
-- Then add the appropriate keybindings, for example replace your current keys
|
||||
-- to switch the workspaces with this at the bottom of your keybindings:
|
||||
--
|
||||
-- > ++
|
||||
-- > [ ((m .|. modm, k), windows (f i))
|
||||
-- > | (i, k) <- zip (workspaces conf) ([xK_1 .. xK_9] ++ [xK_0])
|
||||
-- > , (f, m) <- [ (viewOnScreen 0, 0)
|
||||
-- > , (viewOnScreen 1, controlMask)
|
||||
-- > , (greedyView, controlMask .|. shiftMask) ]
|
||||
-- > ]
|
||||
--
|
||||
-- This will provide you with the following keybindings:
|
||||
--
|
||||
-- * modkey + 1-0:
|
||||
-- Switch to workspace 1-0 on screen 0
|
||||
--
|
||||
-- * modkey + control + 1-0:
|
||||
-- Switch to workspace 1-0 on screen 1
|
||||
--
|
||||
-- * modkey + control + shift + 1-0:
|
||||
-- Default greedyView behaviour
|
||||
--
|
||||
--
|
||||
-- A more basic version inside the default keybindings would be:
|
||||
--
|
||||
-- > , ((modm .|. controlMask, xK_1) windows (viewOnScreen 0 "1"))
|
||||
--
|
||||
-- where 0 is the first screen and "1" the workspace with the tag "1".
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
||||
-- | Switch to the (hidden) workspace with index 'i' on the screen 'sc'.
|
||||
-- A default function (for example 'view' or 'greedyView') will be run if 'sc' is
|
||||
-- the current screen, no valid screen id or workspace 'i' is already visible.
|
||||
onScreen :: (Eq sid, Eq i)
|
||||
=> (i -> StackSet i l a sid sd -> StackSet i l a sid sd) -- ^ default action
|
||||
-> sid -- ^ screen id
|
||||
-> i -- ^ index of the workspace
|
||||
-> StackSet i l a sid sd -- ^ current stack
|
||||
-> StackSet i l a sid sd
|
||||
onScreen defFunc sc i st = fromMaybe (defFunc i st) $ do
|
||||
-- on unfocused current screen
|
||||
guard $ screen (current st) /= sc
|
||||
x <- find ((i==) . tag ) (hidden st)
|
||||
s <- find ((sc==) . screen) (screens st)
|
||||
o <- find ((sc==) . screen) (visible st)
|
||||
let newScreen = s { workspace = x }
|
||||
return st { visible = newScreen : deleteBy ((==) `on` screen) newScreen (visible st)
|
||||
, hidden = workspace o : deleteBy ((==) `on` tag) x (hidden st)
|
||||
}
|
||||
|
||||
-- | Switch to workspace 'i' on screen 'sc'. If 'i' is visible use 'greedyView'
|
||||
-- to switch the current workspace with workspace 'i'.
|
||||
greedyViewOnScreen :: (Eq sid, Eq i)
|
||||
=> sid -- ^ screen id
|
||||
-> i -- ^ index of the workspace
|
||||
-> StackSet i l a sid sd -- ^ current stack
|
||||
-> StackSet i l a sid sd
|
||||
greedyViewOnScreen = onScreen greedyView
|
||||
|
||||
-- | Switch to workspace 'i' on screen 'sc'. If 'i' is visible use 'view' to
|
||||
-- switch focus to the workspace 'i'.
|
||||
viewOnScreen :: (Eq sid, Eq i)
|
||||
=> sid -- ^ screen id
|
||||
-> i -- ^ index of the workspace
|
||||
-> StackSet i l a sid sd -- ^ current stack
|
||||
-> StackSet i l a sid sd
|
||||
viewOnScreen = onScreen view
|
||||
|
||||
-- | Switch to workspace 'i' on screen 'sc'. If 'i' is visible do nothing.
|
||||
onlyOnScreen :: (Eq sid, Eq i)
|
||||
=> sid -- ^ screen id
|
||||
-> i -- ^ index of the workspace
|
||||
-> StackSet i l a sid sd -- ^ current stack
|
||||
-> StackSet i l a sid sd
|
||||
onlyOnScreen = onScreen doNothing
|
||||
where doNothing _ st = st
|
88
XMonad/Actions/PhysicalScreens.hs
Normal file
88
XMonad/Actions/PhysicalScreens.hs
Normal file
@@ -0,0 +1,88 @@
|
||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.PhysicalScreens
|
||||
-- Copyright : (c) Nelson Elhage <nelhage@mit.edu>
|
||||
-- License : BSD
|
||||
--
|
||||
-- Maintainer : Nelson Elhage <nelhage@mit.edu>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Manipulate screens ordered by physical location instead of ID
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.PhysicalScreens (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
PhysicalScreen(..)
|
||||
, getScreen
|
||||
, viewScreen
|
||||
, sendToScreen
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import qualified Graphics.X11.Xlib as X
|
||||
import Graphics.X11.Xinerama
|
||||
|
||||
import Data.List (sortBy)
|
||||
import Data.Function (on)
|
||||
|
||||
{- $usage
|
||||
|
||||
This module allows you name Xinerama screens from XMonad using their
|
||||
physical location relative to each other (as reported by Xinerama),
|
||||
rather than their @ScreenID@ s, which are arbitrarily determined by
|
||||
your X server and graphics hardware.
|
||||
|
||||
Screens are ordered by the upper-left-most corner, from top-to-bottom
|
||||
and then left-to-right.
|
||||
|
||||
Example usage in your @~\/.xmonad\/xmonad.hs@ file:
|
||||
|
||||
> import XMonad.Actions.PhysicalSCreens
|
||||
|
||||
> --
|
||||
> -- mod-{w,e,r}, Switch to physical/Xinerama screens 1, 2, or 3
|
||||
> -- mod-shift-{w,e,r}, Move client to screen 1, 2, or 3
|
||||
> --
|
||||
> [((modm .|. mask, key), f sc)
|
||||
> | (key, sc) <- zip [xK_w, xK_e, xK_r] [0..]
|
||||
> , (f, mask) <- [(viewScreen, 0), (sendToScreen, shiftMask)]]
|
||||
|
||||
For detailed instructions on editing your key bindings, see
|
||||
"XMonad.Doc.Extending#Editing_key_bindings".
|
||||
-}
|
||||
|
||||
-- | The type of the index of a screen by location
|
||||
newtype PhysicalScreen = P Int deriving (Eq,Ord,Show,Read,Enum,Num,Integral,Real)
|
||||
|
||||
-- | Translate a physical screen index to a "ScreenId"
|
||||
getScreen :: PhysicalScreen -> X (Maybe ScreenId)
|
||||
getScreen (P i) = withDisplay $ \dpy -> do
|
||||
screens <- io $ getScreenInfo dpy
|
||||
if i >= length screens
|
||||
then return Nothing
|
||||
else let ss = sortBy (cmpScreen `on` fst) $ zip screens [0..]
|
||||
in return $ Just $ snd $ ss !! i
|
||||
|
||||
-- | Switch to a given physical screen
|
||||
viewScreen :: PhysicalScreen -> X ()
|
||||
viewScreen p = do i <- getScreen p
|
||||
whenJust i $ \s -> do
|
||||
w <- screenWorkspace s
|
||||
whenJust w $ windows . W.view
|
||||
|
||||
-- | Send the active window to a given physical screen
|
||||
sendToScreen :: PhysicalScreen -> X ()
|
||||
sendToScreen p = do i <- getScreen p
|
||||
whenJust i $ \s -> do
|
||||
w <- screenWorkspace s
|
||||
whenJust w $ windows . W.shift
|
||||
|
||||
-- | Compare two screens by their top-left corners, ordering
|
||||
-- | top-to-bottom and then left-to-right.
|
||||
cmpScreen :: Rectangle -> Rectangle -> Ordering
|
||||
cmpScreen (Rectangle x1 y1 _ _) (Rectangle x2 y2 _ _) = compare (y1,x1) (y2,x2)
|
@@ -78,9 +78,9 @@ data Limits
|
||||
-- divisor, the last line will have the remaining workspaces.
|
||||
data Lines
|
||||
= GConf -- ^ Use @gconftool-2@ to find out the number of lines.
|
||||
| Lines Int -- ^ Specify the number of lines explicity.
|
||||
| Lines Int -- ^ Specify the number of lines explicitly.
|
||||
|
||||
-- | This is the way most people would like to use this module. It ataches the
|
||||
-- | This is the way most people would like to use this module. It attaches the
|
||||
-- 'KeyMask' passed as a parameter with 'xK_Left', 'xK_Up', 'xK_Right' and
|
||||
-- 'xK_Down', associating it with 'planeMove' to the corresponding 'Direction'.
|
||||
-- It also associates these bindings with 'shiftMask' to 'planeShift'.
|
||||
|
@@ -33,7 +33,7 @@ import XMonad.StackSet
|
||||
--
|
||||
-- then add a keybinding or substitute 'promote' in place of swapMaster:
|
||||
--
|
||||
-- > , ((modMask x, xK_Return), promote)
|
||||
-- > , ((modm, xK_Return), promote)
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
72
XMonad/Actions/RandomBackground.hs
Normal file
72
XMonad/Actions/RandomBackground.hs
Normal file
@@ -0,0 +1,72 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.RandomBackground
|
||||
-- Copyright : (c) 2009 Anze Slosar
|
||||
-- translation to Haskell by Adam Vogt
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : <vogt.adam@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- An action to start terminals with a random background color
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.RandomBackground (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
randomBg',
|
||||
randomBg,
|
||||
RandomColor(HSV,RGB)
|
||||
) where
|
||||
|
||||
import XMonad(X, XConf(config), XConfig(terminal), io, spawn,
|
||||
MonadIO, asks)
|
||||
import System.Random
|
||||
import Control.Monad(liftM)
|
||||
import Numeric(showHex)
|
||||
|
||||
-- $usage
|
||||
--
|
||||
-- Add to your keybindings something like:
|
||||
--
|
||||
-- > ,((modm .|. shiftMask, xK_Return), randomBg $ HSV 0xff 0x20
|
||||
|
||||
-- | RandomColor fixes constraints when generating random colors. All
|
||||
-- parameters should be in the range 0 -- 0xff
|
||||
data RandomColor = RGB { _colorMin :: Int
|
||||
, _colorMax :: Int
|
||||
} -- ^ specify the minimum and maximum lowest values for each color channel.
|
||||
| HSV { _colorSaturation :: Double
|
||||
, _colorValue :: Double
|
||||
} -- ^ specify the saturation and value, leaving the hue random.
|
||||
|
||||
toHex :: [Int] -> String
|
||||
toHex = ("'#"++) . (++"'") . concatMap (ensure 2 . ($ "") . showHex)
|
||||
where ensure x = reverse . take x . (++repeat '0') . reverse
|
||||
|
||||
randPermutation :: (RandomGen g) => [a] -> g -> [a]
|
||||
randPermutation xs g = swap $ zip (randoms g) xs
|
||||
where
|
||||
swap ((True,x):(c,y):ys) = y:swap ((c,x):ys)
|
||||
swap ((False,x):ys) = x:swap ys
|
||||
swap x = map snd x
|
||||
|
||||
-- | @randomBg'@ produces a random hex number in the form @'#xxyyzz'@
|
||||
randomBg' :: (MonadIO m) => RandomColor -> m String
|
||||
randomBg' (RGB l h) = io $ liftM (toHex . take 3 . randomRs (l,h)) newStdGen
|
||||
randomBg' (HSV s v) = io $ do
|
||||
g <- newStdGen
|
||||
let x = (^(2::Int)) $ fst $ randomR (0,sqrt $ pi / 3) g
|
||||
return $ toHex $ map round $ randPermutation [v,(v-s)*x + s,s] g
|
||||
|
||||
-- | @randomBg@ starts a terminal with the background color taken from 'randomBg''
|
||||
--
|
||||
-- This depends on the your 'terminal' configuration field accepting an
|
||||
-- argument like @-bg '#ff0023'@
|
||||
randomBg :: RandomColor -> X ()
|
||||
randomBg x = do
|
||||
t <- asks (terminal . config)
|
||||
c <- randomBg' x
|
||||
spawn $ t ++ " -bg " ++ c
|
@@ -5,7 +5,7 @@
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Hans Philipp Annen <haphi@gmx.net>
|
||||
-- Stability : unstable
|
||||
-- Stability : stable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Rotate all windows except the master window and keep the focus in
|
||||
@@ -28,7 +28,7 @@ import XMonad
|
||||
--
|
||||
-- and add whatever keybindings you would like, for example:
|
||||
--
|
||||
-- > , ((modMask x .|. shiftMask, xK_Tab ), rotSlavesUp)
|
||||
-- > , ((modm .|. shiftMask, xK_Tab ), rotSlavesUp)
|
||||
--
|
||||
-- This operation will rotate all windows except the master window,
|
||||
-- while the focus stays where it is. It is useful together with the
|
||||
|
@@ -10,45 +10,56 @@
|
||||
Modeled after the handy Surfraw CLI search tools at <https://secure.wikimedia.org/wikipedia/en/wiki/Surfraw>.
|
||||
|
||||
Additional sites welcomed. -}
|
||||
module XMonad.Actions.Search ( -- * Usage
|
||||
-- $usage
|
||||
search,
|
||||
SearchEngine(..),
|
||||
searchEngine,
|
||||
promptSearch,
|
||||
promptSearchBrowser,
|
||||
selectSearch,
|
||||
selectSearchBrowser,
|
||||
|
||||
amazon,
|
||||
codesearch,
|
||||
deb,
|
||||
debbts,
|
||||
debpts,
|
||||
dictionary,
|
||||
google,
|
||||
hackage,
|
||||
hoogle,
|
||||
images,
|
||||
imdb,
|
||||
isohunt,
|
||||
maps,
|
||||
mathworld,
|
||||
scholar,
|
||||
thesaurus,
|
||||
wayback,
|
||||
wikipedia,
|
||||
youtube
|
||||
module XMonad.Actions.Search ( -- * Usage
|
||||
-- $usage
|
||||
search,
|
||||
SearchEngine(..),
|
||||
searchEngine,
|
||||
searchEngineF,
|
||||
promptSearch,
|
||||
promptSearchBrowser,
|
||||
selectSearch,
|
||||
selectSearchBrowser,
|
||||
isPrefixOf,
|
||||
escape,
|
||||
use,
|
||||
intelligent,
|
||||
(!>),
|
||||
prefixAware,
|
||||
namedEngine,
|
||||
|
||||
amazon,
|
||||
alpha,
|
||||
codesearch,
|
||||
deb,
|
||||
debbts,
|
||||
debpts,
|
||||
dictionary,
|
||||
google,
|
||||
hackage,
|
||||
hoogle,
|
||||
images,
|
||||
imdb,
|
||||
isohunt,
|
||||
lucky,
|
||||
maps,
|
||||
mathworld,
|
||||
scholar,
|
||||
thesaurus,
|
||||
wayback,
|
||||
wikipedia,
|
||||
wiktionary,
|
||||
youtube,
|
||||
multi
|
||||
-- * Use case: searching with a submap
|
||||
-- $tip
|
||||
|
||||
) where
|
||||
|
||||
import Data.Char (chr, ord, isAlpha, isMark, isDigit)
|
||||
import Data.List (isPrefixOf)
|
||||
import Numeric (showIntAtBase)
|
||||
import XMonad (X(), MonadIO, liftIO)
|
||||
import XMonad.Prompt (XPrompt(showXPrompt), mkXPrompt, XPConfig(), historyCompletion)
|
||||
import XMonad.Prompt (XPrompt(showXPrompt), mkXPrompt, XPConfig(), historyCompletionP)
|
||||
import XMonad.Prompt.Shell (getBrowser)
|
||||
import XMonad.Util.Run (safeSpawn)
|
||||
import XMonad.Util.XSelection (getSelection)
|
||||
@@ -79,13 +90,15 @@ import XMonad.Util.XSelection (getSelection)
|
||||
|
||||
* 'amazon' -- Amazon keyword search.
|
||||
|
||||
* 'alpha' -- Wolfram|Alpha query.
|
||||
|
||||
* 'codesearch' -- Google Labs Code Search search.
|
||||
|
||||
* 'deb' -- Debian package search.
|
||||
|
||||
* 'debbts' -- Debian Bug Tracking System.
|
||||
|
||||
* 'debpts -- Debian Package Tracking System.
|
||||
* 'debpts' -- Debian Package Tracking System.
|
||||
|
||||
* 'dictionary' -- dictionary.reference.com search.
|
||||
|
||||
@@ -101,6 +114,8 @@ import XMonad.Util.XSelection (getSelection)
|
||||
|
||||
* 'isohunt' -- isoHunt search.
|
||||
|
||||
* 'lucky' -- Google "I'm feeling lucky" search.
|
||||
|
||||
* 'maps' -- Google maps.
|
||||
|
||||
* 'mathworld' -- Wolfram MathWorld search.
|
||||
@@ -115,6 +130,8 @@ import XMonad.Util.XSelection (getSelection)
|
||||
|
||||
* 'youtube' -- Youtube video search.
|
||||
|
||||
* 'multi' -- Search based on the prefix. \"amazon:Potter\" will use amazon, etc. With no prefix searches google.
|
||||
|
||||
Feel free to add more! -}
|
||||
|
||||
{- $tip
|
||||
@@ -153,7 +170,7 @@ Or in combination with XMonad.Util.EZConfig:
|
||||
>
|
||||
> ...
|
||||
>
|
||||
> searchList :: [([Char], S.SearchEngine)]
|
||||
> searchList :: [(String, S.SearchEngine)]
|
||||
> searchList = [ ("g", S.google)
|
||||
> , ("h", S.hoohle)
|
||||
> , ("w", S.wikipedia)
|
||||
@@ -205,14 +222,21 @@ escape = escapeURIString (\c -> isAlpha c || isDigit c || isMark c)
|
||||
|
||||
type Browser = FilePath
|
||||
type Query = String
|
||||
type Site = String
|
||||
type Site = String -> String
|
||||
type Name = String
|
||||
data SearchEngine = SearchEngine Name Site
|
||||
|
||||
-- | Given a browser, a search engine, and a search term, perform the
|
||||
-- | Given an already defined search engine, extracts its transformation
|
||||
-- function, making it easy to create compound search engines.
|
||||
-- For an instance you can use @use google@ to get a function which
|
||||
-- makes the same transformation as the google search engine would.
|
||||
use :: SearchEngine -> Site
|
||||
use (SearchEngine _ engine) = engine
|
||||
|
||||
-- | Given a browser, a search engine's transformation function, and a search term, perform the
|
||||
-- requested search in the browser.
|
||||
search :: Browser -> Site -> Query -> X ()
|
||||
search browser site query = safeSpawn browser $ site ++ escape query
|
||||
search browser site query = safeSpawn browser [site query]
|
||||
|
||||
{- | Given a base URL, create the 'SearchEngine' that escapes the query and
|
||||
appends it to the base. You can easily define a new engine locally using
|
||||
@@ -222,45 +246,113 @@ search browser site query = safeSpawn browser $ site ++ escape query
|
||||
|
||||
The important thing is that the site has a interface which accepts the escaped query
|
||||
string as part of the URL. Alas, the exact URL to feed searchEngine varies
|
||||
from site to site, often considerably, so there's no general way to cover this.
|
||||
from site to site, often considerably, so there\'s no general way to cover this.
|
||||
|
||||
Generally, examining the resultant URL of a search will allow you to reverse-engineer
|
||||
it if you can't find the necessary URL already described in other projects such as Surfraw. -}
|
||||
searchEngine :: Name -> Site -> SearchEngine
|
||||
searchEngine = SearchEngine
|
||||
searchEngine :: Name -> String -> SearchEngine
|
||||
searchEngine name site = searchEngineF name (\s -> site ++ (escape s))
|
||||
|
||||
{- | If your search engine is more complex than this (you may want to identify
|
||||
the kind of input and make the search URL dependent on the input or put the query
|
||||
inside of a URL instead of in the end) you can use the alternative 'searchEngineF' function.
|
||||
|
||||
> searchFunc :: String -> String
|
||||
> searchFunc s | s `isPrefixOf` "wiki:" = "http://en.wikipedia.org/wiki/" ++ (escape $ tail $ snd $ break (==':') s)
|
||||
> | s `isPrefixOf` "http://" = s
|
||||
> | otherwise = (use google) s
|
||||
> myNewEngine = searchEngineF "mymulti" searchFunc
|
||||
|
||||
@searchFunc@ here searches for a word in wikipedia if it has a prefix
|
||||
of \"wiki:\" (you can use the 'escape' function to escape any forbidden characters), opens an address
|
||||
directly if it starts with \"http:\/\/\" and otherwise uses the provided google search engine.
|
||||
You can use other engines inside of your own through the 'use' function as shown above to make
|
||||
complex searches.
|
||||
|
||||
The user input will be automatically escaped in search engines created with 'searchEngine',
|
||||
'searchEngineF', however, completely depends on the transformation function passed to it. -}
|
||||
searchEngineF :: Name -> Site -> SearchEngine
|
||||
searchEngineF = SearchEngine
|
||||
|
||||
-- The engines.
|
||||
amazon, codesearch, deb, debbts, debpts, dictionary, google, hackage, hoogle, images,
|
||||
imdb, isohunt, maps, mathworld, scholar, thesaurus, wayback, wikipedia,
|
||||
amazon, alpha, codesearch, deb, debbts, debpts, dictionary, google, hackage, hoogle,
|
||||
images, imdb, isohunt, lucky, maps, mathworld, scholar, thesaurus, wayback, wikipedia, wiktionary,
|
||||
youtube :: SearchEngine
|
||||
amazon = searchEngine "amazon" "http://www.amazon.com/exec/obidos/external-search?index=all&keyword="
|
||||
alpha = searchEngine "alpha" "http://www.wolframalpha.com/input/?i="
|
||||
codesearch = searchEngine "codesearch" "http://www.google.com/codesearch?q="
|
||||
deb = searchEngine "deb" "http://packages.debian.org/"
|
||||
debbts = searchEngine "debbts" "http://bugs.debian.org/"
|
||||
debpts = searchEngine "debpts" "http://packages.qa.debian.org/"
|
||||
dictionary = searchEngine "dictionary" "http://dictionary.reference.com/browse/"
|
||||
dictionary = searchEngine "dict" "http://dictionary.reference.com/browse/"
|
||||
google = searchEngine "google" "http://www.google.com/search?num=100&q="
|
||||
hackage = searchEngine "hackage" "http://hackage.haskell.org/cgi-bin/hackage-scripts/package/"
|
||||
hackage = searchEngine "hackage" "http://hackage.haskell.org/package/"
|
||||
hoogle = searchEngine "hoogle" "http://www.haskell.org/hoogle/?q="
|
||||
images = searchEngine "images" "http://images.google.fr/images?q="
|
||||
imdb = searchEngine "imdb" "http://www.imdb.com/Find?select=all&for="
|
||||
isohunt = searchEngine "isohunt" "http://isohunt.com/torrents/?ihq="
|
||||
lucky = searchEngine "lucky" "http://www.google.com/search?btnI&q="
|
||||
maps = searchEngine "maps" "http://maps.google.com/maps?q="
|
||||
mathworld = searchEngine "mathworld" "http://mathworld.wolfram.com/search/?query="
|
||||
scholar = searchEngine "scholar" "http://scholar.google.com/scholar?q="
|
||||
thesaurus = searchEngine "thesaurus" "http://thesaurus.reference.com/search?q="
|
||||
wikipedia = searchEngine "wikipedia" "https://secure.wikimedia.org/wikipedia/en/wiki/Special:Search?go=Go&search="
|
||||
wikipedia = searchEngine "wiki" "https://secure.wikimedia.org/wikipedia/en/wiki/Special:Search?go=Go&search="
|
||||
wiktionary = searchEngine "wikt" "http://en.wiktionary.org/wiki/Special:Search?go=Go&search="
|
||||
youtube = searchEngine "youtube" "http://www.youtube.com/results?search_type=search_videos&search_query="
|
||||
{- This doesn't seem to work, but nevertheless, it seems to be the official
|
||||
method at <http://web.archive.org/collections/web/advanced.html> to get the
|
||||
latest backup. -}
|
||||
wayback = searchEngine "wayback" "http://web.archive.org/"
|
||||
|
||||
multi :: SearchEngine
|
||||
multi = namedEngine "multi" $ foldr1 (!>) [amazon, alpha, codesearch, deb, debbts, debpts, dictionary, google, hackage, hoogle, images, imdb, isohunt, lucky, maps, mathworld, scholar, thesaurus, wayback, wikipedia, wiktionary, (prefixAware google)]
|
||||
|
||||
{- | This function wraps up a search engine and creates a new one, which works
|
||||
like the argument, but goes directly to a URL if one is given rather than
|
||||
searching.
|
||||
|
||||
> myIntelligentGoogleEngine = intelligent google
|
||||
|
||||
Now if you search for http:\/\/xmonad.org it will directly open in your browser-}
|
||||
intelligent :: SearchEngine -> SearchEngine
|
||||
intelligent (SearchEngine name site) = searchEngineF name (\s -> if (fst $ break (==':') s) `elem` ["http", "https", "ftp"] then s else (site s))
|
||||
|
||||
-- | > removeColonPrefix "foo://bar" ~> "//bar"
|
||||
-- > removeColonPrefix "foo//bar" ~> "foo//bar"
|
||||
removeColonPrefix :: String -> String
|
||||
removeColonPrefix s = if ':' `elem` s then drop 1 $ dropWhile (':' /=) s else s
|
||||
|
||||
{- | Connects a few search engines into one. If the search engines\' names are
|
||||
\"s1\", \"s2\" and \"s3\", then the resulting engine will use s1 if the query
|
||||
is @s1:word@, s2 if you type @s2:word@ and s3 in all other cases.
|
||||
|
||||
Example:
|
||||
|
||||
> multiEngine = intelligent (wikipedia !> mathworld !> (prefixAware google))
|
||||
|
||||
Now if you type \"wiki:Haskell\" it will search for \"Haskell\" in Wikipedia,
|
||||
\"mathworld:integral\" will search mathworld, and everything else will fall back to
|
||||
google. The use of intelligent will make sure that URLs are opened directly. -}
|
||||
(!>) :: SearchEngine -> SearchEngine -> SearchEngine
|
||||
(SearchEngine name1 site1) !> (SearchEngine name2 site2) = searchEngineF (name1 ++ "/" ++ name2) (\s -> if s `isPrefixOf` (name1++":") then site1 (removeColonPrefix s) else site2 s)
|
||||
|
||||
{- | Makes a search engine prefix-aware. Especially useful together with '!>'.
|
||||
It will automatically remove the prefix from a query so that you don\'t end
|
||||
up searching for google:xmonad if google is your fallback engine and you
|
||||
explicitly add the prefix. -}
|
||||
prefixAware :: SearchEngine -> SearchEngine
|
||||
prefixAware (SearchEngine name site) = SearchEngine name (\s -> if s `isPrefixOf` (name++":") then site $ removeColonPrefix s else site s)
|
||||
|
||||
{- | Changes search engine's name -}
|
||||
namedEngine :: Name -> SearchEngine -> SearchEngine
|
||||
namedEngine name (SearchEngine _ site) = searchEngineF name site
|
||||
|
||||
{- | Like 'search', but for use with the output from a Prompt; it grabs the
|
||||
Prompt's result, passes it to a given searchEngine and opens it in a given
|
||||
browser. -}
|
||||
promptSearchBrowser :: XPConfig -> Browser -> SearchEngine -> X ()
|
||||
promptSearchBrowser config browser (SearchEngine name site) = mkXPrompt (Search name) config historyCompletion $ search browser site
|
||||
promptSearchBrowser config browser (SearchEngine name site) =
|
||||
mkXPrompt (Search name) config (historyCompletionP ("Search [" `isPrefixOf`)) $ search browser site
|
||||
|
||||
{- | Like 'search', but in this case, the string is not specified but grabbed
|
||||
from the user's response to a prompt. Example:
|
||||
|
@@ -29,7 +29,7 @@ import XMonad.Util.Run
|
||||
--
|
||||
-- and add a keybinding, for example:
|
||||
--
|
||||
-- > , ((modMask x, xK_d ), date)
|
||||
-- > , ((modm, xK_d ), date)
|
||||
--
|
||||
-- In this example, a popup date menu will now be bound to @mod-d@.
|
||||
--
|
||||
|
@@ -5,17 +5,20 @@
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Provides a simple binding that pushes all floating windows on the current
|
||||
-- workspace back into tiling.
|
||||
-- Provides a simple binding that pushes all floating windows on the
|
||||
-- current workspace back into tiling. Note that the functionality of
|
||||
-- this module has been folded into the more general
|
||||
-- "XMonad.Actions.WithAll"; this module simply re-exports the
|
||||
-- 'sinkAll' function for backwards compatibility.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.SinkAll (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
|
||||
sinkAll) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.StackSet
|
||||
import XMonad.Actions.WithAll (sinkAll)
|
||||
|
||||
-- $usage
|
||||
--
|
||||
@@ -25,16 +28,7 @@ import XMonad.StackSet
|
||||
--
|
||||
-- then add a keybinding; for example:
|
||||
--
|
||||
-- , ((modMask x .|. shiftMask, xK_t), sinkAll)
|
||||
-- > , ((modm .|. shiftMask, xK_t), sinkAll)
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
||||
-- | Un-float all floating windows on the current workspace.
|
||||
sinkAll :: X ()
|
||||
sinkAll = withAll sink
|
||||
|
||||
-- | Apply a function to all windows on current workspace.
|
||||
withAll :: (Window -> WindowSet -> WindowSet) -> X ()
|
||||
withAll f = windows $ \ws -> let all' = integrate' . stack . workspace . current $ ws
|
||||
in foldr f ws all'
|
||||
|
@@ -8,28 +8,26 @@
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- This module provides helper functions to be used in @manageHook@. Here's
|
||||
-- how you might use this:
|
||||
-- Provides a way to modify a window spawned by a command(e.g shift it to the workspace
|
||||
-- it was launched on) by using the _NET_WM_PID property that most windows set on creation.
|
||||
-- Hence this module won't work on applications that don't set this property.
|
||||
--
|
||||
-- > import XMonad.Hooks.ManageHelpers
|
||||
-- > main = do
|
||||
-- > sp <- mkSpawner
|
||||
-- > xmonad defaultConfig {
|
||||
-- > ...
|
||||
-- > manageHook = spawnHook sp <+> manageHook defaultConfig
|
||||
-- > ...
|
||||
-- > }
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.SpawnOn (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
Spawner,
|
||||
mkSpawner,
|
||||
manageSpawn,
|
||||
spawnHere,
|
||||
spawnOn,
|
||||
spawnAndDo,
|
||||
shellPromptHere,
|
||||
shellPromptOn
|
||||
) where
|
||||
|
||||
import Data.List (isInfixOf)
|
||||
import Data.IORef
|
||||
import System.Posix.Types (ProcessID)
|
||||
|
||||
@@ -40,38 +38,85 @@ import XMonad.Hooks.ManageHelpers
|
||||
import XMonad.Prompt
|
||||
import XMonad.Prompt.Shell
|
||||
|
||||
newtype Spawner = Spawner {pidsRef :: IORef [(ProcessID, WorkspaceId)]}
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Actions.SpawnOn
|
||||
--
|
||||
-- > main = do
|
||||
-- > sp <- mkSpawner
|
||||
-- > xmonad defaultConfig {
|
||||
-- > ...
|
||||
-- > manageHook = manageSpawn sp <+> manageHook defaultConfig
|
||||
-- > ...
|
||||
-- > }
|
||||
--
|
||||
-- To ensure that application appears on a workspace it was launched at, add keybindings like:
|
||||
--
|
||||
-- > , ((mod1Mask,xK_o), spawnHere sp "urxvt")
|
||||
-- > , ((mod1Mask,xK_s), shellPromptHere sp defaultXPConfig)
|
||||
--
|
||||
-- The module can also be used to apply other manage hooks to the window of
|
||||
-- the spawned application(e.g. float or resize it).
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
||||
newtype Spawner = Spawner {pidsRef :: IORef [(ProcessID, ManageHook)]}
|
||||
|
||||
maxPids :: Int
|
||||
maxPids = 5
|
||||
|
||||
-- | Create 'Spawner' which then has to be passed to other functions.
|
||||
mkSpawner :: (Functor m, MonadIO m) => m Spawner
|
||||
mkSpawner = io . fmap Spawner $ newIORef []
|
||||
|
||||
-- | Provides a manage hook to react on process spawned with
|
||||
-- 'spawnOn', 'spawnHere' etc.
|
||||
manageSpawn :: Spawner -> ManageHook
|
||||
manageSpawn sp = do
|
||||
pids <- io . readIORef $ pidsRef sp
|
||||
mp <- pid
|
||||
case flip lookup pids =<< mp of
|
||||
Just w -> doF (W.shift w)
|
||||
Nothing -> doF id
|
||||
Just mh -> do
|
||||
whenJust mp $ \p ->
|
||||
io . modifyIORef (pidsRef sp) $ filter ((/= p) . fst)
|
||||
mh
|
||||
|
||||
mkPrompt :: (String -> X ()) -> XPConfig -> X ()
|
||||
mkPrompt cb c = do
|
||||
cmds <- io $ getCommands
|
||||
mkXPrompt Shell c (getShellCompl cmds) cb
|
||||
|
||||
-- | Replacement for Shell prompt ("XMonad.Prompt.Shell") which launches
|
||||
-- application on current workspace.
|
||||
shellPromptHere :: Spawner -> XPConfig -> X ()
|
||||
shellPromptHere sp = mkPrompt (spawnHere sp)
|
||||
|
||||
-- | Replacement for Shell prompt ("XMonad.Prompt.Shell") which launches
|
||||
-- application on given workspace.
|
||||
shellPromptOn :: Spawner -> WorkspaceId -> XPConfig -> X ()
|
||||
shellPromptOn sp ws = mkPrompt (spawnOn sp ws)
|
||||
|
||||
-- | Replacement for 'spawn' which launches
|
||||
-- application on current workspace.
|
||||
spawnHere :: Spawner -> String -> X ()
|
||||
spawnHere sp cmd = withWindowSet $ \ws -> spawnOn sp (currTag ws) cmd
|
||||
where currTag = W.tag . W.workspace . W.current
|
||||
spawnHere sp cmd = withWindowSet $ \ws -> spawnOn sp (W.currentTag ws) cmd
|
||||
|
||||
-- | Replacement for 'spawn' which launches
|
||||
-- application on given workspace.
|
||||
spawnOn :: Spawner -> WorkspaceId -> String -> X ()
|
||||
spawnOn sp ws cmd = do
|
||||
p <- spawnPID cmd
|
||||
io $ modifyIORef (pidsRef sp) (take maxPids . ((p, ws) :))
|
||||
spawnOn sp ws cmd = spawnAndDo sp (doShift ws) cmd
|
||||
|
||||
-- | Spawn an application and apply the manage hook when it opens.
|
||||
spawnAndDo :: Spawner -> ManageHook -> String -> X ()
|
||||
spawnAndDo sp mh cmd = do
|
||||
p <- spawnPID $ mangle cmd
|
||||
io $ modifyIORef (pidsRef sp) (take maxPids . ((p,mh) :))
|
||||
where
|
||||
-- TODO this is silly, search for a better solution
|
||||
mangle xs | any (`elem` metaChars) xs || "exec" `isInfixOf` xs = xs
|
||||
| otherwise = "exec " ++ xs
|
||||
metaChars = "&|;"
|
||||
|
||||
|
@@ -15,9 +15,10 @@
|
||||
module XMonad.Actions.Submap (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
submap
|
||||
submap,
|
||||
submapDefault
|
||||
) where
|
||||
|
||||
import Data.Bits
|
||||
import XMonad hiding (keys)
|
||||
import qualified Data.Map as M
|
||||
import Control.Monad.Fix (fix)
|
||||
@@ -33,7 +34,7 @@ First, import this module into your @~\/.xmonad\/xmonad.hs@:
|
||||
|
||||
Allows you to create a sub-mapping of keys. Example:
|
||||
|
||||
> , ((modMask x, xK_a), submap . M.fromList $
|
||||
> , ((modm, xK_a), submap . M.fromList $
|
||||
> [ ((0, xK_n), spawn "mpc next")
|
||||
> , ((0, xK_p), spawn "mpc prev")
|
||||
> , ((0, xK_z), spawn "mpc random")
|
||||
@@ -57,7 +58,11 @@ For detailed instructions on editing your key bindings, see
|
||||
-- corresponding action, or does nothing if the key is not found in
|
||||
-- the map.
|
||||
submap :: M.Map (KeyMask, KeySym) (X ()) -> X ()
|
||||
submap keys = do
|
||||
submap keys = submapDefault (return ()) keys
|
||||
|
||||
-- | Like 'submap', but executes a default action if the key did not match.
|
||||
submapDefault :: X () -> M.Map (KeyMask, KeySym) (X ()) -> X ()
|
||||
submapDefault def keys = do
|
||||
XConf { theRoot = root, display = d } <- ask
|
||||
|
||||
io $ grabKeyboard d root False grabModeAsync grabModeAsync currentTime
|
||||
@@ -69,8 +74,8 @@ submap keys = do
|
||||
if isModifierKey keysym
|
||||
then nextkey
|
||||
else return (m, keysym)
|
||||
-- Remove num lock mask and Xkb group state bits
|
||||
m' <- cleanMask $ m .&. ((1 `shiftL` 12) - 1)
|
||||
maybe def id (M.lookup (m', s) keys)
|
||||
|
||||
io $ ungrabKeyboard d currentTime
|
||||
|
||||
m' <- cleanMask m
|
||||
whenJust (M.lookup (m', s) keys) id
|
||||
|
@@ -19,12 +19,13 @@ module XMonad.Actions.SwapWorkspaces (
|
||||
swapWithCurrent,
|
||||
swapTo,
|
||||
swapWorkspaces,
|
||||
WSDirection(..)
|
||||
Direction1D(..)
|
||||
) where
|
||||
|
||||
import XMonad (windows, X())
|
||||
import XMonad.StackSet
|
||||
import XMonad.Actions.CycleWS
|
||||
import XMonad.Util.Types
|
||||
import XMonad.Util.WorkspaceCompare
|
||||
|
||||
|
||||
@@ -36,7 +37,7 @@ import XMonad.Util.WorkspaceCompare
|
||||
-- Then throw something like this in your keys definition:
|
||||
--
|
||||
-- > ++
|
||||
-- > [((modMask x .|. controlMask, k), windows $ swapWithCurrent i)
|
||||
-- > [((modm .|. controlMask, k), windows $ swapWithCurrent i)
|
||||
-- > | (i, k) <- zip workspaces [xK_1 ..]]
|
||||
--
|
||||
-- After installing this update, if you're on workspace 1, hitting mod-ctrl-5
|
||||
@@ -52,7 +53,7 @@ swapWithCurrent t s = swapWorkspaces t (currentTag s) s
|
||||
|
||||
-- | Say @swapTo Next@ or @swapTo Prev@ to move your current workspace.
|
||||
-- This is an @X ()@ so can be hooked up to your keybindings directly.
|
||||
swapTo :: WSDirection -> X ()
|
||||
swapTo :: Direction1D -> X ()
|
||||
swapTo dir = findWorkspace getSortByIndex dir AnyWS 1 >>= windows . swapWithCurrent
|
||||
|
||||
-- | Takes two workspace tags and an existing XMonad.StackSet and returns a new
|
||||
|
@@ -42,16 +42,16 @@ import XMonad hiding (workspaces)
|
||||
--
|
||||
-- and add keybindings such as the following:
|
||||
--
|
||||
-- > , ((modMask x, xK_f ), withFocused (addTag "abc"))
|
||||
-- > , ((modMask x .|. controlMask, xK_f ), withFocused (delTag "abc"))
|
||||
-- > , ((modMask x .|. shiftMask, xK_f ), withTaggedGlobal "abc" sink)
|
||||
-- > , ((modMask x, xK_d ), withTaggedP "abc" (shiftWin "2"))
|
||||
-- > , ((modMask x .|. shiftMask, xK_d ), withTaggedGlobalP "abc" shiftHere)
|
||||
-- > , ((modMask x .|. controlMask, xK_d ), focusUpTaggedGlobal "abc")
|
||||
-- > , ((modMask x, xK_g ), tagPrompt defaultXPConfig (\s -> withFocused (addTag s)))
|
||||
-- > , ((modMask x .|. controlMask, xK_g ), tagDelPrompt defaultXPConfig)
|
||||
-- > , ((modMask x .|. shiftMask, xK_g ), tagPrompt defaultXPConfig (\s -> withTaggedGlobal s float))
|
||||
-- > , ((modWinMask, xK_g ), tagPrompt defaultXPConfig (\s -> withTaggedP s (shiftWin "2")))
|
||||
-- > , ((modm, xK_f ), withFocused (addTag "abc"))
|
||||
-- > , ((modm .|. controlMask, xK_f ), withFocused (delTag "abc"))
|
||||
-- > , ((modm .|. shiftMask, xK_f ), withTaggedGlobalP "abc" W.sink)
|
||||
-- > , ((modm, xK_d ), withTaggedP "abc" (W.shiftWin "2"))
|
||||
-- > , ((modm .|. shiftMask, xK_d ), withTaggedGlobalP "abc" shiftHere)
|
||||
-- > , ((modm .|. controlMask, xK_d ), focusUpTaggedGlobal "abc")
|
||||
-- > , ((modm, xK_g ), tagPrompt defaultXPConfig (\s -> withFocused (addTag s)))
|
||||
-- > , ((modm .|. controlMask, xK_g ), tagDelPrompt defaultXPConfig)
|
||||
-- > , ((modm .|. shiftMask, xK_g ), tagPrompt defaultXPConfig (\s -> withTaggedGlobal s float))
|
||||
-- > , ((modWinMask, xK_g ), tagPrompt defaultXPConfig (\s -> withTaggedP s (W.shiftWin "2")))
|
||||
-- > , ((modWinMask .|. shiftMask, xK_g ), tagPrompt defaultXPConfig (\s -> withTaggedGlobalP s shiftHere))
|
||||
-- > , ((modWinMask .|. controlMask, xK_g ), tagPrompt defaultXPConfig (\s -> focusUpTaggedGlobal s))
|
||||
--
|
||||
|
317
XMonad/Actions/TopicSpace.hs
Normal file
317
XMonad/Actions/TopicSpace.hs
Normal file
@@ -0,0 +1,317 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.TopicSpace
|
||||
-- Copyright : (c) Nicolas Pouillard
|
||||
-- License : BSD-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Nicolas Pouillard <nicolas.pouillard@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Turns your workspaces into a more topic oriented system.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.TopicSpace
|
||||
(
|
||||
-- * Overview
|
||||
-- $overview
|
||||
|
||||
-- * Usage
|
||||
-- $usage
|
||||
Topic
|
||||
, Dir
|
||||
, TopicConfig(..)
|
||||
, getLastFocusedTopics
|
||||
, setLastFocusedTopic
|
||||
, pprWindowSet
|
||||
, topicActionWithPrompt
|
||||
, topicAction
|
||||
, currentTopicAction
|
||||
, switchTopic
|
||||
, switchNthLastFocused
|
||||
, shiftNthLastFocused
|
||||
, currentTopicDir
|
||||
, checkTopicConfig
|
||||
, (>*>)
|
||||
)
|
||||
where
|
||||
|
||||
import XMonad
|
||||
|
||||
import Data.List
|
||||
import Data.Maybe (fromMaybe, isNothing, listToMaybe)
|
||||
import Data.Ord
|
||||
import qualified Data.Map as M
|
||||
import Control.Monad ((=<<),liftM2,when,unless,replicateM_)
|
||||
import System.IO
|
||||
|
||||
import XMonad.Operations
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import XMonad.Prompt
|
||||
import XMonad.Prompt.Workspace
|
||||
|
||||
import XMonad.Hooks.UrgencyHook
|
||||
import XMonad.Hooks.DynamicLog (PP(..))
|
||||
import qualified XMonad.Hooks.DynamicLog as DL
|
||||
|
||||
import XMonad.Util.Run (spawnPipe)
|
||||
import XMonad.Util.StringProp(getStringListProp,setStringListProp)
|
||||
|
||||
-- $overview
|
||||
-- This module allows to organize your workspaces on a precise topic basis. So
|
||||
-- instead of having a workspace called `work' you can setup one workspace per
|
||||
-- task. Here we call these workspaces, topics. The great thing with
|
||||
-- topics is that one can attach a directory that makes sense to each
|
||||
-- particular topic. One can also attach an action which will be triggered
|
||||
-- when switching to a topic that does not have any windows in it. So you can
|
||||
-- attach your mail client to the mail topic, some terminals in the right
|
||||
-- directory to the xmonad topic... This package also provides a nice way to
|
||||
-- display your topics in an historical way using a custom `pprWindowSet'
|
||||
-- function. You can also easily switch to recent topics using this history
|
||||
-- of last focused topics.
|
||||
|
||||
-- $usage
|
||||
-- Here is an example of configuration using TopicSpace:
|
||||
--
|
||||
-- @
|
||||
-- -- The list of all topics/workspaces of your xmonad configuration.
|
||||
-- -- The order is important, new topics must be inserted
|
||||
-- -- at the end of the list if you want hot-restarting
|
||||
-- -- to work.
|
||||
-- myTopics :: [Topic]
|
||||
-- myTopics =
|
||||
-- [ \"dashboard\" -- the first one
|
||||
-- , \"admin\", \"build\", \"cleaning\", \"conf\", \"darcs\", \"haskell\", \"irc\"
|
||||
-- , \"mail\", \"movie\", \"music\", \"talk\", \"text\", \"tools\", \"web\", \"xmonad\"
|
||||
-- , \"yi\", \"documents\", \"twitter\", \"pdf\"
|
||||
-- ]
|
||||
-- @
|
||||
--
|
||||
-- @
|
||||
-- myTopicConfig :: TopicConfig
|
||||
-- myTopicConfig = TopicConfig
|
||||
-- { topicDirs = M.fromList $
|
||||
-- [ (\"conf\", \"w\/conf\")
|
||||
-- , (\"dashboard\", \"Desktop\")
|
||||
-- , (\"yi\", \"w\/dev-haskell\/yi\")
|
||||
-- , (\"darcs\", \"w\/dev-haskell\/darcs\")
|
||||
-- , (\"haskell\", \"w\/dev-haskell\")
|
||||
-- , (\"xmonad\", \"w\/dev-haskell\/xmonad\")
|
||||
-- , (\"tools\", \"w\/tools\")
|
||||
-- , (\"movie\", \"Movies\")
|
||||
-- , (\"talk\", \"w\/talks\")
|
||||
-- , (\"music\", \"Music\")
|
||||
-- , (\"documents\", \"w\/documents\")
|
||||
-- , (\"pdf\", \"w\/documents\")
|
||||
-- ]
|
||||
-- , defaultTopicAction = const $ spawnShell >*> 3
|
||||
-- , defaultTopic = \"dashboard\"
|
||||
-- , maxTopicHistory = 10
|
||||
-- , topicActions = M.fromList $
|
||||
-- [ (\"conf\", spawnShell >> spawnShellIn \"wd\/ertai\/private\")
|
||||
-- , (\"darcs\", spawnShell >*> 3)
|
||||
-- , (\"yi\", spawnShell >*> 3)
|
||||
-- , (\"haskell\", spawnShell >*> 2 >>
|
||||
-- spawnShellIn \"wd\/dev-haskell\/ghc\")
|
||||
-- , (\"xmonad\", spawnShellIn \"wd\/x11-wm\/xmonad\" >>
|
||||
-- spawnShellIn \"wd\/x11-wm\/xmonad\/contrib\" >>
|
||||
-- spawnShellIn \"wd\/x11-wm\/xmonad\/utils\" >>
|
||||
-- spawnShellIn \".xmonad\" >>
|
||||
-- spawnShellIn \".xmonad\")
|
||||
-- , (\"mail\", mailAction)
|
||||
-- , (\"irc\", ssh somewhere)
|
||||
-- , (\"admin\", ssh somewhere >>
|
||||
-- ssh nowhere)
|
||||
-- , (\"dashboard\", spawnShell)
|
||||
-- , (\"twitter\", spawnShell)
|
||||
-- , (\"web\", spawn browserCmd)
|
||||
-- , (\"movie\", spawnShell)
|
||||
-- , (\"documents\", spawnShell >*> 2 >>
|
||||
-- spawnShellIn \"Documents\" >*> 2)
|
||||
-- , (\"pdf\", spawn pdfViewerCmd)
|
||||
-- ]
|
||||
-- }
|
||||
-- @
|
||||
--
|
||||
-- @
|
||||
-- -- extend your keybindings
|
||||
-- myKeys conf\@XConfig{modMask=modm} =
|
||||
-- [ ((modm , xK_n ), spawnShell) -- %! Launch terminal
|
||||
-- , ((modm , xK_a ), currentTopicAction myTopicConfig)
|
||||
-- , ((modm , xK_g ), promptedGoto)
|
||||
-- , ((modm .|. shiftMask, xK_g ), promptedShift)
|
||||
-- ...
|
||||
-- ]
|
||||
-- ++
|
||||
-- [ ((modm, k), switchNthLastFocused myTopicConfig i)
|
||||
-- | (i, k) <- zip [1..] workspaceKeys]
|
||||
-- @
|
||||
--
|
||||
-- @
|
||||
-- spawnShell :: X ()
|
||||
-- spawnShell = currentTopicDir myTopicConfig >>= spawnShellIn
|
||||
-- @
|
||||
--
|
||||
-- @
|
||||
-- spawnShellIn :: Dir -> X ()
|
||||
-- spawnShellIn dir = spawn $ \"urxvt '(cd ''\" ++ dir ++ \"'' && \" ++ myShell ++ \" )'\"
|
||||
-- @
|
||||
--
|
||||
-- @
|
||||
-- goto :: Topic -> X ()
|
||||
-- goto = switchTopic myTopicConfig
|
||||
-- @
|
||||
--
|
||||
-- @
|
||||
-- promptedGoto :: X ()
|
||||
-- promptedGoto = workspacePrompt myXPConfig goto
|
||||
-- @
|
||||
--
|
||||
-- @
|
||||
-- promptedShift :: X ()
|
||||
-- promptedShift = workspacePrompt myXPConfig $ windows . W.shift
|
||||
-- @
|
||||
--
|
||||
-- @
|
||||
-- myConfig = do
|
||||
-- checkTopicConfig myTopics myTopicConfig
|
||||
-- myLogHook <- makeMyLogHook
|
||||
-- return $ defaultConfig
|
||||
-- { borderWidth = 1 -- Width of the window border in pixels.
|
||||
-- , workspaces = myTopics
|
||||
-- , layoutHook = myModifiers myLayout
|
||||
-- , manageHook = myManageHook
|
||||
-- , logHook = myLogHook
|
||||
-- , handleEventHook = myHandleEventHook
|
||||
-- , terminal = myTerminal -- The preferred terminal program.
|
||||
-- , normalBorderColor = \"#3f3c6d\"
|
||||
-- , focusedBorderColor = \"#4f66ff\"
|
||||
-- , XMonad.modMask = mod1Mask
|
||||
-- , keys = myKeys
|
||||
-- , mouseBindings = myMouseBindings
|
||||
-- }
|
||||
-- @
|
||||
--
|
||||
-- @
|
||||
-- main :: IO ()
|
||||
-- main = xmonad =<< myConfig
|
||||
-- @
|
||||
|
||||
-- | An alias for @flip replicateM_@
|
||||
(>*>) :: Monad m => m a -> Int -> m ()
|
||||
(>*>) = flip replicateM_
|
||||
infix >*>
|
||||
|
||||
-- | 'Topic' is just an alias for 'WorkspaceId'
|
||||
type Topic = WorkspaceId
|
||||
|
||||
-- | 'Dir' is just an alias for 'FilePath' but should points to a directory.
|
||||
type Dir = FilePath
|
||||
|
||||
-- | Here is the topic space configuration area.
|
||||
data TopicConfig = TopicConfig { topicDirs :: M.Map Topic Dir
|
||||
-- ^ This mapping associate a directory to each topic.
|
||||
, topicActions :: M.Map Topic (X ())
|
||||
-- ^ This mapping associate an action to trigger when
|
||||
-- switching to a given topic which workspace is empty.
|
||||
, defaultTopicAction :: Topic -> X ()
|
||||
-- ^ This is the default topic action.
|
||||
, defaultTopic :: Topic
|
||||
-- ^ This is the default topic.
|
||||
, maxTopicHistory :: Int
|
||||
-- ^ This setups the maximum depth of topic history, usually
|
||||
-- 10 is a good default since we can bind all of them using
|
||||
-- numeric keypad.
|
||||
}
|
||||
|
||||
-- | Returns the list of last focused workspaces the empty list otherwise.
|
||||
-- This function rely on a reserved property namely _XMONAD_LAST_FOCUSED_WORKSPACES.
|
||||
getLastFocusedTopics :: X [String]
|
||||
getLastFocusedTopics = asks display >>= flip getStringListProp "_XMONAD_LAST_FOCUSED_WORKSPACES"
|
||||
|
||||
-- | Given a 'TopicConfig', the last focused topic, and a predicate that will
|
||||
-- select topics that one want to keep, this function will set the property
|
||||
-- of last focused topics.
|
||||
setLastFocusedTopic :: TopicConfig -> Topic -> (Topic -> Bool) -> X ()
|
||||
setLastFocusedTopic tg w predicate = do
|
||||
disp <- asks display
|
||||
setStringListProp disp "_XMONAD_LAST_FOCUSED_WORKSPACES"
|
||||
. take (maxTopicHistory tg) . nub . (w:) . filter predicate =<< getLastFocusedTopics
|
||||
|
||||
-- | This function is a variant of 'DL.pprWindowSet' which takes a topic configuration
|
||||
-- and a pretty-printing record 'PP'. It will show the list of topics sorted historically
|
||||
-- and highlighting topics with urgent windows.
|
||||
pprWindowSet :: TopicConfig -> PP -> X String
|
||||
pprWindowSet tg pp = do
|
||||
winset <- gets windowset
|
||||
urgents <- readUrgents
|
||||
let empty_workspaces = map W.tag $ filter (isNothing . W.stack) $ W.workspaces winset
|
||||
maxDepth = maxTopicHistory tg
|
||||
setLastFocusedTopic tg (W.tag . W.workspace . W.current $ winset)
|
||||
(`notElem` empty_workspaces)
|
||||
lastWs <- getLastFocusedTopics
|
||||
let depth topic = elemIndex topic lastWs
|
||||
add_depth proj topic = proj pp $ maybe topic (((topic++":")++) . show) $ depth topic
|
||||
pp' = pp { ppHidden = add_depth ppHidden, ppVisible = add_depth ppVisible }
|
||||
sortWindows = take (maxDepth - 1) . sortBy (comparing $ fromMaybe maxDepth . depth . W.tag)
|
||||
return $ DL.pprWindowSet sortWindows urgents pp' winset
|
||||
|
||||
-- | Given a prompt configuration and a topic configuration, triggers the action associated with
|
||||
-- the topic given in prompt.
|
||||
topicActionWithPrompt :: XPConfig -> TopicConfig -> X ()
|
||||
topicActionWithPrompt xp tg = workspacePrompt xp (liftM2 (>>) (switchTopic tg) (topicAction tg))
|
||||
|
||||
-- | Given a configuration and a topic, triggers the action associated with the given topic.
|
||||
topicAction :: TopicConfig -> Topic -> X ()
|
||||
topicAction tg topic = fromMaybe (defaultTopicAction tg topic) $ M.lookup topic $ topicActions tg
|
||||
|
||||
-- | Trigger the action associated with the current topic.
|
||||
currentTopicAction :: TopicConfig -> X ()
|
||||
currentTopicAction tg = topicAction tg =<< gets (W.tag . W.workspace . W.current . windowset)
|
||||
|
||||
-- | Switch to the given topic.
|
||||
switchTopic :: TopicConfig -> Topic -> X ()
|
||||
switchTopic tg topic = do
|
||||
windows $ W.greedyView topic
|
||||
wins <- gets (W.integrate' . W.stack . W.workspace . W.current . windowset)
|
||||
when (null wins) $ topicAction tg topic
|
||||
|
||||
-- | Switch to the Nth last focused topic or failback to the 'defaultTopic'.
|
||||
switchNthLastFocused ::TopicConfig -> Int -> X ()
|
||||
switchNthLastFocused tg depth = do
|
||||
lastWs <- getLastFocusedTopics
|
||||
switchTopic tg $ (lastWs ++ repeat (defaultTopic tg)) !! depth
|
||||
|
||||
-- | Shift the focused window to the Nth last focused topic, or fallback to doing nothing.
|
||||
shiftNthLastFocused :: Int -> X ()
|
||||
shiftNthLastFocused n = do
|
||||
ws <- fmap (listToMaybe . drop n) getLastFocusedTopics
|
||||
whenJust ws $ windows . W.shift
|
||||
|
||||
-- | Returns the directory associated with current topic returns the empty string otherwise.
|
||||
currentTopicDir :: TopicConfig -> X String
|
||||
currentTopicDir tg = do
|
||||
topic <- gets (W.tag . W.workspace . W.current . windowset)
|
||||
return . fromMaybe "" . M.lookup topic $ topicDirs tg
|
||||
|
||||
-- | Check the given topic configuration for duplicates topics or undefined topics.
|
||||
checkTopicConfig :: [Topic] -> TopicConfig -> IO ()
|
||||
checkTopicConfig tags tg = do
|
||||
-- tags <- gets $ map W.tag . workspaces . windowset
|
||||
|
||||
let
|
||||
seenTopics = nub $ sort $ M.keys (topicDirs tg) ++ M.keys (topicActions tg)
|
||||
dups = tags \\ nub tags
|
||||
diffTopic = seenTopics \\ sort tags
|
||||
check lst msg = unless (null lst) $ xmessage $ msg ++ " (tags): " ++ show lst
|
||||
|
||||
check diffTopic "Seen but missing topics/workspaces"
|
||||
check dups "Duplicate topics/workspaces"
|
||||
|
||||
-- | Display the given message using the @xmessage@ program.
|
||||
xmessage :: String -> IO ()
|
||||
xmessage s = do
|
||||
h <- spawnPipe "xmessage -file -"
|
||||
hPutStr h s
|
||||
hClose h
|
61
XMonad/Actions/UpdateFocus.hs
Normal file
61
XMonad/Actions/UpdateFocus.hs
Normal file
@@ -0,0 +1,61 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.UpdateFocus
|
||||
-- Copyright : (c) Daniel Schoepe
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Daniel Schoepe <asgaroth_@gmx.de>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Updates the focus on mouse move in unfocused windows.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.UpdateFocus (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
focusOnMouseMove,
|
||||
adjustEventInput
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
import Graphics.X11.Xlib.Extras
|
||||
import Control.Monad (when)
|
||||
import Data.Monoid
|
||||
|
||||
-- $usage
|
||||
-- To make the focus update on mouse movement within an unfocused window, add the
|
||||
-- following to your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Actions.UpdateFocus
|
||||
-- > xmonad $ defaultConfig {
|
||||
-- > ..
|
||||
-- > startupHook = adjustEventInput
|
||||
-- > handleEventHook = focusOnMouseMove
|
||||
-- > ..
|
||||
-- > }
|
||||
--
|
||||
-- This module is probably only useful when focusFollowsMouse is set to True(default).
|
||||
|
||||
-- | Changes the focus if the mouse is moved within an unfocused window.
|
||||
focusOnMouseMove :: Event -> X All
|
||||
focusOnMouseMove (MotionEvent { ev_x = x, ev_y = y, ev_window = root }) = do
|
||||
-- check only every 15 px to avoid excessive calls to translateCoordinates
|
||||
when (x `mod` 15 == 0 || y `mod` 15 == 0) $ do
|
||||
dpy <- asks display
|
||||
Just foc <- withWindowSet $ return . W.peek
|
||||
-- get the window under the pointer:
|
||||
(_,_,_,w) <- io $ translateCoordinates dpy root root (fromIntegral x) (fromIntegral y)
|
||||
when (foc /= w) $ focus w
|
||||
return (All True)
|
||||
focusOnMouseMove _ = return (All True)
|
||||
|
||||
-- | Adjusts the event mask to pick up pointer movements.
|
||||
adjustEventInput :: X ()
|
||||
adjustEventInput = withDisplay $ \dpy -> do
|
||||
rootw <- asks theRoot
|
||||
io $ selectInput dpy rootw $ substructureRedirectMask .|. substructureNotifyMask
|
||||
.|. enterWindowMask .|. leaveWindowMask .|. structureNotifyMask
|
||||
.|. buttonPressMask .|. pointerMotionMask
|
@@ -3,7 +3,7 @@
|
||||
-- Module : XMonadContrib.UpdatePointer
|
||||
-- Copyright : (c) Robert Marlow <robreim@bobturf.org>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
--
|
||||
-- Maintainer : Robert Marlow <robreim@bobturf.org>
|
||||
-- Stability : stable
|
||||
-- Portability : portable
|
||||
@@ -14,7 +14,7 @@
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.UpdatePointer
|
||||
module XMonad.Actions.UpdatePointer
|
||||
(
|
||||
-- * Usage
|
||||
-- $usage
|
||||
@@ -25,7 +25,8 @@ module XMonad.Actions.UpdatePointer
|
||||
|
||||
import XMonad
|
||||
import Control.Monad
|
||||
import XMonad.StackSet (member)
|
||||
import XMonad.StackSet (member, peek, screenDetail, current)
|
||||
import Data.Maybe
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
@@ -34,9 +35,9 @@ import XMonad.StackSet (member)
|
||||
-- > 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)
|
||||
@@ -50,43 +51,55 @@ import XMonad.StackSet (member)
|
||||
--
|
||||
-- which moves the pointer to the bottom-right corner of the focused window.
|
||||
|
||||
data PointerPosition = Nearest | Relative Rational Rational
|
||||
data PointerPosition = Nearest | Relative Rational Rational | TowardsCentre Rational Rational
|
||||
deriving (Read,Show)
|
||||
|
||||
-- | Update the pointer's location to the currently focused
|
||||
-- window 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
|
||||
updatePointer :: PointerPosition -> X ()
|
||||
updatePointer p = withFocused $ \w -> do
|
||||
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
|
||||
wa <- io $ getWindowAttributes dpy w
|
||||
(_sameRoot,_,currentWindow,rootx,rooty,_,_,_) <- io $ queryPointer dpy root
|
||||
unless (pointWithinRegion rootx rooty (wa_x wa) (wa_y wa) (wa_width wa) (wa_height wa)
|
||||
drag <- gets dragging
|
||||
unless (pointWithin (fi rootx) (fi rooty) rect
|
||||
|| mouseIsMoving
|
||||
|| not (currentWindow `member` ws)) $
|
||||
|| isJust drag
|
||||
|| not (currentWindow `member` ws || currentWindow == none)) $
|
||||
case p of
|
||||
Nearest -> do
|
||||
let x = moveWithin rootx (wa_x wa) ((wa_x wa) + (wa_width wa))
|
||||
let y = moveWithin rooty (wa_y wa) ((wa_y wa) + (wa_height wa))
|
||||
io $ warpPointer dpy none root 0 0 0 0 (fromIntegral x) (fromIntegral y)
|
||||
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 w 0 0 0 0
|
||||
(fraction h (wa_width wa)) (fraction v (wa_height wa))
|
||||
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)
|
||||
|
||||
moveWithin :: Integral a => a -> a -> a -> a
|
||||
moveWithin current lower upper =
|
||||
if current < lower
|
||||
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 current > upper
|
||||
else if now > upper
|
||||
then upper
|
||||
else current
|
||||
else now
|
||||
|
||||
-- Test that a point resides within a region.
|
||||
-- This belongs somewhere more generally accessible than this module.
|
||||
pointWithinRegion :: Integral a => a -> a -> a -> a -> a -> a -> Bool
|
||||
pointWithinRegion px py rx ry rw rh =
|
||||
within px rx (rx + rw) && within py ry (ry + rh)
|
||||
where within x left right = x >= left && x <= right
|
||||
fi :: (Num b, Integral a) => a -> b
|
||||
fi = fromIntegral
|
||||
|
@@ -34,11 +34,11 @@ You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
|
||||
then add appropriate keybindings to warp the pointer; for example:
|
||||
|
||||
> , ((modMask x, xK_z ), warpToWindow (1%2) (1%2)) -- @@ Move pointer to currently focused window
|
||||
> , ((modm, xK_z ), warpToWindow (1%2) (1%2)) -- @@ Move pointer to currently focused window
|
||||
>
|
||||
>-- mod-ctrl-{w,e,r} @@ Move mouse pointer to screen 1, 2, or 3
|
||||
>
|
||||
> [((modMask x .|. controlMask, key), warpToScreen sc (1%2) (1%2))
|
||||
> [((modm .|. controlMask, key), warpToScreen sc (1%2) (1%2))
|
||||
> | (key, sc) <- zip [xK_w, xK_e, xK_r] [0..]]
|
||||
|
||||
Note that warping to a particular screen may change the focus.
|
||||
|
@@ -5,7 +5,7 @@
|
||||
-- License : BSD-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Devin Mullins <me@twifkak.com>
|
||||
-- Stability : unstable
|
||||
-- Stability : stable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- dmenu operations to bring windows to you, and bring you to windows.
|
||||
@@ -15,11 +15,11 @@
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.WindowBringer (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
gotoMenu, gotoMenu', bringMenu, windowMap,
|
||||
bringWindow
|
||||
) where
|
||||
-- * Usage
|
||||
-- $usage
|
||||
gotoMenu, gotoMenu', bringMenu, windowMap,
|
||||
bringWindow
|
||||
) where
|
||||
|
||||
import Data.Char (toLower)
|
||||
import qualified Data.Map as M
|
||||
@@ -38,8 +38,8 @@ import XMonad.Util.NamedWindows (getName)
|
||||
--
|
||||
-- and define appropriate key bindings:
|
||||
--
|
||||
-- > , ((modMask x .|. shiftMask, xK_g ), gotoMenu)
|
||||
-- > , ((modMask x .|. shiftMask, xK_b ), bringMenu)
|
||||
-- > , ((modm .|. shiftMask, xK_g ), gotoMenu)
|
||||
-- > , ((modm .|. shiftMask, xK_b ), bringMenu)
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
@@ -28,18 +28,23 @@ module XMonad.Actions.WindowGo (
|
||||
runOrRaiseMaster,
|
||||
raiseAndDo,
|
||||
raiseMaster,
|
||||
|
||||
ifWindows,
|
||||
ifWindow,
|
||||
raiseHook,
|
||||
module XMonad.ManageHook
|
||||
) where
|
||||
|
||||
import Control.Monad (filterM)
|
||||
import Control.Monad
|
||||
import Data.Char (toLower)
|
||||
|
||||
import XMonad (Query(), X(), withWindowSet, spawn, runQuery, liftIO)
|
||||
import Data.Monoid
|
||||
import XMonad (Query(), X(), ManageHook, withWindowSet, runQuery, liftIO, ask)
|
||||
import Graphics.X11 (Window)
|
||||
import XMonad.ManageHook
|
||||
import XMonad.Operations (windows)
|
||||
import XMonad.Prompt.Shell (getBrowser, getEditor)
|
||||
import qualified XMonad.StackSet as W (allWindows, peek, swapMaster, focusWindow)
|
||||
import XMonad.Util.Run (safeSpawnProg)
|
||||
{- $usage
|
||||
|
||||
Import the module into your @~\/.xmonad\/xmonad.hs@:
|
||||
@@ -48,8 +53,8 @@ Import the module into your @~\/.xmonad\/xmonad.hs@:
|
||||
|
||||
and define appropriate key bindings:
|
||||
|
||||
> , ((modMask x .|. shiftMask, xK_g), raise (className =? "Firefox"))
|
||||
> , ((modMask x .|. shiftMask, xK_b), runOrRaise "firefox" (className =? "Firefox"))
|
||||
> , ((modm .|. shiftMask, xK_g), raise (className =? "Firefox"))
|
||||
> , ((modm .|. shiftMask, xK_b), runOrRaise "firefox" (className =? "Firefox"))
|
||||
|
||||
(Note that Firefox v3 and up have a class-name of \"Firefox\" and \"Navigator\";
|
||||
lower versions use other classnames such as \"Firefox-bin\". Either choose the
|
||||
@@ -59,20 +64,35 @@ appropriate one, or cover your bases by using instead something like
|
||||
For detailed instructions on editing your key bindings, see
|
||||
"XMonad.Doc.Extending#Editing_key_bindings". -}
|
||||
|
||||
-- | 'action' is an executable to be run via 'spawn' (of "XMonad.Core") if the Window cannot be found.
|
||||
-- | If windows that satisfy the query exist, apply the supplied
|
||||
-- function to them, otherwise run the action given as
|
||||
-- second parameter.
|
||||
ifWindows :: Query Bool -> ([Window] -> X ()) -> X () -> X ()
|
||||
ifWindows qry f el = withWindowSet $ \wins -> do
|
||||
matches <- filterM (runQuery qry) $ W.allWindows wins
|
||||
case matches of
|
||||
[] -> el
|
||||
ws -> f ws
|
||||
|
||||
-- | The same as ifWindows, but applies a ManageHook to the first match
|
||||
-- instead and discards the other matches
|
||||
ifWindow :: Query Bool -> ManageHook -> X () -> X ()
|
||||
ifWindow qry mh = ifWindows qry (windows . appEndo <=< runQuery mh . head)
|
||||
|
||||
-- | 'action' is an executable to be run via 'safeSpawnProg' (of "XMonad.Util.Run") if the Window cannot be found.
|
||||
-- Presumably this executable is the same one that you were looking for.
|
||||
runOrRaise :: String -> Query Bool -> X ()
|
||||
runOrRaise = raiseMaybe . spawn
|
||||
runOrRaise = raiseMaybe . safeSpawnProg
|
||||
|
||||
-- | See 'raiseMaybe'. If the Window can't be found, quietly give up and do nothing.
|
||||
raise :: Query Bool -> X ()
|
||||
raise = raiseMaybe $ return ()
|
||||
|
||||
{- | 'raiseMaybe' queries all Windows based on a boolean provided by the
|
||||
user. Currently, there are three such useful booleans defined in
|
||||
"XMonad.ManageHook": title, resource, className. Each one tests based pretty
|
||||
user. Currently, there are 3 such useful booleans defined in
|
||||
"XMonad.ManageHook": 'title', 'resource', 'className'. Each one tests based pretty
|
||||
much as you would think. ManageHook also defines several operators, the most
|
||||
useful of which is (=?). So a useful test might be finding a Window whose
|
||||
useful of which is (=?). So a useful test might be finding a @Window@ whose
|
||||
class is Firefox. Firefox 3 declares the class \"Firefox\", so you'd want to
|
||||
pass in a boolean like @(className =? \"Firefox\")@.
|
||||
|
||||
@@ -95,15 +115,15 @@ raise = raiseMaybe $ return ()
|
||||
> , ((modm, xK_m), raiseMaybe (runInTerm "-title mutt" "mutt") (title =? "mutt"))
|
||||
-}
|
||||
raiseMaybe :: X () -> Query Bool -> X ()
|
||||
raiseMaybe f thatUserQuery = withWindowSet $ \s -> do
|
||||
maybeResult <- filterM (runQuery thatUserQuery) (W.allWindows s)
|
||||
case maybeResult of
|
||||
[] -> f
|
||||
(x:_) -> windows $ W.focusWindow x
|
||||
raiseMaybe f qry = ifWindow qry raiseHook f
|
||||
|
||||
-- | A manage hook that raises the window.
|
||||
raiseHook :: ManageHook
|
||||
raiseHook = ask >>= doF . W.focusWindow
|
||||
|
||||
-- | See 'runOrRaise' and 'raiseNextMaybe'. Version that allows cycling through matches.
|
||||
runOrRaiseNext :: String -> Query Bool -> X ()
|
||||
runOrRaiseNext = raiseNextMaybe . spawn
|
||||
runOrRaiseNext = raiseNextMaybe . safeSpawnProg
|
||||
|
||||
-- | See 'raise' and 'raiseNextMaybe'. Version that allows cycling through matches.
|
||||
raiseNext :: Query Bool -> X ()
|
||||
@@ -115,18 +135,14 @@ raiseNext = raiseNextMaybe $ return ()
|
||||
query the next matching window is raised. If no matches are found
|
||||
the function f is executed.
|
||||
-}
|
||||
|
||||
raiseNextMaybe :: X () -> Query Bool -> X ()
|
||||
raiseNextMaybe f thatUserQuery = withWindowSet $ \s -> do
|
||||
ws <- filterM (runQuery thatUserQuery) (W.allWindows s)
|
||||
case ws of
|
||||
[] -> f
|
||||
(x:_) -> let go (Just w) | (w `elem` ws) = next w $ cycle ws
|
||||
go _ = windows $ W.focusWindow x
|
||||
in go $ W.peek s
|
||||
where
|
||||
next w (x:y:_) | x==w = windows $ W.focusWindow y
|
||||
next w (_:xs) = next w xs
|
||||
next _ _ = error "raiseNextMaybe: empty list"
|
||||
raiseNextMaybe f qry = flip (ifWindows qry) f $ \ws -> do
|
||||
foc <- withWindowSet $ return . W.peek
|
||||
case foc of
|
||||
Just w | w `elem` ws -> let (_:y:_) = dropWhile (/=w) $ cycle ws -- cannot fail to match
|
||||
in windows $ W.focusWindow y
|
||||
_ -> windows . W.focusWindow . head $ ws
|
||||
|
||||
-- | Given a function which gets us a String, we try to raise a window with that classname,
|
||||
-- or we then interpret that String as a executable name.
|
||||
@@ -135,38 +151,34 @@ raiseVar getvar = liftIO getvar >>= \var -> runOrRaise var (fmap (map toLower) c
|
||||
|
||||
{- | 'raiseBrowser' and 'raiseEditor' grab $BROWSER and $EDITOR respectively and they either
|
||||
take you to the specified program's window, or they try to run it. This is most useful
|
||||
if your variables are simple and look like 'firefox' or 'emacs'. -}
|
||||
if your variables are simple and look like \"firefox\" or \"emacs\". -}
|
||||
raiseBrowser, raiseEditor :: X ()
|
||||
raiseBrowser = raiseVar getBrowser
|
||||
raiseEditor = raiseVar getEditor
|
||||
|
||||
{- | if the window is found the window is focused and the third argument is called
|
||||
{- | If the window is found the window is focused and the third argument is called
|
||||
otherwise, the first argument is called
|
||||
See 'raiseMaster' for an example -}
|
||||
See 'raiseMaster' for an example. -}
|
||||
raiseAndDo :: X () -> Query Bool -> (Window -> X ())-> X ()
|
||||
raiseAndDo raisef thatUserQuery afterRaise = withWindowSet $ \s -> do
|
||||
maybeResult <- filterM (runQuery thatUserQuery) (W.allWindows s)
|
||||
case maybeResult of
|
||||
[] -> raisef
|
||||
(x:_) -> do windows $ W.focusWindow x
|
||||
afterRaise x
|
||||
raiseAndDo f qry after = ifWindow qry (afterRaise `mappend` raiseHook) f
|
||||
where afterRaise = ask >>= (>> idHook) . liftX . after
|
||||
|
||||
{- | if the window is found the window is focused and the third argument is called
|
||||
otherwise, raisef is called -}
|
||||
{- | If a window matching the second arugment is found, the window is focused and the third argument is called;
|
||||
otherwise, the first argument is called. -}
|
||||
runOrRaiseAndDo :: String -> Query Bool -> (Window -> X ()) -> X ()
|
||||
runOrRaiseAndDo = raiseAndDo . spawn
|
||||
runOrRaiseAndDo = raiseAndDo . safeSpawnProg
|
||||
|
||||
{- | if the window is found the window is focused and set to master
|
||||
otherwise, the first argument is called
|
||||
otherwise, the first argument is called.
|
||||
|
||||
raiseMaster (runInTerm \"-title ghci\" \"zsh -c \'ghci\'\") (title =? \"ghci\") -}
|
||||
> raiseMaster (runInTerm \"-title ghci\" \"zsh -c \'ghci\'\") (title =? \"ghci\") -}
|
||||
raiseMaster :: X () -> Query Bool -> X ()
|
||||
raiseMaster raisef thatUserQuery = raiseAndDo raisef thatUserQuery (\_ -> windows W.swapMaster)
|
||||
|
||||
{- | if the window is found the window is focused and set to master
|
||||
otherwise, action is run
|
||||
{- | If the window is found the window is focused and set to master
|
||||
otherwise, action is run.
|
||||
|
||||
runOrRaiseMaster \"firefox\" (className =? \"Firefox\"))
|
||||
> runOrRaiseMaster \"firefox\" (className =? \"Firefox\"))
|
||||
-}
|
||||
runOrRaiseMaster :: String -> Query Bool -> X ()
|
||||
runOrRaiseMaster run query = runOrRaiseAndDo run query (\_ -> windows W.swapMaster)
|
||||
|
71
XMonad/Actions/WindowMenu.hs
Normal file
71
XMonad/Actions/WindowMenu.hs
Normal file
@@ -0,0 +1,71 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.WindowMenu
|
||||
-- Copyright : (c) Jan Vornberger 2009
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : jan.vornberger@informatik.uni-oldenburg.de
|
||||
-- Stability : unstable
|
||||
-- Portability : not portable
|
||||
--
|
||||
-- Uses "XMonad.Actions.GridSelect" to display a number of actions related to
|
||||
-- window management in the center of the focused window. Actions include: Closing,
|
||||
-- maximizing, minimizing and shifting the window to another workspace.
|
||||
--
|
||||
-- Note: For maximizing and minimizing to actually work, you will need
|
||||
-- to integrate "XMonad.Layout.Maximize" and "XMonad.Layout.Minimize" into your
|
||||
-- setup. See the documentation of those modules for more information.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.WindowMenu (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
windowMenu
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad.Actions.GridSelect
|
||||
import XMonad.Layout.Maximize
|
||||
import XMonad.Layout.Minimize
|
||||
import XMonad.Util.XUtils (fi)
|
||||
|
||||
-- $usage
|
||||
--
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Actions.WindowMenu
|
||||
--
|
||||
-- Then add a keybinding, e.g.
|
||||
--
|
||||
-- > , ((modm, xK_o ), windowMenu)
|
||||
|
||||
windowMenu :: X ()
|
||||
windowMenu = withFocused $ \w -> do
|
||||
tags <- asks (workspaces . config)
|
||||
Rectangle x y wh ht <- getSize w
|
||||
Rectangle sx sy swh sht <- gets $ screenRect . W.screenDetail . W.current . windowset
|
||||
let originFractX = (fi x - fi sx + fi wh / 2) / fi swh
|
||||
originFractY = (fi y - fi sy + fi ht / 2) / fi sht
|
||||
gsConfig = defaultGSConfig
|
||||
{ gs_originFractX = originFractX
|
||||
, gs_originFractY = originFractY }
|
||||
actions = [ ("Cancel menu", return ())
|
||||
, ("Close" , kill)
|
||||
, ("Maximize" , sendMessage $ maximizeRestore w)
|
||||
, ("Minimize" , sendMessage $ MinimizeWin w)
|
||||
] ++
|
||||
[ ("Move to " ++ tag, windows $ W.shift tag)
|
||||
| tag <- tags ]
|
||||
runSelectedAction gsConfig actions
|
||||
|
||||
getSize :: Window -> X (Rectangle)
|
||||
getSize w = do
|
||||
d <- asks display
|
||||
wa <- io $ getWindowAttributes d w
|
||||
let x = fi $ wa_x wa
|
||||
y = fi $ wa_y wa
|
||||
wh = fi $ wa_width wa
|
||||
ht = fi $ wa_height wa
|
||||
return (Rectangle x y wh ht)
|
@@ -5,6 +5,8 @@
|
||||
-- Devin Mullins <me@twifkak.com>
|
||||
-- Maintainer : Devin Mullins <me@twifkak.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- This is a rewrite of "XMonad.Layout.WindowNavigation". WindowNavigation
|
||||
-- lets you assign keys to move up\/down\/left\/right, based on actual cartesian
|
||||
@@ -34,11 +36,11 @@ module XMonad.Actions.WindowNavigation (
|
||||
withWindowNavigationKeys,
|
||||
WNAction(..),
|
||||
go, swap,
|
||||
Direction(..)
|
||||
Direction2D(..)
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Hooks.ManageDocks (Direction(..))
|
||||
import XMonad.Util.Types (Direction2D(..))
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import Control.Applicative ((<$>))
|
||||
@@ -84,15 +86,15 @@ import Graphics.X11.Xlib
|
||||
-- - manageHook to draw window decos?
|
||||
|
||||
withWindowNavigation :: (KeySym, KeySym, KeySym, KeySym) -> XConfig l -> IO (XConfig l)
|
||||
withWindowNavigation (u,l,d,r) conf =
|
||||
withWindowNavigationKeys [ ((modMask conf , u), WNGo U),
|
||||
((modMask conf , l), WNGo L),
|
||||
((modMask conf , d), WNGo D),
|
||||
((modMask conf , r), WNGo R),
|
||||
((modMask conf .|. shiftMask, u), WNSwap U),
|
||||
((modMask conf .|. shiftMask, l), WNSwap L),
|
||||
((modMask conf .|. shiftMask, d), WNSwap D),
|
||||
((modMask conf .|. shiftMask, r), WNSwap R) ]
|
||||
withWindowNavigation (u,l,d,r) conf@XConfig{modMask=modm} =
|
||||
withWindowNavigationKeys [ ((modm , u), WNGo U),
|
||||
((modm , l), WNGo L),
|
||||
((modm , d), WNGo D),
|
||||
((modm , r), WNGo R),
|
||||
((modm .|. shiftMask, u), WNSwap U),
|
||||
((modm .|. shiftMask, l), WNSwap L),
|
||||
((modm .|. shiftMask, d), WNSwap D),
|
||||
((modm .|. shiftMask, r), WNSwap R) ]
|
||||
conf
|
||||
|
||||
withWindowNavigationKeys :: [((KeyMask, KeySym), WNAction)] -> XConfig l -> IO (XConfig l)
|
||||
@@ -104,7 +106,7 @@ withWindowNavigationKeys wnKeys conf = do
|
||||
where fromWNAction posRef (WNGo dir) = go posRef dir
|
||||
fromWNAction posRef (WNSwap dir) = swap posRef dir
|
||||
|
||||
data WNAction = WNGo Direction | WNSwap Direction
|
||||
data WNAction = WNGo Direction2D | WNSwap Direction2D
|
||||
|
||||
type WNState = Map WorkspaceId Point
|
||||
|
||||
@@ -113,10 +115,10 @@ type WNState = Map WorkspaceId Point
|
||||
-- 2. get target windowrect
|
||||
-- 3. focus window
|
||||
-- 4. set new position
|
||||
go :: IORef WNState -> Direction -> X ()
|
||||
go :: IORef WNState -> Direction2D -> X ()
|
||||
go = withTargetWindow W.focusWindow
|
||||
|
||||
swap :: IORef WNState -> Direction -> X ()
|
||||
swap :: IORef WNState -> Direction2D -> X ()
|
||||
swap = withTargetWindow swapWithFocused
|
||||
where swapWithFocused targetWin winSet =
|
||||
case W.peek winSet of
|
||||
@@ -128,7 +130,7 @@ swap = withTargetWindow swapWithFocused
|
||||
mapWindows'' f (W.Stack focused up down) = W.Stack (f focused) (map f up) (map f down)
|
||||
swapWin win1 win2 win = if win == win1 then win2 else if win == win2 then win1 else win
|
||||
|
||||
withTargetWindow :: (Window -> WindowSet -> WindowSet) -> IORef WNState -> Direction -> X ()
|
||||
withTargetWindow :: (Window -> WindowSet -> WindowSet) -> IORef WNState -> Direction2D -> X ()
|
||||
withTargetWindow adj posRef dir = fromCurrentPoint posRef $ \win pos -> do
|
||||
targets <- filter ((/= win) . fst) <$> navigableTargets pos dir
|
||||
whenJust (listToMaybe targets) $ \(targetWin, targetRect) -> do
|
||||
@@ -175,12 +177,12 @@ Point x y `inside` Rectangle rx ry rw rh =
|
||||
midPoint :: Position -> Dimension -> Position
|
||||
midPoint pos dim = pos + fromIntegral dim `div` 2
|
||||
|
||||
navigableTargets :: Point -> Direction -> X [(Window, Rectangle)]
|
||||
navigableTargets :: Point -> Direction2D -> X [(Window, Rectangle)]
|
||||
navigableTargets point dir = navigable dir point <$> windowRects
|
||||
|
||||
-- Filters and sorts the windows in terms of what is closest from the Point in
|
||||
-- the Direction.
|
||||
navigable :: Direction -> Point -> [(Window, Rectangle)] -> [(Window, Rectangle)]
|
||||
-- the Direction2D.
|
||||
navigable :: Direction2D -> Point -> [(Window, Rectangle)] -> [(Window, Rectangle)]
|
||||
navigable d pt = sortby d . filter (inr d pt . snd)
|
||||
|
||||
-- Produces a list of normal-state windows, on any screen. Rectangles are
|
||||
@@ -197,7 +199,7 @@ windowRect win = withDisplay $ \dpy -> do
|
||||
|
||||
-- Modified from droundy's implementation of WindowNavigation:
|
||||
|
||||
inr :: Direction -> Point -> Rectangle -> Bool
|
||||
inr :: Direction2D -> Point -> Rectangle -> Bool
|
||||
inr D (Point px py) (Rectangle rx ry w h) = px >= rx && px < rx + fromIntegral w &&
|
||||
py < ry + fromIntegral h
|
||||
inr U (Point px py) (Rectangle rx ry w _) = px >= rx && px < rx + fromIntegral w &&
|
||||
@@ -207,7 +209,7 @@ inr R (Point px py) (Rectangle rx ry _ h) = px < rx &&
|
||||
inr L (Point px py) (Rectangle rx ry w h) = px > rx + fromIntegral w &&
|
||||
py >= ry && py < ry + fromIntegral h
|
||||
|
||||
sortby :: Direction -> [(a,Rectangle)] -> [(a,Rectangle)]
|
||||
sortby :: Direction2D -> [(a,Rectangle)] -> [(a,Rectangle)]
|
||||
sortby D = sortBy $ comparing (rect_y . snd)
|
||||
sortby R = sortBy $ comparing (rect_x . snd)
|
||||
sortby U = reverse . sortby D
|
||||
|
54
XMonad/Actions/WithAll.hs
Normal file
54
XMonad/Actions/WithAll.hs
Normal file
@@ -0,0 +1,54 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.WithAll
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Provides functions for performing a given action on all windows of
|
||||
-- the current workspace.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.WithAll (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
sinkAll, withAll,
|
||||
withAll', killAll) where
|
||||
|
||||
import Data.Foldable hiding (foldr)
|
||||
|
||||
import XMonad
|
||||
import XMonad.Core
|
||||
import XMonad.Operations
|
||||
import XMonad.StackSet
|
||||
|
||||
-- $usage
|
||||
--
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Actions.WithAll
|
||||
--
|
||||
-- then add a keybinding; for example:
|
||||
--
|
||||
-- , ((modm .|. shiftMask, xK_t), sinkAll)
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
||||
-- | Un-float all floating windows on the current workspace.
|
||||
sinkAll :: X ()
|
||||
sinkAll = withAll' sink
|
||||
|
||||
-- | Apply a function to all windows on the current workspace.
|
||||
withAll' :: (Window -> WindowSet -> WindowSet) -> X ()
|
||||
withAll' f = windows $ \ws -> let all' = integrate' . stack . workspace . current $ ws
|
||||
in foldr f ws all'
|
||||
|
||||
-- | Execute an 'X' action for each window on the current workspace.
|
||||
withAll :: (Window -> X ()) -> X()
|
||||
withAll f = withWindowSet $ \ws -> let all' = integrate' . stack . workspace . current $ ws
|
||||
in forM_ all' f
|
||||
|
||||
-- | Kill all the windows on the current workspace.
|
||||
killAll :: X()
|
||||
killAll = withAll killWindow
|
219
XMonad/Actions/WorkspaceCursors.hs
Normal file
219
XMonad/Actions/WorkspaceCursors.hs
Normal file
@@ -0,0 +1,219 @@
|
||||
{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, MultiParamTypeClasses #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.WorkspaceCursors
|
||||
-- Copyright : (c) 2009 Adam Vogt <vogt.adam@gmail.com>
|
||||
-- License : BSD
|
||||
--
|
||||
-- Maintainer : Adam Vogt
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Like "XMonad.Actions.Plane" for an arbitrary number of dimensions.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.WorkspaceCursors
|
||||
(
|
||||
-- * Usage
|
||||
-- $usage
|
||||
|
||||
focusDepth
|
||||
,makeCursors
|
||||
,toList
|
||||
,workspaceCursors
|
||||
|
||||
,WorkspaceCursors
|
||||
,getFocus
|
||||
|
||||
-- * Modifying the focus
|
||||
,modifyLayer
|
||||
,modifyLayer'
|
||||
,shiftModifyLayer,shiftLayer
|
||||
|
||||
-- * Functions to pass to 'modifyLayer'
|
||||
,focusNth'
|
||||
,noWrapUp,noWrapDown
|
||||
|
||||
-- * Todo
|
||||
-- $todo
|
||||
) where
|
||||
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import XMonad.Actions.FocusNth(focusNth')
|
||||
import XMonad.Layout.LayoutModifier(ModifiedLayout(..),
|
||||
LayoutModifier(handleMess, redoLayout))
|
||||
import XMonad(Typeable, Message, WorkspaceId, X, XState(windowset),
|
||||
fromMessage, sendMessage, windows, gets)
|
||||
import Control.Monad((<=<), guard, liftM, liftM2, when)
|
||||
import Control.Applicative((<$>))
|
||||
import Data.Foldable(Foldable(foldMap), toList)
|
||||
import Data.Maybe(fromJust, listToMaybe)
|
||||
import Data.Monoid(Monoid(mappend, mconcat))
|
||||
import Data.Traversable(sequenceA)
|
||||
|
||||
-- $usage
|
||||
--
|
||||
-- Here is an example config:
|
||||
--
|
||||
-- > import XMonad
|
||||
-- > import XMonad.Actions.WorkspaceCursors
|
||||
-- > import XMonad.Hooks.DynamicLog
|
||||
-- > import XMonad.Util.EZConfig
|
||||
-- > import qualified XMonad.StackSet as W
|
||||
-- >
|
||||
-- > main = do
|
||||
-- > x <- xmobar conf
|
||||
-- > xmonad x
|
||||
-- >
|
||||
-- > conf = additionalKeysP defaultConfig
|
||||
-- > { layoutHook = workspaceCursors myCursors $ layoutHook defaultConfig
|
||||
-- > , workspaces = toList myCursors } $
|
||||
-- > [("M-"++shift++control++[k], f direction depth)
|
||||
-- > | (f,shift) <- zip [modifyLayer,shiftModifyLayer] ["","S-"]
|
||||
-- > , (direction,control) <- zip [W.focusUp',W.focusDown'] ["C-",""]
|
||||
-- > , (depth,k) <- zip (reverse [1..focusDepth myCursors]) "asdf"]
|
||||
-- > ++ moreKeybindings
|
||||
-- >
|
||||
-- > moreKeybindings = []
|
||||
-- >
|
||||
-- > myCursors = makeCursors $ map (map (\x -> [x])) [ "1234", "abc", "xyz"]
|
||||
-- > -- myCursors = makeCursors [["wsA","wsB","wsC"],["-alpha-","-beta-","-gamma-"],["x","y"]]
|
||||
|
||||
|
||||
-- $todo
|
||||
--
|
||||
-- * Find and document how to raise the allowable length of arguments:
|
||||
-- restoring xmonad's state results in: @xmonad: executeFile: resource
|
||||
-- exhausted (Argument list too long)@ when you specify more than about 50
|
||||
-- workspaces. Or change it such that workspaces are created when you try to
|
||||
-- view it.
|
||||
--
|
||||
-- * Function for pretty printing for DynamicLog that groups workspaces by
|
||||
-- common prefixes
|
||||
--
|
||||
-- * Examples of adding workspaces to the cursors, having them appear multiple
|
||||
-- times for being able to show jumping to some n'th multiple workspace
|
||||
|
||||
-- | makeCursors requires a nonempty string, and each sublist must be nonempty
|
||||
makeCursors :: [[String]] -> Cursors String
|
||||
makeCursors [] = error "Workspace Cursors cannot be empty"
|
||||
makeCursors a = concat . reverse <$> foldl addDim x xs
|
||||
where x = end $ map return $ head a
|
||||
xs = map (map return) $ tail a
|
||||
-- this could probably be simplified, but this true:
|
||||
-- toList . makeCursors == map (concat . reverse) . sequence . reverse . map (map (:[]))
|
||||
-- the strange order is used because it makes the regular M-1..9
|
||||
-- bindings change the prefixes first
|
||||
|
||||
addDim :: (Monoid a) => Cursors a -> [a] -> Cursors a
|
||||
addDim prev prefixes = Cons . fromJust . W.differentiate
|
||||
$ map ((<$> prev) . mappend) prefixes
|
||||
|
||||
end :: [a] -> Cursors a
|
||||
end = Cons . fromJust . W.differentiate . map End
|
||||
|
||||
data Cursors a
|
||||
= Cons (W.Stack (Cursors a))
|
||||
| End a deriving (Eq,Show,Read,Typeable)
|
||||
|
||||
instance Foldable Cursors where
|
||||
foldMap f (End x) = f x
|
||||
foldMap f (Cons (W.Stack x y z)) = foldMap f x `mappend` mconcat (map (foldMap f) $ reverse y ++ z)
|
||||
|
||||
instance Functor Cursors where
|
||||
fmap f (End a) = End $ f a
|
||||
fmap f (Cons (W.Stack x y z)) = Cons $ W.Stack (fmap f x) (fmap (fmap f) y) (fmap (fmap f) z)
|
||||
|
||||
changeFocus :: (Cursors t -> Bool) -> Cursors t -> [Cursors t]
|
||||
changeFocus p (Cons x) = do
|
||||
choose <- chFocus p x
|
||||
foc <- changeFocus p $ W.focus choose
|
||||
return . Cons $ choose { W.focus = foc }
|
||||
changeFocus p x = guard (p x) >> return x
|
||||
|
||||
chFocus :: (a -> Bool) -> W.Stack a -> [W.Stack a]
|
||||
chFocus p st = filter (p . W.focus) $ zipWith const (iterate W.focusDown' st) (W.integrate st)
|
||||
|
||||
getFocus :: Cursors b -> b
|
||||
getFocus (Cons x) = getFocus $ W.focus x
|
||||
getFocus (End x) = x
|
||||
|
||||
-- This could be made more efficient, if the fact that the suffixes are grouped
|
||||
focusTo :: (Eq t) => t -> Cursors t -> Maybe (Cursors t)
|
||||
focusTo x = listToMaybe . filter ((x==) . getFocus) . changeFocus (const True)
|
||||
|
||||
-- | non-wrapping version of 'W.focusUp''
|
||||
noWrapUp :: W.Stack t -> W.Stack t
|
||||
noWrapUp (W.Stack t (l:ls) rs) = W.Stack l ls (t:rs)
|
||||
noWrapUp x@(W.Stack _ [] _ ) = x
|
||||
|
||||
-- | non-wrapping version of 'W.focusDown''
|
||||
noWrapDown :: W.Stack t -> W.Stack t
|
||||
noWrapDown = reverseStack . noWrapUp . reverseStack
|
||||
where reverseStack (W.Stack t ls rs) = W.Stack t rs ls
|
||||
|
||||
focusDepth :: Cursors t -> Int
|
||||
focusDepth (Cons x) = 1 + focusDepth (W.focus x)
|
||||
focusDepth (End _) = 0
|
||||
|
||||
descend :: Monad m =>(W.Stack (Cursors a) -> m (W.Stack (Cursors a)))-> Int-> Cursors a-> m (Cursors a)
|
||||
descend f 1 (Cons x) = Cons `liftM` f x
|
||||
descend f n (Cons x) | n > 1 = liftM Cons $ descend f (pred n) `onFocus` x
|
||||
descend _ _ x = return x
|
||||
|
||||
onFocus :: (Monad m) => (a1 -> m a1) -> W.Stack a1 -> m (W.Stack a1)
|
||||
onFocus f st = (\x -> st { W.focus = x}) `liftM` f (W.focus st)
|
||||
|
||||
-- | @modifyLayer@ is used to change the focus at a given depth
|
||||
modifyLayer :: (W.Stack (Cursors String) -> W.Stack (Cursors String)) -> Int -> X ()
|
||||
modifyLayer f depth = modifyCursors (descend (return . f) depth)
|
||||
|
||||
-- | @shiftModifyLayer@ is the same as 'modifyLayer', but also shifts the
|
||||
-- currently focused window to the new workspace
|
||||
shiftModifyLayer :: (W.Stack (Cursors String) -> W.Stack (Cursors WorkspaceId))-> Int-> X ()
|
||||
shiftModifyLayer f = modifyLayer' $ \st -> do
|
||||
let st' = f st
|
||||
windows $ W.shift $ getFocus (Cons st')
|
||||
return st'
|
||||
|
||||
-- | @shiftLayer@ is the same as 'shiftModifyLayer', but the focus remains on
|
||||
-- the current workspace.
|
||||
shiftLayer :: (W.Stack (Cursors String) -> W.Stack (Cursors WorkspaceId))-> Int-> X ()
|
||||
shiftLayer f = modifyLayer' $ \st -> do
|
||||
windows $ W.shift $ getFocus $ Cons $ f st
|
||||
return st
|
||||
|
||||
-- | example usages are 'shiftLayer' and 'shiftModifyLayer'
|
||||
modifyLayer' :: (W.Stack (Cursors String) -> X (W.Stack (Cursors String))) -> Int -> X ()
|
||||
modifyLayer' f depth = modifyCursors (descend f depth)
|
||||
|
||||
modifyCursors :: (Cursors String -> X (Cursors String)) -> X ()
|
||||
modifyCursors = sendMessage . ChangeCursors . (liftM2 (>>) updateXMD return <=<)
|
||||
|
||||
data WorkspaceCursors a = WorkspaceCursors (Cursors String)
|
||||
deriving (Typeable,Read,Show)
|
||||
|
||||
-- | The state is stored in the 'WorkspaceCursors' layout modifier. Put this as
|
||||
-- your outermost modifier, unless you want different cursors at different
|
||||
-- times (using "XMonad.Layout.MultiToggle")
|
||||
workspaceCursors :: Cursors String -> l a -> ModifiedLayout WorkspaceCursors l a
|
||||
workspaceCursors = ModifiedLayout . WorkspaceCursors
|
||||
|
||||
data ChangeCursors = ChangeCursors { unWrap :: Cursors String -> X (Cursors String) }
|
||||
deriving (Typeable)
|
||||
|
||||
instance Message ChangeCursors
|
||||
|
||||
updateXMD :: Cursors WorkspaceId -> X ()
|
||||
updateXMD cs = do
|
||||
changed <- gets $ (getFocus cs /=) . W.currentTag . windowset
|
||||
when changed $ windows $ W.greedyView $ getFocus cs
|
||||
|
||||
instance LayoutModifier WorkspaceCursors a where
|
||||
redoLayout (WorkspaceCursors cs) _ _ arrs = do
|
||||
cws <- gets $ W.currentTag . windowset
|
||||
return (arrs,WorkspaceCursors <$> focusTo cws cs)
|
||||
|
||||
handleMess (WorkspaceCursors cs) m =
|
||||
sequenceA $ fmap WorkspaceCursors . ($ cs) . unWrap <$> fromMessage m
|
@@ -90,8 +90,7 @@ arossatoConfig = do
|
||||
map show [7 .. 9 :: Int]
|
||||
, logHook = myDynLog xmobar -- REMOVE this line if you do not have xmobar installed!
|
||||
, manageHook = newManageHook
|
||||
, layoutHook = eventHook ServerMode $
|
||||
avoidStruts $
|
||||
, layoutHook = avoidStruts $
|
||||
decorated |||
|
||||
noBorders mytabs |||
|
||||
otherLays
|
||||
@@ -99,6 +98,7 @@ arossatoConfig = do
|
||||
, normalBorderColor = "white"
|
||||
, focusedBorderColor = "black"
|
||||
, keys = newKeys
|
||||
, handleEventHook = serverModeEventHook
|
||||
, focusFollowsMouse = False
|
||||
}
|
||||
where
|
||||
|
@@ -7,6 +7,8 @@
|
||||
-- License : BSD
|
||||
--
|
||||
-- Maintainer : Devin Mullins <me@twifkak.com>
|
||||
-- Stability : stable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- This module fixes some of the keybindings for the francophone among you who
|
||||
-- use an AZERTY keyboard layout. Config stolen from TeXitoi's config on the
|
||||
|
@@ -7,29 +7,173 @@
|
||||
-- License : BSD
|
||||
--
|
||||
-- Maintainer : Spencer Janssen <spencerjanssen@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- This module provides a config suitable for use with a desktop
|
||||
-- environment such as KDE or GNOME.
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
module XMonad.Config.Desktop (
|
||||
|
||||
-- | Several basic integration settings are common to all of xmonad's
|
||||
-- desktop integration configurations. The specific desktop environment
|
||||
-- (DE) modules like "XMonad.Config.Gnome" use this module's
|
||||
-- @desktopConfig@ to set up basic communication between xmonad and
|
||||
-- the DE via a subset of the Extended Window Manager Hints (EWMH)
|
||||
-- specification. Extra xmonad settings unique to specific DE's are
|
||||
-- added by overriding or modifying @desktopConfig@ fields in the
|
||||
-- same way that @defaultConfig@ is customized in @~\/.xmonad/xmonad.hs@.
|
||||
--
|
||||
-- For more information about EWMH see:
|
||||
--
|
||||
-- <http://standards.freedesktop.org/wm-spec/wm-spec-latest.html>
|
||||
--
|
||||
-- See also: "XMonad.Hooks.EwmhDesktops", "XMonad.Hooks.ManageDocks",
|
||||
-- "XMonad.Util.EZConfig".
|
||||
|
||||
-- * Usage
|
||||
-- $usage
|
||||
|
||||
desktopConfig,
|
||||
|
||||
-- * Customizing a desktop config
|
||||
-- $customizing
|
||||
|
||||
-- ** Modifying layouts, manageHook, or key bindings
|
||||
-- $layouts
|
||||
desktopLayoutModifiers
|
||||
|
||||
-- ** Modifying the logHook
|
||||
-- $logHook
|
||||
|
||||
-- ** Modifying the handleEventHook
|
||||
-- $eventHook
|
||||
|
||||
-- ** Modifying the startupHook
|
||||
-- $startupHook
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Config (defaultConfig)
|
||||
import XMonad.Hooks.ManageDocks
|
||||
import XMonad.Hooks.EwmhDesktops
|
||||
import XMonad.Util.Cursor
|
||||
|
||||
import qualified Data.Map as M
|
||||
|
||||
desktopConfig = defaultConfig
|
||||
{ logHook = ewmhDesktopsLogHook
|
||||
, layoutHook = desktopLayoutModifiers $ layoutHook defaultConfig
|
||||
, manageHook = manageHook defaultConfig <+> manageDocks
|
||||
, keys = \c -> desktopKeys c `M.union` keys defaultConfig c }
|
||||
-- $usage
|
||||
-- While this document describes how to configure xmonad, you also need
|
||||
-- to set up your Desktop Environment (DE) and display manager to use
|
||||
-- xmonad as its window manager. For DE and distro specific tips on
|
||||
-- how to do so, see the xmonad wiki:
|
||||
--
|
||||
-- <http://haskell.org/haskellwiki/Xmonad>
|
||||
--
|
||||
-- To configure xmonad for use with a DE or with DE tools like panels
|
||||
-- and pagers, in place of @defaultConfig@ in your @~\/.xmonad/xmonad.hs@,
|
||||
-- use @desktopConfig@ or one of the other desktop configs from the
|
||||
-- @XMonad.Config@ namespace. The following setup and customization examples
|
||||
-- work the same way for the other desktop configs as for @desktopConfig@.
|
||||
-- If you are using a specific DE config, import its module instead, and
|
||||
-- use its config in place of @desktopConfig@ in the following examples.
|
||||
--
|
||||
-- > import XMonad
|
||||
-- > import XMonad.Config.Desktop
|
||||
-- >
|
||||
-- > main = xmonad desktopConfig
|
||||
--
|
||||
-- @desktopConfig@ is an 'XConfig' that configures xmonad to
|
||||
-- ignore and leave room for dock type windows like panels and trays, adds
|
||||
-- the default key binding to toggle panel visibility, and activates basic
|
||||
-- EWMH support. It also sets a prettier root window mouse pointer.
|
||||
|
||||
-- $customizing
|
||||
-- To customize a desktop config, modify its fields as is illustrated with
|
||||
-- @defaultConfig@ in the \"Extending xmonad\" section of "XMonad.Doc.Extending".
|
||||
|
||||
-- $layouts
|
||||
-- See also "XMonad.Util.EZConfig" for more options for modifying key bindings.
|
||||
-- To add to layouts, manageHook or key bindings use something like the following
|
||||
-- to combine your modifications with the desktop config settings:
|
||||
--
|
||||
-- > import XMonad
|
||||
-- > import XMonad.Config.Desktop
|
||||
-- > import XMonad.Layout.Tabbed
|
||||
-- > import XMonad.Util.EZConfig (additionalKeys)
|
||||
-- >
|
||||
-- > main =
|
||||
-- > xmonad $ desktopConfig {
|
||||
-- > -- add manage hooks while still ignoring panels and using default manageHooks
|
||||
-- > manageHook = myManageHook <+> manageHook desktopConfig
|
||||
-- >
|
||||
-- > -- add a fullscreen tabbed layout that does not avoid covering
|
||||
-- > -- up desktop panels before the desktop layouts
|
||||
-- > , layoutHook = simpleTabbed ||| layoutHook desktopConfig
|
||||
-- > }
|
||||
-- > -- add a screenshot key to the default desktop bindings
|
||||
-- > `additionalKeys` [ ((mod4Mask, xK_F8), spawn "scrot") ]
|
||||
--
|
||||
-- To replace the desktop layouts with your own choices, but still
|
||||
-- allow toggling panel visibility, use 'desktopLayoutModifiers' to
|
||||
-- modify your layouts:
|
||||
--
|
||||
-- > , layoutHook = desktopLayoutModifiers $ simpleTabbed ||| Tall 1 0.03 0.5
|
||||
--
|
||||
-- @desktopLayoutModifiers@ modifies a layout to avoid covering docks, panels,
|
||||
-- etc. that set the @_NET_WM_STRUT_PARTIAL@ property.
|
||||
-- See also "XMonad.Hooks.ManageDocks".
|
||||
|
||||
-- $logHook
|
||||
-- To add to the logHook while still sending workspace and window information
|
||||
-- to DE apps use something like:
|
||||
--
|
||||
-- > , logHook = myLogHook >> logHook desktopConfig
|
||||
--
|
||||
-- Or for more elaborate logHooks you can use @do@:
|
||||
--
|
||||
-- > , logHook = do
|
||||
-- > dynamicLogWithPP xmobarPP
|
||||
-- > updatePointer (Relative 0.9 0.9)
|
||||
-- > logHook desktopConfig
|
||||
--
|
||||
|
||||
-- $eventHook
|
||||
-- To customize xmonad's event handling while still having it respond
|
||||
-- to EWMH events from pagers, task bars, etc. add to your imports:
|
||||
--
|
||||
-- > import Data.Monoid
|
||||
--
|
||||
-- and use 'Data.Monoid.mappend' to combine event hooks (right to left application like @\<+\>@)
|
||||
--
|
||||
-- > , handleEventHook = mappend myEventHooks (handleEventHook desktopConfig)
|
||||
--
|
||||
-- or 'Data.Monoid.mconcat' (like @composeAll@)
|
||||
--
|
||||
-- > , handleEventHook = mconcat
|
||||
-- > [ myMouseHandler
|
||||
-- > , myMessageHandler
|
||||
-- > , handleEventHook desktopConfig ]
|
||||
--
|
||||
|
||||
-- $startupHook
|
||||
-- To run the desktop startupHook, plus add further actions to be run each
|
||||
-- time xmonad starts or restarts, use '>>' to combine actions as in the
|
||||
-- logHook example, or something like:
|
||||
--
|
||||
-- > , startupHook = do
|
||||
-- > startupHook desktopConfig
|
||||
-- > spawn "xmonad-restart.sh"
|
||||
-- > adjustEventInput
|
||||
--
|
||||
|
||||
desktopConfig = ewmh defaultConfig
|
||||
{ startupHook = setDefaultCursor xC_left_ptr
|
||||
, layoutHook = desktopLayoutModifiers $ layoutHook defaultConfig
|
||||
, manageHook = manageHook defaultConfig <+> manageDocks
|
||||
, keys = \c -> desktopKeys c `M.union` keys defaultConfig c }
|
||||
|
||||
desktopKeys (XConfig {modMask = modm}) = M.fromList $
|
||||
[ ((modm, xK_b), sendMessage ToggleStruts) ]
|
||||
|
||||
desktopLayoutModifiers layout = avoidStruts $ ewmhDesktopsLayout layout
|
||||
desktopLayoutModifiers layout = avoidStruts layout
|
||||
|
||||
|
@@ -22,7 +22,7 @@ import XMonad.Layout.Combo ( combineTwo )
|
||||
import XMonad.Layout.Named ( named )
|
||||
import XMonad.Layout.LayoutCombinators
|
||||
import XMonad.Layout.Square ( Square(Square) )
|
||||
import XMonad.Layout.WindowNavigation ( Navigate(Move,Swap,Go), Direction(U,D,R,L),
|
||||
import XMonad.Layout.WindowNavigation ( Navigate(Move,Swap,Go), Direction2D(U,D,R,L),
|
||||
windowNavigation )
|
||||
import XMonad.Layout.BoringWindows ( boringWindows, markBoring, clearBoring,
|
||||
focusUp, focusDown )
|
||||
@@ -40,11 +40,10 @@ import XMonad.Actions.CopyWindow ( kill1, copy )
|
||||
import XMonad.Actions.DynamicWorkspaces ( withNthWorkspace, withWorkspace,
|
||||
selectWorkspace, renameWorkspace, removeWorkspace )
|
||||
import XMonad.Actions.CycleWS ( moveTo, WSType( HiddenNonEmptyWS ),
|
||||
WSDirection( Prev, Next) )
|
||||
Direction1D( Prev, Next) )
|
||||
|
||||
import XMonad.Hooks.ManageDocks ( avoidStruts, manageDocks )
|
||||
import XMonad.Hooks.EwmhDesktops ( ewmhDesktopsLogHook,
|
||||
ewmhDesktopsLayout )
|
||||
import XMonad.Hooks.EwmhDesktops ( ewmh )
|
||||
|
||||
myXPConfig :: XPConfig
|
||||
myXPConfig = defaultXPConfig {font="-*-lucida-medium-r-*-*-14-*-*-*-*-*-*-*"
|
||||
@@ -95,7 +94,7 @@ keys x = M.fromList $
|
||||
, ((modMask x .|. controlMask .|. shiftMask, xK_Left), sendMessage $ Move L)
|
||||
, ((modMask x .|. controlMask .|. shiftMask, xK_Up), sendMessage $ Move U)
|
||||
, ((modMask x .|. controlMask .|. shiftMask, xK_Down), sendMessage $ Move D)
|
||||
|
||||
|
||||
, ((0, xK_F2 ), spawn "gnome-terminal") -- %! Launch gnome-terminal
|
||||
, ((0, xK_F3 ), shellPrompt myXPConfig) -- %! Launch program
|
||||
, ((0, xK_F11 ), spawn "ksnapshot") -- %! Take snapshot
|
||||
@@ -112,16 +111,16 @@ keys x = M.fromList $
|
||||
, ((modMask x, xK_space), sendMessage Toggle)
|
||||
|
||||
]
|
||||
|
||||
|
||||
++
|
||||
zip (zip (repeat $ modMask x) [xK_F1..xK_F12]) (map (withNthWorkspace W.greedyView) [0..])
|
||||
++
|
||||
zip (zip (repeat (modMask x .|. shiftMask)) [xK_F1..xK_F12]) (map (withNthWorkspace copy) [0..])
|
||||
|
||||
config = defaultConfig
|
||||
config = ewmh defaultConfig
|
||||
{ borderWidth = 1 -- Width of the window border in pixels.
|
||||
, XMonad.workspaces = ["mutt","iceweasel"]
|
||||
, layoutHook = ewmhDesktopsLayout $ showWName $ workspaceDir "~" $
|
||||
, layoutHook = showWName $ workspaceDir "~" $
|
||||
boringWindows $ smartBorders $ windowNavigation $
|
||||
maximizeVertical $ toggleLayouts Full $ avoidStruts $
|
||||
named "tabbed" mytab |||
|
||||
@@ -131,7 +130,6 @@ config = defaultConfig
|
||||
****//* combineTwo Square mytab mytab) -- |||
|
||||
--mosaic 0.25 0.5
|
||||
, manageHook = manageHook defaultConfig <+> manageDocks -- add panel-handling
|
||||
, logHook = ewmhDesktopsLogHook -- actually, no logging here, just other stuff
|
||||
, terminal = "xterm" -- The preferred terminal program.
|
||||
, normalBorderColor = "#222222" -- Border color for unfocused windows.
|
||||
, focusedBorderColor = "#00ff00" -- Border color for focused windows.
|
||||
@@ -142,12 +140,12 @@ config = defaultConfig
|
||||
mytab = tabbed CustomShrink defaultTheme
|
||||
|
||||
instance Shrinker CustomShrink where
|
||||
shrinkIt shr s | Just s' <- dropFromHead " " s = shrinkIt shr s'
|
||||
shrinkIt shr s | Just s' <- dropFromTail " " s = shrinkIt shr s'
|
||||
shrinkIt shr s | Just s' <- dropFromTail "- Iceweasel" s = shrinkIt shr s'
|
||||
shrinkIt shr s | Just s' <- dropFromTail "- KPDF" s = shrinkIt shr s'
|
||||
shrinkIt shr s | Just s' <- dropFromHead "file://" s = shrinkIt shr s'
|
||||
shrinkIt shr s | Just s' <- dropFromHead "http://" s = shrinkIt shr s'
|
||||
shrinkIt shr s | Just s' <- dropFromHead " " s = shrinkIt shr s'
|
||||
shrinkIt shr s | Just s' <- dropFromTail " " s = shrinkIt shr s'
|
||||
shrinkIt shr s | Just s' <- dropFromTail "- Iceweasel" s = shrinkIt shr s'
|
||||
shrinkIt shr s | Just s' <- dropFromTail "- KPDF" s = shrinkIt shr s'
|
||||
shrinkIt shr s | Just s' <- dropFromHead "file://" s = shrinkIt shr s'
|
||||
shrinkIt shr s | Just s' <- dropFromHead "http://" s = shrinkIt shr s'
|
||||
shrinkIt _ s | n > 9 = s : map cut [2..(halfn-3)] ++ shrinkIt shrinkText s
|
||||
where n = length s
|
||||
halfn = n `div` 2
|
||||
|
@@ -7,6 +7,8 @@
|
||||
-- License : BSD
|
||||
--
|
||||
-- Maintainer : Spencer Janssen <spencerjanssen@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- This module provides a config suitable for use with the GNOME desktop
|
||||
-- environment.
|
||||
@@ -15,14 +17,18 @@ module XMonad.Config.Gnome (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
gnomeConfig,
|
||||
gnomeRun
|
||||
gnomeRun,
|
||||
gnomeRegister
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Config.Desktop
|
||||
import XMonad.Util.Run (safeSpawn)
|
||||
|
||||
import qualified Data.Map as M
|
||||
|
||||
import System.Environment (getEnvironment)
|
||||
|
||||
-- $usage
|
||||
-- To use this module, start with the following @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
@@ -30,11 +36,13 @@ import qualified Data.Map as M
|
||||
-- > import XMonad.Config.Gnome
|
||||
-- >
|
||||
-- > main = xmonad gnomeConfig
|
||||
--
|
||||
--
|
||||
-- For examples of how to further customize @gnomeConfig@ see "XMonad.Config.Desktop".
|
||||
|
||||
gnomeConfig = desktopConfig
|
||||
{ terminal = "gnome-terminal"
|
||||
, keys = \c -> gnomeKeys c `M.union` keys desktopConfig c }
|
||||
, keys = \c -> gnomeKeys c `M.union` keys desktopConfig c
|
||||
, startupHook = gnomeRegister >> startupHook desktopConfig }
|
||||
|
||||
gnomeKeys (XConfig {modMask = modm}) = M.fromList $
|
||||
[ ((modm, xK_p), gnomeRun)
|
||||
@@ -53,3 +61,22 @@ gnomeRun = withDisplay $ \dpy -> do
|
||||
setClientMessageEvent e rw gnome_panel 32 panel_run 0
|
||||
sendEvent dpy rw False structureNotifyMask e
|
||||
sync dpy False
|
||||
|
||||
-- | Register xmonad with gnome. 'dbus-send' must be in the $PATH with which
|
||||
-- xmonad is started.
|
||||
--
|
||||
-- This action reduces a delay on startup only only if you have configured
|
||||
-- gnome-session>=2.26: to start xmonad with a command as such:
|
||||
--
|
||||
-- > gconftool-2 -s /desktop/gnome/session/required_components/windowmanager xmonad --type string
|
||||
gnomeRegister :: MonadIO m => m ()
|
||||
gnomeRegister = io $ do
|
||||
x <- lookup "DESKTOP_AUTOSTART_ID" `fmap` getEnvironment
|
||||
whenJust x $ \sessionId -> safeSpawn "dbus-send"
|
||||
["--session"
|
||||
,"--print-reply=string"
|
||||
,"--dest=org.gnome.SessionManager"
|
||||
,"/org/gnome/SessionManager"
|
||||
,"org.gnome.SessionManager.RegisterClient"
|
||||
,"string:xmonad"
|
||||
,"string:"++sessionId]
|
||||
|
@@ -7,6 +7,8 @@
|
||||
-- License : BSD
|
||||
--
|
||||
-- Maintainer : Spencer Janssen <spencerjanssen@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- This module provides a config suitable for use with the KDE desktop
|
||||
-- environment.
|
||||
@@ -32,7 +34,9 @@ import qualified Data.Map as M
|
||||
-- > main = xmonad kdeConfig
|
||||
--
|
||||
-- For KDE 4, replace 'kdeConfig' with 'kde4Config'
|
||||
--
|
||||
--
|
||||
-- For examples of how to further customize @kdeConfig@ see "XMonad.Config.Desktop".
|
||||
|
||||
|
||||
kdeConfig = desktopConfig
|
||||
{ terminal = "konsole"
|
||||
|
@@ -38,7 +38,7 @@ layout = fromSetGet (\x c -> c { layoutHook = x }) layoutHook
|
||||
terminal = fromSetGet (\x c -> c { X.terminal = x }) X.terminal
|
||||
keys = fromSetGet (\x c -> c { X.keys = x }) X.keys
|
||||
|
||||
set :: Accessor (XConfig LayoutList) a -> a -> Config ()
|
||||
set :: Accessor (XConfig LayoutList) a -> a -> Config ()
|
||||
set r x = tell (mkW $ r ^= x)
|
||||
add r x = tell (mkW (r ^: mappend x))
|
||||
|
||||
|
@@ -10,6 +10,7 @@ import XMonad.Config (defaultConfig)
|
||||
import XMonad.Layout.NoBorders
|
||||
import XMonad.Hooks.DynamicLog hiding (xmobar)
|
||||
import XMonad.Hooks.ManageDocks
|
||||
import XMonad.Hooks.ManageHelpers (isFullscreen, doFullFloat)
|
||||
import XMonad.Hooks.EwmhDesktops
|
||||
import XMonad.Prompt
|
||||
import XMonad.Actions.SpawnOn
|
||||
@@ -19,38 +20,39 @@ import XMonad.Layout.TwoPane
|
||||
|
||||
import qualified Data.Map as M
|
||||
|
||||
sjanssenConfigXmobar = statusBar "xmobar" sjanssenPP strutkey =<< sjanssenConfig
|
||||
sjanssenConfigXmobar = statusBar "exec xmobar" sjanssenPP strutkey =<< sjanssenConfig
|
||||
where
|
||||
strutkey (XConfig {modMask = modm}) = (modm, xK_b)
|
||||
|
||||
sjanssenConfig = do
|
||||
sp <- mkSpawner
|
||||
return $ defaultConfig
|
||||
{ terminal = "urxvtc"
|
||||
return . ewmh $ defaultConfig
|
||||
{ terminal = "exec urxvt"
|
||||
, workspaces = ["irc", "web"] ++ map show [3 .. 9 :: Int]
|
||||
, mouseBindings = \(XConfig {modMask = modm}) -> M.fromList $
|
||||
[ ((modm, button1), (\w -> focus w >> mouseMoveWindow w))
|
||||
, ((modm, button2), (\w -> focus w >> windows W.swapMaster))
|
||||
, ((modm.|. shiftMask, button1), (\w -> focus w >> mouseResizeWindow w)) ]
|
||||
, keys = \c -> mykeys sp c `M.union` keys defaultConfig c
|
||||
, layoutHook = modifiers layouts
|
||||
, logHook = ewmhDesktopsLogHook
|
||||
, manageHook = composeAll [className =? x --> doF (W.shift w)
|
||||
, layoutHook = modifiers layouts
|
||||
, manageHook = composeAll [className =? x --> doShift w
|
||||
| (x, w) <- [ ("Firefox", "web")
|
||||
, ("Ktorrent", "7")
|
||||
, ("Amarokapp", "7")]]
|
||||
<+> manageHook defaultConfig <+> manageDocks <+> manageSpawn sp
|
||||
<+> manageHook defaultConfig <+> manageDocks <+> manageSpawn sp
|
||||
<+> (isFullscreen --> doFullFloat)
|
||||
}
|
||||
where
|
||||
tiled = HintedTile 1 0.03 0.5 TopLeft
|
||||
layouts = (tiled Tall ||| (tiled Wide ||| Full)) ||| tabbed shrinkText myTheme
|
||||
modifiers = smartBorders
|
||||
|
||||
mykeys sp (XConfig {modMask = modm, workspaces = ws}) = M.fromList $
|
||||
mykeys sp (XConfig {modMask = modm}) = M.fromList $
|
||||
[((modm, xK_p ), shellPromptHere sp myPromptConfig)
|
||||
,((modm .|. shiftMask, xK_Return), spawnHere sp =<< asks (terminal . config))
|
||||
,((modm .|. shiftMask, xK_c ), kill1)
|
||||
,((modm .|. shiftMask .|. controlMask, xK_c ), kill)
|
||||
,((modm .|. shiftMask, xK_0 ), windows $ \w -> foldr copy w ws)
|
||||
,((modm .|. shiftMask, xK_0 ), windows $ copyToAll)
|
||||
,((modm, xK_z ), layoutScreens 2 $ TwoPane 0.5 0.5)
|
||||
,((modm .|. shiftMask, xK_z ), rescreen)
|
||||
]
|
||||
|
@@ -6,7 +6,9 @@
|
||||
-- Copyright : (c) Ivan Miljenovic <Ivan.Miljenovic@gmail.com>
|
||||
-- License : BSD
|
||||
--
|
||||
-- Maintainer : Ivan Miljenovic <Ivan.Miljenovic@gmail.com>
|
||||
-- Maintainer : none
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- This module provides a config suitable for use with the Xfce desktop
|
||||
-- environment.
|
||||
@@ -29,7 +31,8 @@ import qualified Data.Map as M
|
||||
-- > import XMonad.Config.Xfce
|
||||
-- >
|
||||
-- > main = xmonad xfceConfig
|
||||
--
|
||||
--
|
||||
-- For examples of how to further customize @xfceConfig@ see "XMonad.Config.Desktop".
|
||||
|
||||
xfceConfig = desktopConfig
|
||||
{ terminal = "Terminal"
|
||||
|
@@ -49,16 +49,25 @@ if it does exist, xmonad will use whatever settings you specify. Note
|
||||
that this file can contain arbitrary Haskell code, which means that
|
||||
you have quite a lot of flexibility in configuring xmonad.
|
||||
|
||||
NOTE for users of previous versions (< 0.5) of xmonad: this is a major
|
||||
change in the way xmonad is configured. Prior to version 0.5,
|
||||
configuring xmonad required editing an xmonad source file called
|
||||
Config.hs, recompiling xmonad, and then restarting. From version 0.5
|
||||
onwards, however, you should NOT edit this file. All you have to do
|
||||
is edit xmonad.hs and restart with @mod-q@; xmonad does the
|
||||
recompiling itself. The format of the configuration file has also
|
||||
changed; it is now simpler and much shorter, only requiring you to
|
||||
list those settings which are different from the defaults.
|
||||
HISTORICAL NOTE regarding upgrading from versions (< 0.5) of xmonad
|
||||
or using old documentation:
|
||||
|
||||
xmonad-0.5 delivered a major change in the way xmonad is configured. Prior
|
||||
to version 0.5, configuring xmonad required editing a source file called
|
||||
Config.hs, manually recompiling xmonad, and then restarting. From
|
||||
version 0.5 onwards, however, you should NOT edit this file or manually
|
||||
compile with ghc --make. All you have to do is edit xmonad.hs and restart
|
||||
with @mod-q@; xmonad does the recompiling itself. The format of the
|
||||
configuration file also changed with version 0.5; enabling simpler and
|
||||
much shorter xmonad.hs files that only require listing those settings which
|
||||
are different from the defaults.
|
||||
|
||||
While the complicated template.hs (man/xmonad.hs) files listing all default
|
||||
settings are still provided for reference, once you wish to make substantial
|
||||
changes to your configuration, the template.hs style configuration is not
|
||||
recommended. It is fine to use top-level definitions to organize your
|
||||
xmonad.hs, but wherever possible it is better to leave out settings that
|
||||
simply duplicate defaults.
|
||||
-}
|
||||
|
||||
{- $example
|
||||
@@ -90,8 +99,8 @@ describe values that differ from the defaults.
|
||||
|
||||
As an alternative, you can copy the template @xmonad.hs@ file (found
|
||||
either in the @man@ directory, if you have the xmonad source, or on
|
||||
the xmonad wiki at
|
||||
@http:\/\/haskell.org\/haskellwiki\/Xmonad\/Config_archive\/Template_xmonad.hs@)
|
||||
the xmonad wiki config archive at
|
||||
<http://haskell.org/haskellwiki/Xmonad/Config_archive>)
|
||||
into your @~\/.xmonad\/@ directory. This template file contains all
|
||||
the default settings spelled out, and you should be able to simply
|
||||
change the ones you would like to change.
|
||||
@@ -106,18 +115,14 @@ be found in "XMonad.Core".
|
||||
#Checking_whether_your_xmonad.hs_is_correct#
|
||||
|
||||
After changing your configuration, it is a good idea to check that it
|
||||
is syntactically and type correct. You can do this easily by loading
|
||||
your configuration file in the Haskell interpreter:
|
||||
is syntactically and type correct. You can do this easily by using an xmonad
|
||||
flag:
|
||||
|
||||
> $ ghci ~/.xmonad/xmonad.hs
|
||||
> GHCi, version 6.8.2: http://www.haskell.org/ghc/ :? for help
|
||||
> Loading package base ... linking ... done.
|
||||
> Ok, modules loaded: Main.
|
||||
>
|
||||
> Prelude Main> :t main
|
||||
> main :: IO ()
|
||||
> $ xmonad --recompile
|
||||
> $
|
||||
|
||||
Ok, looks good.
|
||||
If there is no output, your xmonad.hs has no errors. If there are errors, they
|
||||
will be printed to the console. Patch them up and try again.
|
||||
|
||||
Note, however, that if you skip this step and try restarting xmonad
|
||||
with errors in your xmonad.hs, it's not the end of the world; xmonad
|
||||
@@ -139,15 +144,8 @@ all your windows, layouts, etc. intact. (If you change anything
|
||||
related to your layouts, you may need to hit @mod-shift-space@ after
|
||||
restarting to see the changes take effect.) If something goes wrong,
|
||||
the previous (default) settings will be used. Note this requires that
|
||||
GHC and xmonad are in your @$PATH@. If GHC isn't in your path, you can
|
||||
still compile @xmonad.hs@ yourself:
|
||||
|
||||
> $ cd ~/.xmonad
|
||||
> $ /path/to/ghc --make xmonad.hs
|
||||
> $ ls
|
||||
> xmonad xmonad.hi xmonad.hs xmonad.o
|
||||
|
||||
When you hit @mod-q@, this newly compiled xmonad will be used.
|
||||
GHC and xmonad are in the @$PATH@ in the environment from which xmonad
|
||||
is started.
|
||||
|
||||
-}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -53,20 +53,20 @@ import Data.IORef
|
||||
-- You must include this @dynHooksRef@ value when using the functions in this
|
||||
-- module:
|
||||
--
|
||||
-- > xmonad { keys = myKeys `Data.Map.union` Data.Map.fromList
|
||||
-- > [((modMask conf, xK_i), oneShotHook dynHooksRef
|
||||
-- > "FFlaunchHook" (className =? "firefox") (doShift "3")
|
||||
-- > xmonad { keys = myKeys `Data.Map.union` Data.Map.fromList
|
||||
-- > [((modm, xK_i), oneShotHook dynHooksRef
|
||||
-- > "FFlaunchHook" (className =? "firefox") (doShift "3")
|
||||
-- > >> spawn "firefox")
|
||||
-- > ,((modMask conf, xK_u), addDynamicHook dynHooksRef
|
||||
-- > ,((modm, xK_u), addDynamicHook dynHooksRef
|
||||
-- > (className =? "example" --> doFloat))
|
||||
-- > ,((modMask conf, xK_y), updatePermanentHook dynHooksRef
|
||||
-- > ,((modm, xK_y), updatePermanentHook dynHooksRef
|
||||
-- > (const idHook))) ] -- resets the permanent hook.
|
||||
--
|
||||
|
||||
data DynamicHooks = DynamicHooks
|
||||
{ transients :: [(Query Bool, ManageHook)]
|
||||
, permanent :: ManageHook }
|
||||
|
||||
|
||||
|
||||
-- | Creates the 'IORef' that stores the dynamically created 'ManageHook's.
|
||||
initDynamicHooks :: IO (IORef DynamicHooks)
|
||||
@@ -80,7 +80,7 @@ initDynamicHooks = newIORef (DynamicHooks { transients = [],
|
||||
-- doFloat and doIgnore are idempotent.
|
||||
-- | Master 'ManageHook' that must be in your @xmonad.hs@ 'ManageHook'.
|
||||
dynamicMasterHook :: IORef DynamicHooks -> ManageHook
|
||||
dynamicMasterHook ref = return True -->
|
||||
dynamicMasterHook ref = return True -->
|
||||
(ask >>= \w -> liftX (do
|
||||
dh <- io $ readIORef ref
|
||||
(Endo f) <- runQuery (permanent dh) w
|
||||
@@ -99,7 +99,7 @@ addDynamicHook ref m = updateDynamicHook ref (<+> m)
|
||||
|
||||
-- | Modifies the permanent 'ManageHook' with an arbitrary function.
|
||||
updateDynamicHook :: IORef DynamicHooks -> (ManageHook -> ManageHook) -> X ()
|
||||
updateDynamicHook ref f =
|
||||
updateDynamicHook ref f =
|
||||
io $ modifyIORef ref $ \dh -> dh { permanent = f (permanent dh) }
|
||||
|
||||
|
||||
@@ -108,10 +108,10 @@ updateDynamicHook ref f =
|
||||
--
|
||||
-- > className =? "example" --> doFloat
|
||||
--
|
||||
-- you must call 'oneShotHook' as
|
||||
-- you must call 'oneShotHook' as
|
||||
--
|
||||
-- > oneShotHook dynHooksRef (className =? "example) doFloat
|
||||
--
|
||||
--
|
||||
oneShotHook :: IORef DynamicHooks -> Query Bool -> ManageHook -> X ()
|
||||
oneShotHook ref q a =
|
||||
io $ modifyIORef ref
|
||||
|
@@ -38,8 +38,9 @@ module XMonad.Hooks.DynamicLog (
|
||||
dzenPP, xmobarPP, sjanssenPP, byorgeyPP,
|
||||
|
||||
-- * Formatting utilities
|
||||
wrap, pad, shorten,
|
||||
xmobarColor, dzenColor, dzenEscape,
|
||||
wrap, pad, trim, shorten,
|
||||
xmobarColor, xmobarStrip,
|
||||
dzenColor, dzenEscape, dzenStrip,
|
||||
|
||||
-- * Internal formatting functions
|
||||
pprWindowSet,
|
||||
@@ -55,6 +56,7 @@ module XMonad.Hooks.DynamicLog (
|
||||
--
|
||||
import XMonad
|
||||
import Control.Monad
|
||||
import Data.Char ( isSpace )
|
||||
import Data.Maybe ( isJust, catMaybes )
|
||||
import Data.List
|
||||
import qualified Data.Map as M
|
||||
@@ -302,6 +304,11 @@ wrap l r m = l ++ m ++ r
|
||||
pad :: String -> String
|
||||
pad = wrap " " " "
|
||||
|
||||
-- | Trim leading and trailing whitespace from a string.
|
||||
trim :: String -> String
|
||||
trim = f . f
|
||||
where f = reverse . dropWhile isSpace
|
||||
|
||||
-- | Limit a string to a certain length, adding "..." if truncated.
|
||||
shorten :: Int -> String -> String
|
||||
shorten n xs | length xs < n = xs
|
||||
@@ -332,6 +339,20 @@ dzenColor fg bg = wrap (fg1++bg1) (fg2++bg2)
|
||||
dzenEscape :: String -> String
|
||||
dzenEscape = concatMap (\x -> if x == '^' then "^^" else [x])
|
||||
|
||||
-- | Strip dzen formatting or commands. Useful to remove ppHidden
|
||||
-- formatting in ppUrgent field. For example:
|
||||
--
|
||||
-- > , ppHidden = dzenColor "gray20" "" . wrap "(" ")"
|
||||
-- > , ppUrgent = dzenColor "dark orange" "" . dzenStrip
|
||||
dzenStrip :: String -> String
|
||||
dzenStrip = strip [] where
|
||||
strip keep x
|
||||
| null x = keep
|
||||
| "^^" `isPrefixOf` x = strip (keep ++ "^") (drop 2 x)
|
||||
| '^' == head x = strip keep (drop 1 . dropWhile (/= ')') $ x)
|
||||
| otherwise = let (good,x') = span (/= '^') x
|
||||
in strip (keep ++ good) x'
|
||||
|
||||
-- | Use xmobar escape codes to output a string with given foreground
|
||||
-- and background colors.
|
||||
xmobarColor :: String -- ^ foreground color: a color name, or #rrggbb format
|
||||
@@ -343,6 +364,21 @@ xmobarColor fg bg = wrap t "</fc>"
|
||||
|
||||
-- ??? add an xmobarEscape function?
|
||||
|
||||
-- | Strip xmobar markup. Useful to remove ppHidden color from ppUrgent
|
||||
-- field. For example:
|
||||
--
|
||||
-- > , ppHidden = xmobarColor "gray20" "" . wrap "<" ">"
|
||||
-- > , ppUrgent = xmobarColor "dark orange" "" . xmobarStrip
|
||||
xmobarStrip :: String -> String
|
||||
xmobarStrip = strip [] where
|
||||
strip keep x
|
||||
| null x = keep
|
||||
| "<fc=" `isPrefixOf` x = strip keep (drop 1 . dropWhile (/= '>') $ x)
|
||||
| "</fc>" `isPrefixOf` x = strip keep (drop 5 x)
|
||||
| '<' == head x = strip (keep ++ "<") (tail x)
|
||||
| otherwise = let (good,x') = span (/= '<') x
|
||||
in strip (keep ++ good) x'
|
||||
|
||||
-- | The 'PP' type allows the user to customize the formatting of
|
||||
-- status information.
|
||||
data PP = PP { ppCurrent :: WorkspaceId -> String
|
||||
@@ -415,13 +451,14 @@ defaultPP = PP { ppCurrent = wrap "[" "]"
|
||||
, ppExtras = []
|
||||
}
|
||||
|
||||
-- | Settings to emulate dwm's statusbar, dzen only.
|
||||
-- | Settings to emulate dwm's statusbar, dzen only. Uses dzenStrip in
|
||||
-- ppUrgent.
|
||||
dzenPP :: PP
|
||||
dzenPP = defaultPP { ppCurrent = dzenColor "white" "#2b4f98" . pad
|
||||
, ppVisible = dzenColor "black" "#999999" . pad
|
||||
, ppHidden = dzenColor "black" "#cccccc" . pad
|
||||
, ppHiddenNoWindows = const ""
|
||||
, ppUrgent = dzenColor "red" "yellow"
|
||||
, ppUrgent = dzenColor "red" "yellow" . dzenStrip
|
||||
, ppWsSep = ""
|
||||
, ppSep = ""
|
||||
, ppLayout = dzenColor "black" "#cccccc" .
|
||||
@@ -439,6 +476,7 @@ xmobarPP :: PP
|
||||
xmobarPP = defaultPP { ppCurrent = xmobarColor "yellow" "" . wrap "[" "]"
|
||||
, ppTitle = xmobarColor "green" "" . shorten 40
|
||||
, ppVisible = wrap "(" ")"
|
||||
, ppUrgent = xmobarColor "red" "yellow"
|
||||
}
|
||||
|
||||
-- | The options that sjanssen likes to use with xmobar, as an
|
||||
|
@@ -1,107 +0,0 @@
|
||||
{-# OPTIONS_GHC -fglasgow-exts #-} -- for deriving Typeable
|
||||
{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, PatternGuards #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Hooks.EventHook
|
||||
-- Copyright : (c) 2007 Andrea Rossato
|
||||
-- License : BSD-style (see xmonad/LICENSE)
|
||||
--
|
||||
-- Maintainer : andrea.rossato@unibz.it
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- A layout modifier that implements an event hook at the layout level.
|
||||
--
|
||||
-- Since it operates at the 'Workspace' level, it will install itself
|
||||
-- on the first current 'Workspace' and will broadcast a 'Message' to
|
||||
-- all other 'Workspace's not to handle events.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Hooks.EventHook
|
||||
( -- * Usage
|
||||
-- $usage
|
||||
|
||||
-- * Writing a hook
|
||||
-- $hook
|
||||
EventHook (..)
|
||||
, eventHook
|
||||
, HandleEvent
|
||||
) where
|
||||
|
||||
import Data.Maybe
|
||||
|
||||
import XMonad
|
||||
import XMonad.StackSet (Workspace (..), currentTag)
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your
|
||||
-- @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Hooks.EventHook
|
||||
--
|
||||
-- Then edit your @layoutHook@ by adding the 'eventHook':
|
||||
--
|
||||
-- > layoutHook = eventHook EventHookExample $ avoidStruts $ simpleTabbed ||| Full ||| etc..
|
||||
--
|
||||
-- and then:
|
||||
--
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayouts }
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
-- "XMonad.Doc.Extending#Editing_the_layout_hook"
|
||||
|
||||
-- $hook
|
||||
-- Writing a hook is very simple.
|
||||
--
|
||||
-- This is a basic example to log all events:
|
||||
--
|
||||
-- > data EventHookExample = EventHookExample deriving ( Show, Read )
|
||||
-- > instance EventHook EventHookExample where
|
||||
-- > handleEvent _ e = io $ hPutStrLn stderr . show $ e --return ()
|
||||
--
|
||||
-- This is an 'EventHook' to log mouse button events:
|
||||
--
|
||||
-- > data EventHookButton = EventHookButton deriving ( Show, Read )
|
||||
-- > instance EventHook EventHookButton where
|
||||
-- > handleEvent _ (ButtonEvent {ev_window = w}) = do
|
||||
-- > io $ hPutStrLn stderr $ "This is a button event on window " ++ (show w)
|
||||
-- > handleEvent _ _ = return ()
|
||||
--
|
||||
-- Obviously you can compose event hooks:
|
||||
--
|
||||
-- > layoutHook = eventHook EventHookButton $ eventHook EventHookExample $ avoidStruts $ simpleTabbed ||| Full ||| etc..
|
||||
|
||||
eventHook :: EventHook eh => eh -> l a -> (HandleEvent eh l) a
|
||||
eventHook = HandleEvent Nothing True
|
||||
|
||||
class (Read eh, Show eh) => EventHook eh where
|
||||
handleEvent :: eh -> Event -> X ()
|
||||
handleEvent _ _ = return ()
|
||||
|
||||
data HandleEvent eh l a = HandleEvent (Maybe WorkspaceId) Bool eh (l a) deriving ( Show, Read )
|
||||
|
||||
data EventHandleMsg = HandlerOff deriving ( Typeable )
|
||||
instance Message EventHandleMsg
|
||||
|
||||
instance (EventHook eh, LayoutClass l a) => LayoutClass (HandleEvent eh l) a where
|
||||
runLayout (Workspace i (HandleEvent Nothing True eh l) ms) r = do
|
||||
broadcastMessage HandlerOff
|
||||
iws <- gets (currentTag . windowset)
|
||||
(wrs, ml) <- runLayout (Workspace i l ms) r
|
||||
return (wrs, Just $ HandleEvent (Just iws) True eh (fromMaybe l ml))
|
||||
|
||||
runLayout (Workspace i (HandleEvent mi b eh l) ms) r = do
|
||||
(wrs, ml) <- runLayout (Workspace i l ms) r
|
||||
return (wrs, Just $ HandleEvent mi b eh (fromMaybe l ml))
|
||||
|
||||
handleMessage (HandleEvent i True eh l) m
|
||||
| Just HandlerOff <- fromMessage m = return . Just $ HandleEvent i False eh l
|
||||
| Just e <- fromMessage m = handleMessage l (SomeMessage e) >>= \ml ->
|
||||
handleEvent eh e >>
|
||||
maybe (return Nothing) (\l' -> return . Just $ HandleEvent i True eh l') ml
|
||||
handleMessage (HandleEvent i b eh l) m = handleMessage l m >>=
|
||||
maybe (return Nothing) (\l' -> return . Just $ HandleEvent i b eh l')
|
||||
|
||||
description (HandleEvent _ _ _ l) = description l
|
@@ -15,14 +15,17 @@
|
||||
module XMonad.Hooks.EwmhDesktops (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
EwmhDesktopsHook,
|
||||
ewmh,
|
||||
ewmhDesktopsStartup,
|
||||
ewmhDesktopsLogHook,
|
||||
ewmhDesktopsLogHookCustom,
|
||||
ewmhDesktopsLayout
|
||||
ewmhDesktopsEventHook
|
||||
) where
|
||||
|
||||
import Codec.Binary.UTF8.String (encode)
|
||||
import Data.List
|
||||
import Data.Maybe
|
||||
import Data.Monoid
|
||||
|
||||
import XMonad
|
||||
import Control.Monad
|
||||
@@ -30,7 +33,6 @@ import qualified XMonad.StackSet as W
|
||||
|
||||
import XMonad.Hooks.SetWMName
|
||||
import XMonad.Util.WorkspaceCompare
|
||||
import XMonad.Hooks.EventHook
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
@@ -38,34 +40,29 @@ import XMonad.Hooks.EventHook
|
||||
-- > import XMonad
|
||||
-- > import XMonad.Hooks.EwmhDesktops
|
||||
-- >
|
||||
-- > myLogHook :: X ()
|
||||
-- > myLogHook = ewmhDesktopsLogHook
|
||||
-- >
|
||||
-- > myLayoutHook = ewmhDesktopsLayout $ avoidStruts $ layoutHook defaultConfig
|
||||
-- >
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayouts, logHook = myLogHook }
|
||||
-- > main = xmonad $ ewmh defaultConfig
|
||||
--
|
||||
-- 'avoidStruts' is used to automatically leave space for dock programs, and
|
||||
-- can be found in 'XMonad.Hooks.ManageDocks'.
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
-- "XMonad.Doc.Extending#The_log_hook_and_external_status_bars"
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
-- "XMonad.Doc.Extending#Editing_the_layout_hook"
|
||||
-- You may also be interested in 'avoidStruts' from XMonad.Hooks.ManageDocks.
|
||||
|
||||
|
||||
-- | Add EWMH functionality to the given config. See above for an example.
|
||||
ewmh :: XConfig a -> XConfig a
|
||||
ewmh c = c { startupHook = startupHook c +++ ewmhDesktopsStartup
|
||||
, handleEventHook = handleEventHook c +++ ewmhDesktopsEventHook
|
||||
, logHook = logHook c +++ ewmhDesktopsLogHook }
|
||||
where x +++ y = mappend x y
|
||||
|
||||
-- |
|
||||
-- Initializes EwmhDesktops and advertises EWMH support to the X
|
||||
-- server
|
||||
ewmhDesktopsStartup :: X ()
|
||||
ewmhDesktopsStartup = setSupported
|
||||
|
||||
-- |
|
||||
-- Notifies pagers and window lists, such as those in the gnome-panel
|
||||
-- of the current state of workspaces and windows.
|
||||
ewmhDesktopsLogHook :: X ()
|
||||
ewmhDesktopsLogHook = ewmhDesktopsLogHookCustom id
|
||||
|
||||
|
||||
-- |
|
||||
-- Generalized version of ewmhDesktopsLogHook that allows an arbitrary
|
||||
-- user-specified function to transform the workspace list (post-sorting)
|
||||
@@ -74,8 +71,6 @@ ewmhDesktopsLogHookCustom f = withWindowSet $ \s -> do
|
||||
sort' <- getSortByIndex
|
||||
let ws = f $ sort' $ W.workspaces s
|
||||
|
||||
setSupported
|
||||
|
||||
-- Number of Workspaces
|
||||
setNumberOfDesktops (length ws)
|
||||
|
||||
@@ -100,7 +95,7 @@ ewmhDesktopsLogHookCustom f = withWindowSet $ \s -> do
|
||||
setWindowDesktop win curr
|
||||
|
||||
forM_ (W.hidden s) $ \w ->
|
||||
case elemIndex (W.tag w) (map W.tag ws) of
|
||||
case elemIndex (W.tag w) (map W.tag ws) of
|
||||
Nothing -> return ()
|
||||
Just wn -> forM_ (W.integrate' (W.stack w)) $ \win -> do
|
||||
setWindowDesktop win wn
|
||||
@@ -119,13 +114,8 @@ ewmhDesktopsLogHookCustom f = withWindowSet $ \s -> do
|
||||
--
|
||||
-- * _NET_ACTIVE_WINDOW (activate another window, changing workspace if needed)
|
||||
--
|
||||
ewmhDesktopsLayout :: layout a -> HandleEvent EwmhDesktopsHook layout a
|
||||
ewmhDesktopsLayout = eventHook EwmhDesktopsHook
|
||||
|
||||
data EwmhDesktopsHook = EwmhDesktopsHook deriving ( Show, Read )
|
||||
instance EventHook EwmhDesktopsHook where
|
||||
handleEvent _ e@ClientMessageEvent {} = do handle e
|
||||
handleEvent _ _ = return ()
|
||||
ewmhDesktopsEventHook :: Event -> X All
|
||||
ewmhDesktopsEventHook e = handle e >> return (All True)
|
||||
|
||||
handle :: Event -> X ()
|
||||
handle ClientMessageEvent {
|
||||
@@ -156,9 +146,12 @@ handle ClientMessageEvent {
|
||||
else if mt == a_cw then do
|
||||
killWindow w
|
||||
else if mt `elem` a_ignore then do
|
||||
return ()
|
||||
else trace $ "Unknown ClientMessageEvent " ++ show mt
|
||||
handle _ = undefined -- does not happen, as otherwise ewmhDesktopsHook would not match
|
||||
return ()
|
||||
else do
|
||||
-- The Message is unknown to us, but that is ok, not all are meant
|
||||
-- to be handled by the window manager
|
||||
return ()
|
||||
handle _ = return ()
|
||||
|
||||
|
||||
setNumberOfDesktops :: (Integral a) => a -> X ()
|
||||
@@ -181,8 +174,7 @@ setDesktopNames names = withDisplay $ \dpy -> do
|
||||
r <- asks theRoot
|
||||
a <- getAtom "_NET_DESKTOP_NAMES"
|
||||
c <- getAtom "UTF8_STRING"
|
||||
let names' = map (fromIntegral.fromEnum) $
|
||||
concatMap (++['\0']) names
|
||||
let names' = map fromIntegral $ concatMap ((++[0]) . encode) names
|
||||
io $ changeProperty8 dpy r a c propModeReplace names'
|
||||
|
||||
setClientList :: [Window] -> X ()
|
||||
|
@@ -16,12 +16,17 @@ module XMonad.Hooks.FadeInactive (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
setOpacity,
|
||||
fadeInactiveLogHook
|
||||
isUnfocused,
|
||||
fadeIn,
|
||||
fadeOut,
|
||||
fadeIf,
|
||||
fadeInactiveLogHook,
|
||||
fadeOutLogHook
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
import Control.Monad (forM_)
|
||||
import Control.Monad
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
@@ -31,11 +36,11 @@ import Control.Monad (forM_)
|
||||
-- >
|
||||
-- > myLogHook :: X ()
|
||||
-- > myLogHook = fadeInactiveLogHook fadeAmount
|
||||
-- > where fadeAmount = 0xdddddddd
|
||||
-- > where fadeAmount = 0.8
|
||||
-- >
|
||||
-- > main = xmonad defaultConfig { logHook = myLogHook }
|
||||
--
|
||||
-- fadeAmount can be any integer
|
||||
-- fadeAmount can be any rational between 0 and 1.
|
||||
-- you will need to have xcompmgr <http://freedesktop.org/wiki/Software/xapps>
|
||||
-- or something similar for this to do anything
|
||||
--
|
||||
@@ -47,31 +52,43 @@ import Control.Monad (forM_)
|
||||
--
|
||||
-- "XMonad.Doc.Extending#Editing_the_layout_hook"
|
||||
|
||||
-- |
|
||||
-- sets the opacity of a window
|
||||
setOpacity :: Window -> Integer -> X ()
|
||||
-- | Converts a percentage to the format required for _NET_WM_WINDOW_OPACITY
|
||||
rationalToOpacity :: Integral a => Rational -> a
|
||||
rationalToOpacity perc
|
||||
| perc < 0 || perc > 1 = round perc -- to maintain backwards-compatability
|
||||
| otherwise = round $ perc * 0xffffffff
|
||||
|
||||
-- | sets the opacity of a window
|
||||
setOpacity :: Window -> Rational -> X ()
|
||||
setOpacity w t = withDisplay $ \dpy -> do
|
||||
a <- getAtom "_NET_WM_WINDOW_OPACITY"
|
||||
c <- getAtom "CARDINAL"
|
||||
io $ changeProperty32 dpy w a c propModeReplace [fromIntegral t]
|
||||
io $ changeProperty32 dpy w a c propModeReplace [rationalToOpacity t]
|
||||
|
||||
-- |
|
||||
-- fades a window out by setting the opacity
|
||||
fadeOut :: Integer -> Window -> X ()
|
||||
fadeOut amt = flip setOpacity amt
|
||||
-- | fades a window out by setting the opacity
|
||||
fadeOut :: Rational -> Window -> X ()
|
||||
fadeOut = flip setOpacity
|
||||
|
||||
-- |
|
||||
-- makes a window completely opaque
|
||||
-- | makes a window completely opaque
|
||||
fadeIn :: Window -> X ()
|
||||
fadeIn = flip setOpacity 0xffffffff
|
||||
fadeIn = fadeOut 1
|
||||
|
||||
-- |
|
||||
-- lowers the opacity of inactive windows to the specified amount
|
||||
fadeInactiveLogHook :: Integer -> X ()
|
||||
fadeInactiveLogHook amt = withWindowSet $ \s ->
|
||||
forM_ (visibleWins s) (fadeOut amt) >>
|
||||
withFocused fadeIn
|
||||
where
|
||||
visibleWins s = (maybe [] unfocused . W.stack . W.workspace) (W.current s) ++
|
||||
concatMap (W.integrate' . W.stack . W.workspace) (W.visible s)
|
||||
unfocused (W.Stack _ l r) = l ++ r
|
||||
-- | Fades a window by the specified amount if it satisfies the first query, otherwise
|
||||
-- makes it opaque.
|
||||
fadeIf :: Query Bool -> Rational -> Query Rational
|
||||
fadeIf qry amt = qry >>= \b -> return $ if b then amt else 1
|
||||
|
||||
-- | sets the opacity of inactive windows to the specified amount
|
||||
fadeInactiveLogHook :: Rational -> X ()
|
||||
fadeInactiveLogHook = fadeOutLogHook . fadeIf isUnfocused
|
||||
|
||||
-- | returns True if the window doesn't have the focus.
|
||||
isUnfocused :: Query Bool
|
||||
isUnfocused = ask >>= \w -> liftX . gets $ maybe True (w /=) . W.peek . windowset
|
||||
|
||||
-- | fades out every window by the amount returned by the query.
|
||||
fadeOutLogHook :: Query Rational -> X ()
|
||||
fadeOutLogHook qry = withWindowSet $ \s -> do
|
||||
let visibleWins = (W.integrate' . W.stack . W.workspace . W.current $ s) ++
|
||||
concatMap (W.integrate' . W.stack . W.workspace) (W.visible s)
|
||||
forM_ visibleWins $ liftM2 (=<<) setOpacity (runQuery qry)
|
||||
|
157
XMonad/Hooks/FloatNext.hs
Normal file
157
XMonad/Hooks/FloatNext.hs
Normal file
@@ -0,0 +1,157 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Hooks.FloatNext
|
||||
-- Copyright : Quentin Moser <quentin.moser@unifr.ch>
|
||||
-- License : BSD-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Quentin Moser <quentin.moser@unifr.ch>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Hook and keybindings for automatically sending the next
|
||||
-- spawned window(s) to the floating layer.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Hooks.FloatNext ( -- * Usage
|
||||
-- $usage
|
||||
|
||||
-- * The hook
|
||||
floatNextHook
|
||||
|
||||
-- * Actions
|
||||
, floatNext
|
||||
, toggleFloatNext
|
||||
, floatAllNew
|
||||
, toggleFloatAllNew
|
||||
|
||||
-- * Queries
|
||||
, willFloatNext
|
||||
, willFloatAllNew
|
||||
|
||||
-- * 'DynamicLog' utilities
|
||||
-- $pp
|
||||
, willFloatNextPP
|
||||
, willFloatAllNewPP
|
||||
, runLogHook ) where
|
||||
|
||||
import Prelude hiding (all)
|
||||
|
||||
import XMonad
|
||||
|
||||
import Control.Monad (join)
|
||||
import Control.Applicative ((<$>))
|
||||
import Control.Arrow (first, second)
|
||||
import Control.Concurrent.MVar
|
||||
import System.IO.Unsafe (unsafePerformIO)
|
||||
|
||||
|
||||
{- Helper functions -}
|
||||
|
||||
modifyMVar2 :: MVar a -> (a -> a) -> IO ()
|
||||
modifyMVar2 v f = modifyMVar_ v (return . f)
|
||||
|
||||
_set :: ((a -> a) -> (Bool, Bool) -> (Bool, Bool)) -> a -> X ()
|
||||
_set f b = io $ modifyMVar2 floatModeMVar (f $ const b)
|
||||
|
||||
_toggle :: ((Bool -> Bool) -> (Bool, Bool) -> (Bool, Bool)) -> X ()
|
||||
_toggle f = io $ modifyMVar2 floatModeMVar (f not)
|
||||
|
||||
_get :: ((Bool, Bool) -> a) -> X a
|
||||
_get f = io $ f <$> readMVar floatModeMVar
|
||||
|
||||
_pp :: ((Bool, Bool) -> Bool) -> String -> (String -> String) -> X (Maybe String)
|
||||
_pp f s st = _get f >>= \b -> if b then return $ Just $ st s else return Nothing
|
||||
|
||||
|
||||
{- The current state is kept here -}
|
||||
|
||||
floatModeMVar :: MVar (Bool, Bool)
|
||||
floatModeMVar = unsafePerformIO $ newMVar (False, False)
|
||||
|
||||
|
||||
-- $usage
|
||||
-- This module provides actions (that can be set as keybindings)
|
||||
-- to automatically send the next spawned window(s) to the floating
|
||||
-- layer.
|
||||
--
|
||||
-- You can use it by including the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Hooks.FloatNext
|
||||
--
|
||||
-- and adding 'floatNextHook' to your 'ManageHook':
|
||||
--
|
||||
-- > myManageHook = floatNextHook <+> manageHook defaultConfig
|
||||
--
|
||||
-- The 'floatNext' and 'toggleFloatNext' functions can be used in key
|
||||
-- bindings to float the next spawned window:
|
||||
--
|
||||
-- > , ((modm, xK_e), toggleFloatNext)
|
||||
--
|
||||
-- 'floatAllNew' and 'toggleFloatAllNew' are similar but float all
|
||||
-- spawned windows until disabled again.
|
||||
--
|
||||
-- > , ((modm, xK_r), toggleFloatAllNew)
|
||||
|
||||
|
||||
-- | This 'ManageHook' will selectively float windows as set
|
||||
-- by 'floatNext' and 'floatAllNew'.
|
||||
floatNextHook :: ManageHook
|
||||
floatNextHook = do (next, all) <- io $ takeMVar floatModeMVar
|
||||
io $ putMVar floatModeMVar (False, all)
|
||||
if next || all then doFloat else idHook
|
||||
|
||||
|
||||
-- | @floatNext True@ arranges for the next spawned window to be
|
||||
-- sent to the floating layer, @floatNext False@ cancels it.
|
||||
floatNext :: Bool -> X ()
|
||||
floatNext = _set first
|
||||
|
||||
toggleFloatNext :: X ()
|
||||
toggleFloatNext = _toggle first
|
||||
|
||||
-- | @floatAllNew True@ arranges for new windows to be
|
||||
-- sent to the floating layer, @floatAllNew False@ cancels it
|
||||
floatAllNew :: Bool -> X ()
|
||||
floatAllNew = _set second
|
||||
|
||||
toggleFloatAllNew :: X ()
|
||||
toggleFloatAllNew = _toggle second
|
||||
|
||||
|
||||
-- | Whether the next window will be set floating
|
||||
willFloatNext :: X Bool
|
||||
willFloatNext = _get fst
|
||||
|
||||
-- | Whether new windows will be set floating
|
||||
willFloatAllNew :: X Bool
|
||||
willFloatAllNew = _get snd
|
||||
|
||||
|
||||
-- $pp
|
||||
-- The following functions are used to display the current
|
||||
-- state of 'floatNext' and 'floatAllNew' in your
|
||||
-- 'XMonad.Hooks.DynamicLog.dynamicLogWithPP'.
|
||||
-- 'willFloatNextPP' and 'willFloatAllNewPP' should be added
|
||||
-- to the 'XMonad.Hooks.DynamicLog.ppExtras' field of your
|
||||
-- 'XMonad.Hooks.DynamicLog.PP'.
|
||||
--
|
||||
-- Use 'runLogHook' to refresh the output of your 'logHook', so
|
||||
-- that the effects of a 'floatNext'/... will be visible
|
||||
-- immediately:
|
||||
--
|
||||
-- > , ((modm, xK_e), toggleFloatNext >> runLogHook)
|
||||
--
|
||||
-- The @String -> String@ parameters to 'willFloatNextPP' and
|
||||
-- 'willFloatAllNewPP' will be applied to their output, you
|
||||
-- can use them to set the text color, etc., or you can just
|
||||
-- pass them 'id'.
|
||||
|
||||
willFloatNextPP :: (String -> String) -> X (Maybe String)
|
||||
willFloatNextPP = _pp fst "Next"
|
||||
|
||||
willFloatAllNewPP :: (String -> String) -> X (Maybe String)
|
||||
willFloatAllNewPP = _pp snd "All"
|
||||
|
||||
runLogHook :: X ()
|
||||
runLogHook = join $ asks $ logHook . config
|
74
XMonad/Hooks/InsertPosition.hs
Normal file
74
XMonad/Hooks/InsertPosition.hs
Normal file
@@ -0,0 +1,74 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Hooks.InsertPosition
|
||||
-- Copyright : (c) 2009 Adam Vogt
|
||||
-- License : BSD-style (see xmonad/LICENSE)
|
||||
--
|
||||
-- Maintainer : vogt.adam@gmail.com
|
||||
-- Stability : unstable
|
||||
-- Portability : portable
|
||||
--
|
||||
-- Configure where new windows should be added and which window should be
|
||||
-- focused.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Hooks.InsertPosition (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
insertPosition
|
||||
,Focus(..), Position(..)
|
||||
) where
|
||||
|
||||
import XMonad(ManageHook, MonadReader(ask))
|
||||
import qualified XMonad.StackSet as W
|
||||
import Control.Applicative((<$>))
|
||||
import Data.Maybe(fromMaybe)
|
||||
import Data.List(find)
|
||||
import Data.Monoid(Endo(Endo))
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Hooks.InsertPosition
|
||||
-- > xmonad defaultConfig { manageHook = insertPosition Master Newer <+> myManageHook }
|
||||
--
|
||||
-- You should you put the manageHooks that use 'doShift' to take effect
|
||||
-- /before/ 'insertPosition', so that the window order will be consistent.
|
||||
-- Because ManageHooks compose from right to left (like function composition
|
||||
-- '.'), this means that 'insertPosition' should be the leftmost ManageHook.
|
||||
|
||||
data Position = Master | End | Above | Below
|
||||
data Focus = Newer | Older
|
||||
|
||||
-- | insertPosition. A manage hook for placing new windows. XMonad's default is
|
||||
-- the same as using: @insertPosition Above Newer@.
|
||||
insertPosition :: Position -> Focus -> ManageHook
|
||||
insertPosition pos foc = Endo . g <$> ask
|
||||
where
|
||||
g w = viewingWs w (updateFocus w . ins w . W.delete w)
|
||||
ins w = (\f ws -> fromMaybe id (W.focusWindow <$> W.peek ws) $ f ws) $
|
||||
case pos of
|
||||
Master -> W.insertUp w . W.focusMaster
|
||||
End -> insertDown w . W.modify' focusLast'
|
||||
Above -> W.insertUp w
|
||||
Below -> insertDown w
|
||||
updateFocus =
|
||||
case foc of
|
||||
Older -> const id
|
||||
Newer -> W.focusWindow
|
||||
|
||||
-- | Modify the StackSet when the workspace containing w is focused
|
||||
viewingWs :: (Eq a, Eq s, Eq i, Show i) =>a-> (W.StackSet i l a s sd -> W.StackSet i l a s sd)-> W.StackSet i l a s sd-> W.StackSet i l a s sd
|
||||
viewingWs w f = do
|
||||
i <- W.tag . W.workspace . W.current
|
||||
ws <- find (elem w . W.integrate' . W.stack) . W.workspaces
|
||||
maybe id (fmap (W.view i . f) . W.view . W.tag) ws
|
||||
|
||||
-- | 'insertDown' and 'focusLast' belong in XMonad.StackSet?
|
||||
insertDown :: (Eq a) => a -> W.StackSet i l a s sd -> W.StackSet i l a s sd
|
||||
insertDown w = W.swapDown . W.insertUp w
|
||||
|
||||
focusLast' :: W.Stack a -> W.Stack a
|
||||
focusLast' st = let ws = W.integrate st
|
||||
in W.Stack (last ws) (tail $ reverse ws) []
|
@@ -1,6 +1,6 @@
|
||||
{-# LANGUAGE PatternGuards, FlexibleInstances, MultiParamTypeClasses #-}
|
||||
{-# OPTIONS -fglasgow-exts #-}
|
||||
-- deriving Typeable
|
||||
-- deriving Typeable for ghc-6.6 compatibility, which is retained in the core
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Hooks.ManageDocks
|
||||
@@ -18,7 +18,12 @@ module XMonad.Hooks.ManageDocks (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
manageDocks, checkDock, AvoidStruts, avoidStruts, avoidStrutsOn,
|
||||
ToggleStruts(..), Direction(..)
|
||||
ToggleStruts(..),
|
||||
SetStruts(..),
|
||||
module XMonad.Util.Types,
|
||||
|
||||
-- for XMonad.Actions.FloatSnap
|
||||
calcGap
|
||||
) where
|
||||
|
||||
|
||||
@@ -27,8 +32,10 @@ import XMonad
|
||||
import Foreign.C.Types (CLong)
|
||||
import Control.Monad
|
||||
import XMonad.Layout.LayoutModifier
|
||||
import XMonad.Util.Types
|
||||
import XMonad.Util.WindowProperties (getProp32s)
|
||||
|
||||
import Data.List (delete)
|
||||
import qualified Data.Set as S
|
||||
|
||||
-- $usage
|
||||
-- To use this module, add the following import to @~\/.xmonad\/xmonad.hs@:
|
||||
@@ -52,12 +59,12 @@ import Data.List (delete)
|
||||
-- 'AvoidStruts' also supports toggling the dock gaps; add a keybinding
|
||||
-- similar to:
|
||||
--
|
||||
-- > ,((modMask x, xK_b ), sendMessage ToggleStruts)
|
||||
-- > ,((modm, xK_b ), sendMessage ToggleStruts)
|
||||
--
|
||||
-- If you have multiple docks, you can toggle their gaps individually.
|
||||
-- For example, to toggle only the top gap:
|
||||
--
|
||||
-- > ,((modMask x .|. controlMask, xK_t), sendMessage $ ToggleStrut U)
|
||||
-- > ,((modm .|. controlMask, xK_t), sendMessage $ ToggleStrut U)
|
||||
--
|
||||
-- Similarly, you can use 'D', 'L', and 'R' to individually toggle
|
||||
-- gaps on the bottom, left, or right.
|
||||
@@ -79,18 +86,6 @@ import Data.List (delete)
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
--
|
||||
|
||||
-- | An enumeration of the four cardinal directions\/sides of the
|
||||
-- screen.
|
||||
--
|
||||
-- Ideally this would go in its own separate module in Util,
|
||||
-- but ManageDocks is angling for inclusion into the xmonad core,
|
||||
-- so keep the dependencies to a minimum.
|
||||
data Direction = U -- ^ Up\/top
|
||||
| D -- ^ Down\/bottom
|
||||
| R -- ^ Right
|
||||
| L -- ^ Left
|
||||
deriving ( Read, Show, Eq, Ord, Enum, Bounded )
|
||||
|
||||
-- | Detects if the given window is of type DOCK and if so, reveals
|
||||
-- it, but does not manage it. If the window has the STRUT property
|
||||
-- set, adjust the gap accordingly.
|
||||
@@ -100,10 +95,9 @@ manageDocks = checkDock --> doIgnore
|
||||
-- | Checks if a window is a DOCK or DESKTOP window
|
||||
checkDock :: Query Bool
|
||||
checkDock = ask >>= \w -> liftX $ do
|
||||
a <- getAtom "_NET_WM_WINDOW_TYPE"
|
||||
dock <- getAtom "_NET_WM_WINDOW_TYPE_DOCK"
|
||||
desk <- getAtom "_NET_WM_WINDOW_TYPE_DESKTOP"
|
||||
mbr <- getProp a w
|
||||
mbr <- getProp32s "_NET_WM_WINDOW_TYPE" w
|
||||
case mbr of
|
||||
Just [r] -> return $ elem (fromIntegral r) [dock, desk]
|
||||
_ -> return False
|
||||
@@ -111,12 +105,10 @@ checkDock = ask >>= \w -> liftX $ do
|
||||
-- | Gets the STRUT config, if present, in xmonad gap order
|
||||
getStrut :: Window -> X [Strut]
|
||||
getStrut w = do
|
||||
spa <- getAtom "_NET_WM_STRUT_PARTIAL"
|
||||
sa <- getAtom "_NET_WM_STRUT"
|
||||
msp <- getProp spa w
|
||||
msp <- getProp32s "_NET_WM_STRUT_PARTIAL" w
|
||||
case msp of
|
||||
Just sp -> return $ parseStrutPartial sp
|
||||
Nothing -> fmap (maybe [] parseStrut) $ getProp sa w
|
||||
Nothing -> fmap (maybe [] parseStrut) $ getProp32s "_NET_WM_STRUT" w
|
||||
where
|
||||
parseStrut xs@[_, _, _, _] = parseStrutPartial . take 12 $ xs ++ cycle [minBound, maxBound]
|
||||
parseStrut _ = []
|
||||
@@ -126,13 +118,9 @@ getStrut w = do
|
||||
[(L, l, ly1, ly2), (R, r, ry1, ry2), (U, t, tx1, tx2), (D, b, bx1, bx2)]
|
||||
parseStrutPartial _ = []
|
||||
|
||||
-- | Helper to read a property
|
||||
getProp :: Atom -> Window -> X (Maybe [CLong])
|
||||
getProp a w = withDisplay $ \dpy -> io $ getWindowProperty32 dpy a w
|
||||
|
||||
-- | Goes through the list of windows and find the gap so that all
|
||||
-- STRUT settings are satisfied.
|
||||
calcGap :: [Direction] -> X (Rectangle -> Rectangle)
|
||||
calcGap :: S.Set Direction2D -> X (Rectangle -> Rectangle)
|
||||
calcGap ss = withDisplay $ \dpy -> do
|
||||
rootw <- asks theRoot
|
||||
-- We don't keep track of dock like windows, so we find all of them here
|
||||
@@ -145,7 +133,7 @@ calcGap ss = withDisplay $ \dpy -> do
|
||||
wa <- io $ getWindowAttributes dpy rootw
|
||||
let screen = r2c $ Rectangle (fi $ wa_x wa) (fi $ wa_y wa) (fi $ wa_width wa) (fi $ wa_height wa)
|
||||
return $ \r -> c2r $ foldr (reduce screen) (r2c r) struts
|
||||
where careAbout (s,_,_,_) = s `elem` ss
|
||||
where careAbout (s,_,_,_) = s `S.member` ss
|
||||
|
||||
-- | Adjust layout automagically: don't cover up any docks, status
|
||||
-- bars, etc.
|
||||
@@ -156,39 +144,68 @@ avoidStruts = avoidStrutsOn [U,D,L,R]
|
||||
-- etc. on the indicated sides of the screen. Valid sides are U
|
||||
-- (top), D (bottom), R (right), or L (left).
|
||||
avoidStrutsOn :: LayoutClass l a =>
|
||||
[Direction]
|
||||
[Direction2D]
|
||||
-> l a
|
||||
-> ModifiedLayout AvoidStruts l a
|
||||
avoidStrutsOn ss = ModifiedLayout (AvoidStruts ss)
|
||||
avoidStrutsOn ss = ModifiedLayout $ AvoidStruts $ S.fromList ss
|
||||
|
||||
data AvoidStruts a = AvoidStruts [Direction] deriving ( Read, Show )
|
||||
data AvoidStruts a = AvoidStruts (S.Set Direction2D) deriving ( Read, Show )
|
||||
|
||||
-- | Message type which can be sent to an 'AvoidStruts' layout
|
||||
-- modifier to alter its behavior.
|
||||
data ToggleStruts = ToggleStruts
|
||||
| ToggleStrut Direction
|
||||
| ToggleStrut Direction2D
|
||||
deriving (Read,Show,Typeable)
|
||||
|
||||
instance Message ToggleStruts
|
||||
|
||||
-- | SetStruts is a message constructor used to set or unset specific struts,
|
||||
-- regardless of whether or not the struts were originally set. Here are some
|
||||
-- example bindings:
|
||||
--
|
||||
-- Show all gaps:
|
||||
--
|
||||
-- > ,((modm .|. shiftMask ,xK_b),sendMessage $ SetStruts [minBound .. maxBound] [])
|
||||
--
|
||||
-- Hide all gaps:
|
||||
--
|
||||
-- > ,((modm .|. controlMask,xK_b),sendMessage $ SetStruts [] [minBound .. maxBound])
|
||||
--
|
||||
-- Show only upper and left gaps:
|
||||
--
|
||||
-- > ,((modm .|. controlMask .|. shiftMask,xK_b),sendMessage $ SetStruts [U,L] [minBound .. maxBound])
|
||||
--
|
||||
-- Hide the bottom keeping whatever the other values were:
|
||||
--
|
||||
-- > ,((modm .|. controlMask .|. shiftMask,xK_g),sendMessage $ SetStruts [] [D])
|
||||
data SetStruts = SetStruts { addedStruts :: [Direction2D]
|
||||
, removedStruts :: [Direction2D] -- ^ These are removed from the currently set struts before 'addedStruts' are added.
|
||||
}
|
||||
deriving (Read,Show,Typeable)
|
||||
|
||||
instance Message SetStruts
|
||||
|
||||
instance LayoutModifier AvoidStruts a where
|
||||
modifyLayout (AvoidStruts ss) w r = do
|
||||
nr <- fmap ($ r) (calcGap ss)
|
||||
runLayout w nr
|
||||
|
||||
handleMess (AvoidStruts ss) m
|
||||
| Just ToggleStruts <- fromMessage m = return $ Just $ AvoidStruts (toggleAll ss)
|
||||
| Just (ToggleStrut s) <- fromMessage m = return $ Just $ AvoidStruts (toggleOne s ss)
|
||||
| otherwise = return Nothing
|
||||
where toggleAll [] = [U,D,L,R]
|
||||
toggleAll _ = []
|
||||
toggleOne x xs | x `elem` xs = delete x xs
|
||||
| otherwise = x : xs
|
||||
pureMess (AvoidStruts ss) m
|
||||
| Just ToggleStruts <- fromMessage m = Just $ AvoidStruts (toggleAll ss)
|
||||
| Just (ToggleStrut s) <- fromMessage m = Just $ AvoidStruts (toggleOne s ss)
|
||||
| Just (SetStruts n k) <- fromMessage m
|
||||
, let newSS = S.fromList n `S.union` (ss S.\\ S.fromList k)
|
||||
, newSS /= ss = Just $ AvoidStruts newSS
|
||||
| otherwise = Nothing
|
||||
where toggleAll x | S.null x = S.fromList [minBound .. maxBound]
|
||||
| otherwise = S.empty
|
||||
toggleOne x xs | x `S.member` xs = S.delete x xs
|
||||
| otherwise = x `S.insert` xs
|
||||
|
||||
|
||||
-- | (Direction, height\/width, initial pixel, final pixel).
|
||||
|
||||
type Strut = (Direction, CLong, CLong, CLong)
|
||||
type Strut = (Direction2D, CLong, CLong, CLong)
|
||||
|
||||
-- | (Initial x pixel, initial y pixel,
|
||||
-- final x pixel, final y pixel).
|
||||
|
@@ -28,6 +28,7 @@ module XMonad.Hooks.ManageHelpers (
|
||||
Side(..),
|
||||
composeOne,
|
||||
(-?>), (/=?), (<==?), (</=?), (-->>), (-?>>),
|
||||
isInProperty,
|
||||
isKDETrayWindow,
|
||||
isFullscreen,
|
||||
isDialog,
|
||||
@@ -41,11 +42,14 @@ module XMonad.Hooks.ManageHelpers (
|
||||
doFullFloat,
|
||||
doCenterFloat,
|
||||
doSideFloat,
|
||||
doFloatAt,
|
||||
doFloatDep,
|
||||
doHideIgnore
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad.Util.WindowProperties (getProp32s)
|
||||
|
||||
import Data.Maybe
|
||||
import Data.Monoid
|
||||
@@ -117,41 +121,32 @@ p -?>> f = do
|
||||
-- | A predicate to check whether a window is a KDE system tray icon.
|
||||
isKDETrayWindow :: Query Bool
|
||||
isKDETrayWindow = ask >>= \w -> liftX $ do
|
||||
dpy <- asks display
|
||||
kde_tray <- getAtom "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR"
|
||||
r <- io $ getWindowProperty32 dpy kde_tray w
|
||||
r <- getProp32s "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR" w
|
||||
return $ case r of
|
||||
Just [_] -> True
|
||||
_ -> False
|
||||
|
||||
-- | Helper to check if a window property contains certain value.
|
||||
isInProperty :: String -> String -> Query Bool
|
||||
isInProperty p v = ask >>= \w -> liftX $ do
|
||||
va <- getAtom v
|
||||
r <- getProp32s p w
|
||||
return $ case r of
|
||||
Just xs -> fromIntegral va `elem` xs
|
||||
_ -> False
|
||||
|
||||
-- | A predicate to check whether a window wants to fill the whole screen.
|
||||
-- See also 'doFullFloat'.
|
||||
isFullscreen :: Query Bool
|
||||
isFullscreen = ask >>= \w -> liftX $ do
|
||||
dpy <- asks display
|
||||
state <- getAtom "_NET_WM_STATE"
|
||||
full <- getAtom "_NET_WM_STATE_FULLSCREEN"
|
||||
r <- io $ getWindowProperty32 dpy state w
|
||||
return $ case r of
|
||||
Just xs -> fromIntegral full `elem` xs
|
||||
_ -> False
|
||||
isFullscreen = isInProperty "_NET_WM_STATE" "_NET_WM_STATE_FULLSCREEN"
|
||||
|
||||
-- | A predicate to check whether a window is a dialog.
|
||||
isDialog :: Query Bool
|
||||
isDialog = ask >>= \w -> liftX $ do
|
||||
dpy <- asks display
|
||||
w_type <- getAtom "_NET_WM_WINDOW_TYPE"
|
||||
w_dialog <- getAtom "_NET_WM_WINDOW_TYPE_DIALOG"
|
||||
r <- io $ getWindowProperty32 dpy w_type w
|
||||
return $ case r of
|
||||
Just xs -> fromIntegral w_dialog `elem` xs
|
||||
_ -> False
|
||||
isDialog = isInProperty "_NET_WM_WINDOW_TYPE" "_NET_WM_WINDOW_TYPE_DIALOG"
|
||||
|
||||
pid :: Query (Maybe ProcessID)
|
||||
pid = ask >>= \w -> liftX $ do
|
||||
dpy <- asks display
|
||||
a <- getAtom "_NET_WM_PID"
|
||||
p <- io $ getWindowProperty32 dpy a w
|
||||
p <- getProp32s "_NET_WM_PID" w
|
||||
return $ case p of
|
||||
Just [x] -> Just (fromIntegral x)
|
||||
_ -> Nothing
|
||||
@@ -192,19 +187,31 @@ doRectFloat r = ask >>= \w -> doF (W.float w r)
|
||||
doFullFloat :: ManageHook
|
||||
doFullFloat = doRectFloat $ W.RationalRect 0 0 1 1
|
||||
|
||||
-- | Floats a new window using a rectangle computed as a function of
|
||||
-- the rectangle that it would have used by default.
|
||||
doFloatDep :: (W.RationalRect -> W.RationalRect) -> ManageHook
|
||||
doFloatDep move = ask >>= \w -> doF . W.float w . move . snd =<< liftX (floatLocation w)
|
||||
|
||||
-- | Floats a new window with its original size, and its top left
|
||||
-- corner at a specific point on the screen (both coordinates should
|
||||
-- be in the range 0 to 1).
|
||||
doFloatAt :: Rational -> Rational -> ManageHook
|
||||
doFloatAt x y = doFloatDep move
|
||||
where
|
||||
move (W.RationalRect _ _ w h) = W.RationalRect x y w h
|
||||
|
||||
-- | Floats a new window with its original size on the specified side of a
|
||||
-- screen
|
||||
doSideFloat :: Side -> ManageHook
|
||||
doSideFloat side = ask >>= \w -> doF . W.float w . move . snd =<< liftX (floatLocation w)
|
||||
where
|
||||
doSideFloat side = doFloatDep move
|
||||
where
|
||||
move (W.RationalRect _ _ w h) = W.RationalRect cx cy w h
|
||||
where
|
||||
cx = if side `elem` [SC,C ,NC] then (1-w)/2
|
||||
else if side `elem` [SW,CW,NW] then 0
|
||||
else {- side `elem` [SE,CE,NE] -} 1-w
|
||||
cy = if side `elem` [CE,C ,CW] then (1-h)/2
|
||||
else if side `elem` [NE,NC,NW] then 0
|
||||
else {- side `elem` [SE,SC,SW] -} 1-h
|
||||
where cx = if side `elem` [SC,C ,NC] then (1-w)/2
|
||||
else if side `elem` [SW,CW,NW] then 0
|
||||
else {- side `elem` [SE,CE,NE] -} 1-w
|
||||
cy = if side `elem` [CE,C ,CW] then (1-h)/2
|
||||
else if side `elem` [NE,NC,NW] then 0
|
||||
else {- side `elem` [SE,SC,SW] -} 1-h
|
||||
|
||||
-- | Floats a new window with its original size, but centered.
|
||||
doCenterFloat :: ManageHook
|
||||
|
460
XMonad/Hooks/Place.hs
Normal file
460
XMonad/Hooks/Place.hs
Normal file
@@ -0,0 +1,460 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Hooks.Place
|
||||
-- Copyright : Quentin Moser <quentin.moser@unifr.ch>
|
||||
-- License : BSD-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Quentin Moser <quentin.moser@unifr.ch>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Automatic placement of floating windows.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Hooks.Place ( -- * Usage
|
||||
-- $usage
|
||||
|
||||
-- * Placement actions
|
||||
placeFocused
|
||||
, placeHook
|
||||
|
||||
-- * Placement policies
|
||||
-- $placements
|
||||
, Placement
|
||||
, smart
|
||||
, simpleSmart
|
||||
, fixed
|
||||
, underMouse
|
||||
, inBounds
|
||||
, withGaps
|
||||
|
||||
-- * Others
|
||||
, purePlaceWindow ) where
|
||||
|
||||
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as S
|
||||
|
||||
import XMonad.Layout.WindowArranger
|
||||
import XMonad.Actions.FloatKeys
|
||||
|
||||
import qualified Data.Map as M
|
||||
import Data.Ratio ((%))
|
||||
import Data.List (sortBy, minimumBy, partition)
|
||||
import Data.Maybe (maybe, fromMaybe, catMaybes)
|
||||
import Data.Monoid (Endo(..))
|
||||
import Control.Monad (guard, join)
|
||||
import Control.Monad.Trans (lift)
|
||||
|
||||
-- $usage
|
||||
-- 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.
|
||||
--
|
||||
-- You can use this module by including the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Hooks.Place
|
||||
--
|
||||
-- and adding 'placeHook' to your 'manageHook', for example:
|
||||
--
|
||||
-- > 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:
|
||||
--
|
||||
-- > , ((modm, xK_w), placeFocused simpleSmart)
|
||||
--
|
||||
-- Both 'placeHook' and 'placeFocused' take a 'Placement' parameter, which specifies
|
||||
-- the placement policy to use (smart, under the mouse, fixed position, etc.). See
|
||||
-- 'Placement' for a list of available policies.
|
||||
|
||||
|
||||
|
||||
{- Placement policies -}
|
||||
|
||||
-- $placements
|
||||
-- Placement policies determine how windows will be placed by 'placeFocused' and 'placeHook'.
|
||||
--
|
||||
-- A few examples:
|
||||
--
|
||||
-- * Basic smart placement
|
||||
--
|
||||
-- > myPlacement = simpleSmart
|
||||
--
|
||||
-- * Under the mouse (pointer at the top-left corner), but constrained
|
||||
-- inside of the screen area
|
||||
--
|
||||
-- > myPlacement = inBounds (underMouse (0, 0))
|
||||
--
|
||||
-- * Smart placement with a preference for putting windows near
|
||||
-- the center of the screen, and with 16px gaps at the top and bottom
|
||||
-- of the screen where no window will be placed
|
||||
--
|
||||
-- > myPlacement = withGaps (16,0,16,0) (smart (0.5,0.5))
|
||||
|
||||
|
||||
-- | The type of placement policies
|
||||
data Placement = Smart (Rational, Rational)
|
||||
| Fixed (Rational, Rational)
|
||||
| UnderMouse (Rational, Rational)
|
||||
| Bounds (Dimension, Dimension, Dimension, Dimension) Placement
|
||||
deriving (Show, Read, Eq)
|
||||
|
||||
|
||||
-- | Try to place windows with as little overlap as possible
|
||||
smart :: (Rational, Rational) -- ^ Where the window should be placed inside
|
||||
-- the available area. See 'fixed'.
|
||||
-> Placement
|
||||
smart = Smart
|
||||
|
||||
simpleSmart :: Placement
|
||||
simpleSmart = inBounds $ smart (0,0)
|
||||
|
||||
|
||||
-- | Place windows at a fixed position
|
||||
fixed :: (Rational, Rational) -- ^ Where windows should go.
|
||||
--
|
||||
-- * (0,0) -> top left of the screen
|
||||
--
|
||||
-- * (1,0) -> top right of the screen
|
||||
--
|
||||
-- * etc
|
||||
-> Placement
|
||||
fixed = Fixed
|
||||
|
||||
|
||||
-- | Place windows under the mouse
|
||||
underMouse :: (Rational, Rational) -- ^ Where the pointer should be relative to
|
||||
-- the window's frame; see 'fixed'.
|
||||
-> Placement
|
||||
underMouse = UnderMouse
|
||||
|
||||
|
||||
-- | Apply the given placement policy, constraining the
|
||||
-- placed windows inside the screen boundaries.
|
||||
inBounds :: Placement -> Placement
|
||||
inBounds = Bounds (0,0,0,0)
|
||||
|
||||
|
||||
-- | Same as 'inBounds', but allows specifying gaps along the screen's edges
|
||||
withGaps :: (Dimension, Dimension, Dimension, Dimension)
|
||||
-- ^ top, right, bottom and left gaps
|
||||
-> Placement -> Placement
|
||||
withGaps = Bounds
|
||||
|
||||
|
||||
|
||||
{- Placement functions -}
|
||||
|
||||
|
||||
-- | 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
|
||||
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'
|
||||
|
||||
|
||||
-- | Hook to automatically place windows when they are created.
|
||||
placeHook :: Placement -> ManageHook
|
||||
placeHook p = do window <- ask
|
||||
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.
|
||||
purePlaceWindow :: Placement -- ^ The placement strategy
|
||||
-> Rectangle -- ^ The screen
|
||||
-> [Rectangle] -- ^ The other visible windows
|
||||
-> (Position, Position) -- ^ The pointer's position.
|
||||
-> Rectangle -- ^ The window to be placed
|
||||
-> Rectangle
|
||||
purePlaceWindow (Bounds (t,r,b,l) p') (Rectangle sx sy sw sh) rs p w
|
||||
= let s' = (Rectangle (sx + fi l) (sy + fi t) (sw - l - r) (sh - t - b))
|
||||
in checkBounds s' $ purePlaceWindow p' s' rs p w
|
||||
|
||||
purePlaceWindow (Fixed ratios) s _ _ w = placeRatio ratios s w
|
||||
|
||||
purePlaceWindow (UnderMouse (rx, ry)) _ _ (px, py) (Rectangle _ _ w h)
|
||||
= Rectangle (px - truncate (rx * fi w)) (py - truncate (ry * fi h)) w h
|
||||
|
||||
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
|
||||
-- inside another,
|
||||
placeRatio :: (Rational, Rational) -> Rectangle -> Rectangle -> Rectangle
|
||||
placeRatio (rx, ry) (Rectangle x1 y1 w1 h1) (Rectangle _ _ w2 h2)
|
||||
= Rectangle (scale rx x1 (x1 + fi w1 - fi w2))
|
||||
(scale ry y1 (y1 + fi h1 - fi h2))
|
||||
w2 h2
|
||||
|
||||
|
||||
-- | Helper: Ensures its second parameter is contained inside the first
|
||||
-- by possibly moving it.
|
||||
checkBounds :: Rectangle -> Rectangle -> Rectangle
|
||||
checkBounds (Rectangle x1 y1 w1 h1) (Rectangle x2 y2 w2 h2)
|
||||
= Rectangle (max x1 (min (x1 + fi w1 - fi w2) x2))
|
||||
(max y1 (min (y1 + fi h1 - fi h2) y2))
|
||||
w2 h2
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{- Utilities -}
|
||||
|
||||
scale :: (RealFrac a, Integral b) => a -> b -> b -> b
|
||||
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 -}
|
||||
|
||||
stackContents :: Maybe (S.Stack w) -> [w]
|
||||
stackContents = maybe [] S.integrate
|
||||
|
||||
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
|
||||
= do d <- asks display
|
||||
(_, x, y, w, h, _, _) <- io $ getGeometry d window
|
||||
|
||||
-- We can't use the border width returned by
|
||||
-- getGeometry because it will be 0 if the
|
||||
-- window isn't mapped yet.
|
||||
b <- asks $ borderWidth . config
|
||||
|
||||
return $ Rectangle x y (w + 2*b) (h + 2*b)
|
||||
|
||||
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: window's rectangle,
|
||||
-- other windows' rectangles and pointer's coordinates.
|
||||
getNecessaryData :: Window
|
||||
-> S.Workspace WorkspaceId (Layout Window) Window
|
||||
-> [Window]
|
||||
-> X (Rectangle, [Rectangle], (Position, Position))
|
||||
getNecessaryData window ws floats
|
||||
= do r <- getWindowRectangle window
|
||||
|
||||
rs <- return (organizeClients ws window floats)
|
||||
>>= mapM getWindowRectangle
|
||||
|
||||
pointer <- getPointer window
|
||||
|
||||
return (r, rs, pointer)
|
||||
|
||||
|
||||
|
||||
|
||||
{- Smart placement algorithm -}
|
||||
|
||||
-- | Alternate representation for rectangles.
|
||||
data SmartRectangle a = SR
|
||||
{ sr_x0, sr_y0 :: a -- ^ Top left coordinates, inclusive
|
||||
, sr_x1, sr_y1 :: a -- ^ Bottom right coorsinates, exclusive
|
||||
} deriving (Show, Eq)
|
||||
|
||||
r2sr :: Rectangle -> SmartRectangle Position
|
||||
r2sr (Rectangle x y w h) = SR x y (x + fi w) (y + fi h)
|
||||
|
||||
sr2r :: SmartRectangle Position -> Rectangle
|
||||
sr2r (SR x0 y0 x1 y1) = Rectangle x0 y0 (fi $ x1 - x0) (fi $ y1 - y0)
|
||||
|
||||
width :: Num a => SmartRectangle a -> a
|
||||
width r = sr_x1 r - sr_x0 r
|
||||
|
||||
height :: Num a => SmartRectangle a -> a
|
||||
height r = sr_y1 r - sr_y0 r
|
||||
|
||||
isEmpty :: Real a => SmartRectangle a -> Bool
|
||||
isEmpty r = (width r <= 0) || (height r <= 0)
|
||||
|
||||
contains :: Real a => SmartRectangle a -> SmartRectangle a -> Bool
|
||||
contains r1 r2 = sr_x0 r1 <= sr_x0 r2
|
||||
&& sr_y0 r1 <= sr_y0 r2
|
||||
&& sr_x1 r1 >= sr_x1 r2
|
||||
&& sr_y1 r1 >= sr_y1 r2
|
||||
|
||||
|
||||
-- | Main placement function
|
||||
placeSmart :: (Rational, Rational) -- ^ point of the screen where windows
|
||||
-- should be placed first, if possible.
|
||||
-> Rectangle -- ^ screen
|
||||
-> [Rectangle] -- ^ other clients
|
||||
-> Dimension -- ^ width
|
||||
-> Dimension -- ^ height
|
||||
-> Rectangle
|
||||
placeSmart (rx, ry) s@(Rectangle sx sy sw sh) rs w h
|
||||
= let free = map sr2r $ findSpace (r2sr s) (map r2sr rs) (fi w) (fi h)
|
||||
in position free (scale rx sx (sx + fi sw - fi w))
|
||||
(scale ry sy (sy + fi sh - fi h))
|
||||
w h
|
||||
|
||||
-- | Second part of the algorithm:
|
||||
-- Chooses the best position in which to place a window,
|
||||
-- according to a list of free areas and an ideal position for
|
||||
-- the top-left corner.
|
||||
-- We can't use semi-open surfaces for this, so we go back to
|
||||
-- X11 Rectangles/Positions/etc instead.
|
||||
position :: [Rectangle] -- ^ Free areas
|
||||
-> Position -> Position -- ^ Ideal coordinates
|
||||
-> Dimension -> Dimension -- ^ Width and height of the window
|
||||
-> Rectangle
|
||||
position rs x y w h = minimumBy distanceOrder $ map closest rs
|
||||
where distanceOrder r1 r2
|
||||
= compare (distance (rect_x r1,rect_y r1) (x,y) :: Dimension)
|
||||
(distance (rect_x r2,rect_y r2) (x,y) :: Dimension)
|
||||
distance (x1,y1) (x2,y2) = truncate $ (sqrt :: Double -> Double)
|
||||
$ fi $ (x1 - x2)^(2::Int)
|
||||
+ (y1 - y2)^(2::Int)
|
||||
closest r = checkBounds r (Rectangle x y w h)
|
||||
|
||||
|
||||
-- | First part of the algorithm:
|
||||
-- Tries to find an area in which to place a new
|
||||
-- rectangle so that it overlaps as little as possible with
|
||||
-- other rectangles already present. The first rectangles in
|
||||
-- the list will be overlapped first.
|
||||
findSpace :: Real a =>
|
||||
SmartRectangle a -- ^ The total available area
|
||||
-> [SmartRectangle a] -- ^ The parts already in use
|
||||
-> a -- ^ Width of the rectangle to place
|
||||
-> a -- ^ Height of the rectangle to place
|
||||
-> [SmartRectangle a]
|
||||
findSpace total [] _ _ = [total]
|
||||
findSpace total rs@(_:rs') w h
|
||||
= case filter largeEnough $ cleanup $ subtractRects total rs of
|
||||
[] -> findSpace total rs' w h
|
||||
as -> as
|
||||
where largeEnough r = width r >= w && height r >= h
|
||||
|
||||
|
||||
-- | Subtracts smaller rectangles from a total rectangle
|
||||
-- , returning a list of remaining rectangular areas.
|
||||
subtractRects :: Real a => SmartRectangle a
|
||||
-> [SmartRectangle a] -> [SmartRectangle a]
|
||||
subtractRects total [] = [total]
|
||||
subtractRects total (r:rs)
|
||||
= do total' <- subtractRects total rs
|
||||
filter (not . isEmpty)
|
||||
[ total' {sr_y1 = min (sr_y1 total') (sr_y0 r)} -- Above
|
||||
, total' {sr_x0 = max (sr_x0 total') (sr_x1 r)} -- Right
|
||||
, total' {sr_y0 = max (sr_y0 total') (sr_y1 r)} -- Below
|
||||
, total' {sr_x1 = min (sr_x1 total') (sr_x0 r)} -- Left
|
||||
]
|
||||
|
||||
|
||||
-- | "Nubs" a list of rectangles, dropping all those that are
|
||||
-- already contained in another rectangle of the list.
|
||||
cleanup :: Real a => [SmartRectangle a] -> [SmartRectangle a]
|
||||
cleanup rs = foldr dropIfContained [] $ sortBy sizeOrder rs
|
||||
|
||||
sizeOrder :: Real a => SmartRectangle a -> SmartRectangle a -> Ordering
|
||||
sizeOrder r1 r2 | w1 < w2 = LT
|
||||
| w1 == w2 && h1 < h2 = LT
|
||||
| w1 == w2 && h1 == h2 = EQ
|
||||
| otherwise = GT
|
||||
where w1 = width r1
|
||||
w2 = width r2
|
||||
h1 = height r1
|
||||
h2 = height r2
|
||||
|
||||
dropIfContained :: Real a => SmartRectangle a
|
||||
-> [SmartRectangle a] -> [SmartRectangle a]
|
||||
dropIfContained r rs = if any (`contains` r) rs
|
||||
then rs
|
||||
else r:rs
|
49
XMonad/Hooks/RestoreMinimized.hs
Normal file
49
XMonad/Hooks/RestoreMinimized.hs
Normal file
@@ -0,0 +1,49 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Hooks.RestoreMinimized
|
||||
-- Copyright : (c) Jan Vornberger 2009
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : jan.vornberger@informatik.uni-oldenburg.de
|
||||
-- Stability : unstable
|
||||
-- Portability : not portable
|
||||
--
|
||||
-- Lets you restore minimized windows (see "XMonad.Layout.Minimize")
|
||||
-- by selecting them on a taskbar (listens for _NET_ACTIVE_WINDOW
|
||||
-- and WM_CHANGE_STATE).
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Hooks.RestoreMinimized
|
||||
( -- * Usage
|
||||
-- $usage
|
||||
RestoreMinimized (..)
|
||||
, restoreMinimizedEventHook
|
||||
) where
|
||||
|
||||
import Data.Monoid
|
||||
import Control.Monad(when)
|
||||
|
||||
import XMonad
|
||||
import XMonad.Layout.Minimize
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Hooks.RestoreMinimized
|
||||
-- >
|
||||
-- > myHandleEventHook = restoreMinimizedEventHook
|
||||
-- >
|
||||
-- > main = xmonad defaultConfig { handleEventHook = myHandleEventHook }
|
||||
|
||||
data RestoreMinimized = RestoreMinimized deriving ( Show, Read )
|
||||
|
||||
restoreMinimizedEventHook :: Event -> X All
|
||||
restoreMinimizedEventHook (ClientMessageEvent {ev_window = w,
|
||||
ev_message_type = mt}) = do
|
||||
a_aw <- getAtom "_NET_ACTIVE_WINDOW"
|
||||
a_cs <- getAtom "WM_CHANGE_STATE"
|
||||
when (mt == a_aw || mt == a_cs) $ do
|
||||
sendMessage (RestoreMinimizedWin w)
|
||||
return (All True)
|
||||
restoreMinimizedEventHook _ = return (All True)
|
@@ -43,7 +43,7 @@ import System.Directory
|
||||
-- > ...
|
||||
-- > }
|
||||
--
|
||||
-- Now, everytime the startup hook runs, the command
|
||||
-- Now, every time the startup hook runs, the command
|
||||
-- @~\/.xmonad\/hooks startup@ will also.
|
||||
|
||||
-- | Execute a named script hook
|
||||
|
@@ -59,45 +59,48 @@ module XMonad.Hooks.ServerMode
|
||||
( -- * Usage
|
||||
-- $usage
|
||||
ServerMode (..)
|
||||
, eventHook
|
||||
, serverModeEventHook
|
||||
, serverModeEventHook'
|
||||
) where
|
||||
|
||||
import Control.Monad (when)
|
||||
import Data.List
|
||||
import Data.Monoid
|
||||
import System.IO
|
||||
|
||||
import XMonad
|
||||
import XMonad.Actions.Commands
|
||||
import XMonad.Hooks.EventHook
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your
|
||||
-- @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Hooks.ServerMode
|
||||
-- > import XMonad.Actions.Commands
|
||||
--
|
||||
-- Then edit your @layoutHook@ by adding the 'eventHook':
|
||||
-- Then edit your @handleEventHook@ by adding the 'serverModeEventHook':
|
||||
--
|
||||
-- > layoutHook = eventHook ServerMode $ avoidStruts $ simpleTabbed ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { handleEventHook = serverModeEventHook }
|
||||
--
|
||||
-- and then:
|
||||
--
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayouts }
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
-- "XMonad.Doc.Extending#Editing_the_layout_hook"
|
||||
|
||||
data ServerMode = ServerMode deriving ( Show, Read )
|
||||
|
||||
instance EventHook ServerMode where
|
||||
handleEvent _ (ClientMessageEvent {ev_message_type = mt, ev_data = dt}) = do
|
||||
-- | Executes a command of the list when receiving its index via a special ClientMessageEvent
|
||||
-- (indexing starts at 1)
|
||||
serverModeEventHook :: Event -> X All
|
||||
serverModeEventHook = serverModeEventHook' defaultCommands
|
||||
|
||||
-- | serverModeEventHook' additionally takes an action to generate the list of
|
||||
-- commands.
|
||||
serverModeEventHook' :: X [(String,X ())] -> Event -> X All
|
||||
serverModeEventHook' cmdAction (ClientMessageEvent {ev_message_type = mt, ev_data = dt}) = do
|
||||
d <- asks display
|
||||
a <- io $ internAtom d "XMONAD_COMMAND" False
|
||||
when (mt == a && dt /= []) $ do
|
||||
cl <- defaultCommands
|
||||
cl <- cmdAction
|
||||
let listOfCommands = map (uncurry (++)) . zip (map show ([1..] :: [Int])) . map ((++) " - " . fst)
|
||||
case lookup (fromIntegral (head dt) :: Int) (zip [1..] cl) of
|
||||
Just (c,_) -> runCommand' c
|
||||
Nothing -> mapM_ (io . hPutStrLn stderr) . listOfCommands $ cl
|
||||
handleEvent _ _ = return ()
|
||||
Just (_,action) -> action
|
||||
Nothing -> mapM_ (io . hPutStrLn stderr) . listOfCommands $ cl
|
||||
return (All True)
|
||||
serverModeEventHook' _ _ = return (All True)
|
||||
|
@@ -70,7 +70,6 @@ module XMonad.Hooks.UrgencyHook (
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import XMonad.Hooks.EventHook
|
||||
import XMonad.Util.Dzen (dzenWithArgs, seconds)
|
||||
import XMonad.Util.NamedWindows (getName)
|
||||
import XMonad.Util.Timer (TimerId, startTimer, handleTimer)
|
||||
@@ -139,7 +138,7 @@ import Foreign (unsafePerformIO)
|
||||
-- two parts:
|
||||
--
|
||||
-- * The console app must send a ^G (bell). In bash, a helpful trick is
|
||||
-- @sleep 1; echo -e \'\a\'@.
|
||||
-- @sleep 1; echo -e \'\\a\'@.
|
||||
--
|
||||
-- * The terminal must convert the bell into UrgencyHint.
|
||||
--
|
||||
@@ -199,7 +198,7 @@ import Foreign (unsafePerformIO)
|
||||
-- 'urgencyConfig' to control behavior. To change this, use 'withUrgencyHook'
|
||||
-- instead.
|
||||
withUrgencyHook :: (LayoutClass l Window, UrgencyHook h) =>
|
||||
h -> XConfig l -> XConfig (HandleEvent (WithUrgencyHook h) l)
|
||||
h -> XConfig l -> XConfig l
|
||||
withUrgencyHook hook conf = withUrgencyHookC hook urgencyConfig conf
|
||||
|
||||
-- | This lets you modify the defaults set in 'urgencyConfig'. An example:
|
||||
@@ -208,9 +207,9 @@ withUrgencyHook hook conf = withUrgencyHookC hook urgencyConfig conf
|
||||
--
|
||||
-- (Don't type the @...@, you dolt.) See 'UrgencyConfig' for details on configuration.
|
||||
withUrgencyHookC :: (LayoutClass l Window, UrgencyHook h) =>
|
||||
h -> UrgencyConfig -> XConfig l -> XConfig (HandleEvent (WithUrgencyHook h) l)
|
||||
h -> UrgencyConfig -> XConfig l -> XConfig l
|
||||
withUrgencyHookC hook urgConf conf = conf {
|
||||
layoutHook = eventHook (WithUrgencyHook hook urgConf) $ layoutHook conf,
|
||||
handleEventHook = \e -> handleEvent (WithUrgencyHook hook urgConf) e >> handleEventHook conf e,
|
||||
logHook = cleanupUrgents (suppressWhen urgConf) >> logHook conf
|
||||
}
|
||||
|
||||
@@ -252,14 +251,14 @@ urgencyConfig = UrgencyConfig { suppressWhen = Visible, remindWhen = Dont }
|
||||
-- | Focuses the most recently urgent window. Good for what ails ya -- I mean, your keybindings.
|
||||
-- Example keybinding:
|
||||
--
|
||||
-- > , ((modMask , xK_BackSpace), focusUrgent)
|
||||
-- > , ((modm , xK_BackSpace), focusUrgent)
|
||||
focusUrgent :: X ()
|
||||
focusUrgent = withUrgents $ flip whenJust (windows . W.focusWindow) . listToMaybe
|
||||
|
||||
-- | Just makes the urgents go away.
|
||||
-- Example keybinding:
|
||||
--
|
||||
-- > , ((modMask .|. shiftMask, xK_BackSpace), clearUrgents)
|
||||
-- > , ((modm .|. shiftMask, xK_BackSpace), clearUrgents)
|
||||
clearUrgents :: X ()
|
||||
clearUrgents = adjustUrgents (const []) >> adjustReminders (const [])
|
||||
|
||||
@@ -322,9 +321,10 @@ data WithUrgencyHook h = WithUrgencyHook h UrgencyConfig
|
||||
-- ourselves, allowing us to clear urgency when a window is visible, and not to
|
||||
-- set urgency if a window is visible. If you have a better idea, please, let us
|
||||
-- know!
|
||||
instance UrgencyHook h => EventHook (WithUrgencyHook h) where
|
||||
handleEvent wuh event = case event of
|
||||
PropertyEvent { ev_event_type = t, ev_atom = a, ev_window = w } -> do
|
||||
handleEvent :: UrgencyHook h => WithUrgencyHook h -> Event -> X ()
|
||||
handleEvent wuh event =
|
||||
case event of
|
||||
PropertyEvent { ev_event_type = t, ev_atom = a, ev_window = w } -> do
|
||||
when (t == propertyNotify && a == wM_HINTS) $ withDisplay $ \dpy -> do
|
||||
WMHints { wmh_flags = flags } <- io $ getWMHints dpy w
|
||||
if (testBit flags urgencyHintBit) then do
|
||||
@@ -333,9 +333,9 @@ instance UrgencyHook h => EventHook (WithUrgencyHook h) where
|
||||
else
|
||||
clearUrgency w
|
||||
userCodeDef () =<< asks (logHook . config) -- call *after* IORef has been modified
|
||||
DestroyWindowEvent {ev_window = w} ->
|
||||
DestroyWindowEvent {ev_window = w} ->
|
||||
clearUrgency w
|
||||
_ ->
|
||||
_ ->
|
||||
mapM_ handleReminder =<< readReminders
|
||||
where handleReminder reminder = handleTimer (timer reminder) event $ reminderHook wuh reminder
|
||||
|
||||
|
54
XMonad/Hooks/WorkspaceByPos.hs
Normal file
54
XMonad/Hooks/WorkspaceByPos.hs
Normal file
@@ -0,0 +1,54 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Hooks.WorkspaceByPos
|
||||
-- Copyright : (c) Jan Vornberger 2009
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : jan.vornberger@informatik.uni-oldenburg.de
|
||||
-- Stability : unstable
|
||||
-- Portability : not portable
|
||||
--
|
||||
-- Useful in a dual-head setup: Looks at the requested geometry of
|
||||
-- new windows and moves them to the workspace of the non-focused
|
||||
-- screen if necessary.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Hooks.WorkspaceByPos (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
workspaceByPos
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad.Util.XUtils (fi)
|
||||
|
||||
import Data.Maybe
|
||||
import Control.Applicative((<$>))
|
||||
import Control.Monad.Error ((<=<),guard,lift,runErrorT,throwError)
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Hooks.WorkspaceByPos
|
||||
-- >
|
||||
-- > myManageHook = workspaceByPos <+> manageHook defaultConfig
|
||||
-- >
|
||||
-- > main = xmonad defaultConfig { manageHook = myManageHook }
|
||||
|
||||
workspaceByPos :: ManageHook
|
||||
workspaceByPos = (maybe idHook doShift <=< liftX . needsMoving) =<< ask
|
||||
|
||||
needsMoving :: Window -> X (Maybe WorkspaceId)
|
||||
needsMoving w = withDisplay $ \d -> do
|
||||
-- only relocate windows with non-zero position
|
||||
wa <- io $ getWindowAttributes d w
|
||||
fmap (const Nothing `either` Just) . runErrorT $ do
|
||||
guard $ wa_x wa /= 0 || wa_y wa /= 0
|
||||
ws <- gets windowset
|
||||
sc <- lift $ fromMaybe (W.current ws)
|
||||
<$> pointScreen (fi $ wa_x wa) (fi $ wa_y wa)
|
||||
Just wkspc <- lift $ screenWorkspace (W.screen sc)
|
||||
guard $ W.currentTag ws /= wkspc
|
||||
return wkspc `asTypeOf` throwError ""
|
@@ -34,7 +34,7 @@ import XMonad.ManageHook ((-->))
|
||||
-- > import XMonad.Actions.TagWindows
|
||||
-- > import Data.List
|
||||
--
|
||||
-- > manageHook = xPropManageHook xPropMatches
|
||||
-- > manageHook = xPropManageHook xPropMatches
|
||||
-- >
|
||||
-- > xPropMatches :: [XPropMatch]
|
||||
-- > xPropMatches = [ ([ (wM_CLASS, any ("gimp"==))], (\w -> float w >> return (W.shift "2")))
|
||||
@@ -71,7 +71,7 @@ xPropManageHook tms = mconcat $ map propToHook tms
|
||||
where
|
||||
propToHook (ms, f) = fmap and (mapM mkQuery ms) --> mkHook f
|
||||
mkQuery (a, tf) = fmap tf (getQuery a)
|
||||
mkHook func = ask >>= Query . lift . fmap Endo . func
|
||||
mkHook func = ask >>= Query . lift . fmap Endo . func
|
||||
|
||||
getProp :: Display -> Window -> Atom -> X ([String])
|
||||
getProp d w p = do
|
||||
|
@@ -7,7 +7,7 @@
|
||||
-- License : BSD
|
||||
--
|
||||
-- Maintainer : glasser@mit.edu
|
||||
-- Stability : unstable
|
||||
-- Stability : stable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- LayoutClass that puts non-focused windows in ribbons at the top and bottom
|
||||
@@ -30,8 +30,8 @@ import Data.Ratio
|
||||
--
|
||||
-- Then edit your @layoutHook@ by adding the Accordion layout:
|
||||
--
|
||||
-- > myLayouts = Accordion ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayouts }
|
||||
-- > myLayout = Accordion ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayout }
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
|
123
XMonad/Layout/AutoMaster.hs
Normal file
123
XMonad/Layout/AutoMaster.hs
Normal file
@@ -0,0 +1,123 @@
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeSynonymInstances, FlexibleContexts #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.AutoMaster
|
||||
-- Copyright : (c) 2009 Ilya Portnov
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Ilya Portnov <portnov84@rambler.ru>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Provides layout modifier AutoMaster. It separates screen in two parts -
|
||||
-- master and slave. Size of slave area automatically changes depending on
|
||||
-- number of slave windows.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Layout.AutoMaster (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
autoMaster
|
||||
) where
|
||||
import Control.Monad
|
||||
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad.Layout.LayoutModifier
|
||||
|
||||
-- $usage
|
||||
-- This module defines layout modifier named autoMaster. It separates
|
||||
-- screen in two parts - master and slave. Master windows are arranged
|
||||
-- in one row, in slave area underlying layout is run. Size of slave area
|
||||
-- automatically increases when number of slave windows is increasing.
|
||||
--
|
||||
-- You can use this module by adding folowing in your @xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Layout.AutoMaster
|
||||
--
|
||||
-- Then add layouts to your layoutHook:
|
||||
--
|
||||
-- > myLayoutHook = autoMaster 1 (1/100) Grid ||| ...
|
||||
--
|
||||
-- In this example, master area by default contains 1 window (you can
|
||||
-- change this number in runtime with usual IncMasterN message), changing
|
||||
-- slave area size with 1/100 on each Shrink/Expand message.
|
||||
|
||||
-- | Data type for layout modifier
|
||||
data AutoMaster a = AutoMaster Int Float Float
|
||||
deriving (Read,Show)
|
||||
|
||||
instance LayoutModifier AutoMaster Window where
|
||||
modifyLayout (AutoMaster k bias _) = autoLayout k bias
|
||||
pureMess = autoMess
|
||||
|
||||
-- | Handle Shrink/Expand and IncMasterN messages
|
||||
autoMess :: AutoMaster a -> SomeMessage -> Maybe (AutoMaster a)
|
||||
autoMess (AutoMaster k bias delta) m = msum [fmap resize (fromMessage m),
|
||||
fmap incmastern (fromMessage m)]
|
||||
where incmastern (IncMasterN d) = AutoMaster (max 1 (k+d)) bias delta
|
||||
resize Expand = AutoMaster k (min ( 0.4) $ bias+delta) delta
|
||||
resize Shrink = AutoMaster k (max (-0.4) $ bias-delta) delta
|
||||
|
||||
-- | Main layout function
|
||||
autoLayout :: (LayoutClass l Window) =>
|
||||
Int ->
|
||||
Float ->
|
||||
W.Workspace WorkspaceId (l Window) Window
|
||||
-> Rectangle
|
||||
-> X ([(Window, Rectangle)], Maybe (l Window))
|
||||
autoLayout k bias wksp rect = do
|
||||
let stack = W.stack wksp
|
||||
let ws = W.integrate' stack
|
||||
let n = length ws
|
||||
if null ws then
|
||||
runLayout wksp rect
|
||||
else do
|
||||
if (n<=k) then
|
||||
return ((divideRow rect ws),Nothing)
|
||||
else do
|
||||
let master = take k ws
|
||||
let filtStack = stack >>= W.filter (\w -> not (w `elem` master))
|
||||
wrs <- runLayout (wksp {W.stack = filtStack}) (slaveRect rect n bias)
|
||||
return ((divideRow (masterRect rect n bias) master) ++ (fst wrs),
|
||||
snd wrs)
|
||||
|
||||
-- | Calculates height of master area, depending on number of windows.
|
||||
masterHeight :: Int -> Float -> Float
|
||||
masterHeight n bias = (calcHeight n) + bias
|
||||
where calcHeight :: Int -> Float
|
||||
calcHeight 1 = 1.0
|
||||
calcHeight m = if (m<9) then (43/45) - (fromIntegral m)*(7/90) else (1/3)
|
||||
|
||||
-- | Rectangle for master area
|
||||
masterRect :: Rectangle -> Int -> Float -> Rectangle
|
||||
masterRect (Rectangle sx sy sw sh) n bias = Rectangle sx sy sw h
|
||||
where h = round $ (fromIntegral sh)*(masterHeight n bias)
|
||||
|
||||
-- | Rectangle for slave area
|
||||
slaveRect :: Rectangle -> Int -> Float -> Rectangle
|
||||
slaveRect (Rectangle sx sy sw sh) n bias = Rectangle sx (sy+mh) sw h
|
||||
where mh = round $ (fromIntegral sh)*(masterHeight n bias)
|
||||
h = round $ (fromIntegral sh)*(1-masterHeight n bias)
|
||||
|
||||
-- | Divide rectangle between windows
|
||||
divideRow :: Rectangle -> [a] -> [(a, Rectangle)]
|
||||
divideRow (Rectangle x y w h) ws = zip ws rects
|
||||
where n = length ws
|
||||
oneW = fromIntegral w `div` n
|
||||
oneRect = Rectangle x y (fromIntegral oneW) h
|
||||
rects = take n $ iterate (shiftR (fromIntegral oneW)) oneRect
|
||||
|
||||
-- | Shift rectangle right
|
||||
shiftR :: Position -> Rectangle -> Rectangle
|
||||
shiftR s (Rectangle x y w h) = Rectangle (x+s) y w h
|
||||
|
||||
-- | User interface function
|
||||
autoMaster :: LayoutClass l a =>
|
||||
Int -> -- Number of master windows
|
||||
Float -> -- Step for which to increment/decrement master area size with Shrink/Expand
|
||||
l a ->
|
||||
ModifiedLayout AutoMaster l a
|
||||
autoMaster nmaster delta = ModifiedLayout (AutoMaster nmaster 0 delta)
|
||||
|
164
XMonad/Layout/BorderResize.hs
Normal file
164
XMonad/Layout/BorderResize.hs
Normal file
@@ -0,0 +1,164 @@
|
||||
{-# LANGUAGE TypeSynonymInstances, MultiParamTypeClasses, PatternGuards #-}
|
||||
----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.BorderResize
|
||||
-- Copyright : (c) Jan Vornberger 2009
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : jan.vornberger@informatik.uni-oldenburg.de
|
||||
-- Stability : unstable
|
||||
-- Portability : not portable
|
||||
--
|
||||
-- This layout modifier will allow to resize windows by dragging their
|
||||
-- borders with the mouse. However, it only works in layouts or modified
|
||||
-- layouts that react to the 'SetGeometry' message.
|
||||
-- "XMonad.Layout.WindowArranger" can be used to create such a setup.
|
||||
-- BorderResize is probably most useful in floating layouts.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Layout.BorderResize
|
||||
( -- * Usage
|
||||
-- $usage
|
||||
borderResize
|
||||
, BorderResize (..)
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Layout.Decoration
|
||||
import XMonad.Layout.WindowArranger
|
||||
import XMonad.Util.XUtils
|
||||
import Control.Monad(when,forM)
|
||||
import Control.Arrow(first)
|
||||
import Control.Applicative((<$>))
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your
|
||||
-- @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Layout.BorderResize
|
||||
-- > myLayout = borderResize (... layout setup that reacts to SetGeometry ...)
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayout }
|
||||
--
|
||||
|
||||
data BorderInfo = RightSideBorder Window Rectangle
|
||||
| LeftSideBorder Window Rectangle
|
||||
| TopSideBorder Window Rectangle
|
||||
| BottomSideBorder Window Rectangle
|
||||
deriving (Show, Read, Eq)
|
||||
type BorderWithRect = (Rectangle, Rectangle, Glyph, BorderInfo)
|
||||
type BorderWithWin = (Window, BorderInfo)
|
||||
|
||||
data BorderResize a = BR [BorderWithWin] deriving (Show, Read)
|
||||
|
||||
brBorderOffset :: Position
|
||||
brBorderOffset = 5
|
||||
brBorderSize :: Dimension
|
||||
brBorderSize = 10
|
||||
|
||||
brCursorRightSide :: Glyph
|
||||
brCursorRightSide = 96
|
||||
brCursorLeftSide :: Glyph
|
||||
brCursorLeftSide = 70
|
||||
brCursorTopSide :: Glyph
|
||||
brCursorTopSide = 138
|
||||
brCursorBottomSide :: Glyph
|
||||
brCursorBottomSide = 16
|
||||
|
||||
borderResize :: l a -> ModifiedLayout BorderResize l a
|
||||
borderResize = ModifiedLayout (BR [])
|
||||
|
||||
instance LayoutModifier BorderResize Window where
|
||||
redoLayout _ _ Nothing wrs = return (wrs, Nothing)
|
||||
redoLayout (BR borders) _ _ wrs = do
|
||||
let preparedBorders = for wrs $ \wr -> (wr, prepareBorders wr)
|
||||
mapM_ deleteBorder borders
|
||||
newBorders <- forM preparedBorders $ \(wr, (b1, b2, b3, b4)) ->
|
||||
first (++[wr]) . unzip <$> mapM createBorder [b1,b2,b3,b4]
|
||||
let wrs' = concat $ map fst newBorders
|
||||
newBordersSerialized = concat $ map snd newBorders
|
||||
return (wrs', Just $ BR newBordersSerialized)
|
||||
-- What we return is the original wrs with the new border
|
||||
-- windows inserted at the correct positions - this way, the core
|
||||
-- will restack the borders correctly.
|
||||
-- We also return information about our borders, so that we
|
||||
-- can handle events that they receive and destroy them when
|
||||
-- they are no longer needed.
|
||||
|
||||
handleMess (BR borders) m
|
||||
| Just e <- fromMessage m :: Maybe Event = handleResize borders e >> return Nothing
|
||||
| Just _ <- fromMessage m :: Maybe LayoutMessages =
|
||||
mapM_ deleteBorder borders >> return (Just $ BR [])
|
||||
handleMess _ _ = return Nothing
|
||||
|
||||
prepareBorders :: (Window, Rectangle) -> (BorderWithRect, BorderWithRect, BorderWithRect, BorderWithRect)
|
||||
prepareBorders (w, r@(Rectangle x y wh ht)) =
|
||||
((r, (Rectangle (x + fi wh - brBorderOffset) y brBorderSize ht) , brCursorRightSide , RightSideBorder w r),
|
||||
(r, (Rectangle (x - brBorderOffset) y brBorderSize ht) , brCursorLeftSide , LeftSideBorder w r),
|
||||
(r, (Rectangle x (y - brBorderOffset) wh brBorderSize) , brCursorTopSide , TopSideBorder w r),
|
||||
(r, (Rectangle x (y + fi ht - brBorderOffset) wh brBorderSize) , brCursorBottomSide , BottomSideBorder w r)
|
||||
)
|
||||
|
||||
handleResize :: [BorderWithWin] -> Event -> X ()
|
||||
handleResize borders ButtonEvent { ev_window = ew, ev_event_type = et }
|
||||
| et == buttonPress, Just edge <- lookup ew borders =
|
||||
case edge of
|
||||
RightSideBorder hostWin (Rectangle hx hy _ hht) ->
|
||||
mouseDrag (\x _ -> do
|
||||
let nwh = max 1 $ fi (x - hx)
|
||||
rect = Rectangle hx hy nwh hht
|
||||
focus hostWin
|
||||
when (x - hx > 0) $ sendMessage (SetGeometry rect)) (focus hostWin)
|
||||
LeftSideBorder hostWin (Rectangle hx hy hwh hht) ->
|
||||
mouseDrag (\x _ -> do
|
||||
let nx = max 0 $ min (hx + fi hwh) $ x
|
||||
nwh = max 1 $ hwh + fi (hx - x)
|
||||
rect = Rectangle nx hy nwh hht
|
||||
focus hostWin
|
||||
when (x < hx + fi hwh) $ sendMessage (SetGeometry rect)) (focus hostWin)
|
||||
TopSideBorder hostWin (Rectangle hx hy hwh hht) ->
|
||||
mouseDrag (\_ y -> do
|
||||
let ny = max 0 $ min (hy + fi hht) $ y
|
||||
nht = max 1 $ hht + fi (hy - y)
|
||||
rect = Rectangle hx ny hwh nht
|
||||
focus hostWin
|
||||
when (y < hy + fi hht) $ sendMessage (SetGeometry rect)) (focus hostWin)
|
||||
BottomSideBorder hostWin (Rectangle hx hy hwh _) ->
|
||||
mouseDrag (\_ y -> do
|
||||
let nht = max 1 $ fi (y - hy)
|
||||
rect = Rectangle hx hy hwh nht
|
||||
focus hostWin
|
||||
when (y - hy > 0) $ sendMessage (SetGeometry rect)) (focus hostWin)
|
||||
handleResize _ _ = return ()
|
||||
|
||||
createBorder :: BorderWithRect -> X (((Window, Rectangle), BorderWithWin))
|
||||
createBorder (_, borderRect, borderCursor, borderInfo) = do
|
||||
borderWin <- createInputWindow borderCursor borderRect
|
||||
return ((borderWin, borderRect), (borderWin, borderInfo))
|
||||
|
||||
deleteBorder :: BorderWithWin -> X ()
|
||||
deleteBorder (borderWin, _) = deleteWindow borderWin
|
||||
|
||||
createInputWindow :: Glyph -> Rectangle -> X Window
|
||||
createInputWindow cursorGlyph r = withDisplay $ \d -> do
|
||||
win <- mkInputWindow d r
|
||||
io $ selectInput d win (exposureMask .|. buttonPressMask)
|
||||
cursor <- io $ createFontCursor d cursorGlyph
|
||||
io $ defineCursor d win cursor
|
||||
io $ freeCursor d cursor
|
||||
showWindow win
|
||||
return win
|
||||
|
||||
mkInputWindow :: Display -> Rectangle -> X Window
|
||||
mkInputWindow d (Rectangle x y w h) = do
|
||||
rw <- asks theRoot
|
||||
let screen = defaultScreenOfDisplay d
|
||||
visual = defaultVisualOfScreen screen
|
||||
attrmask = cWOverrideRedirect
|
||||
io $ allocaSetWindowAttributes $
|
||||
\attributes -> do
|
||||
set_override_redirect attributes True
|
||||
createWindow d rw x y w h 0 0 inputOnly visual attrmask attributes
|
||||
|
||||
for :: [a] -> (a -> b) -> [b]
|
||||
for = flip map
|
@@ -1,5 +1,5 @@
|
||||
{-# OPTIONS_GHC -fglasgow-exts #-} -- For deriving Data/Typeable
|
||||
|
||||
{-# LANGUAGE TypeSynonymInstances, MultiParamTypeClasses, DeriveDataTypeable #-}
|
||||
{-# LANGUAGE PatternGuards, FlexibleContexts, FlexibleInstances #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.BoringWindows
|
||||
@@ -17,51 +17,122 @@
|
||||
module XMonad.Layout.BoringWindows (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
boringWindows,
|
||||
boringWindows, boringAuto,
|
||||
markBoring, clearBoring,
|
||||
focusUp, focusDown
|
||||
focusUp, focusDown, focusMaster,
|
||||
|
||||
UpdateBoring(UpdateBoring),
|
||||
BoringMessage(Replace,Merge),
|
||||
BoringWindows()
|
||||
) where
|
||||
|
||||
import XMonad hiding (Point)
|
||||
import XMonad.Layout.LayoutModifier(ModifiedLayout(..),
|
||||
LayoutModifier(handleMessOrMaybeModifyIt, redoLayout))
|
||||
import XMonad(Typeable, LayoutClass, Message, X, fromMessage,
|
||||
sendMessage, windows, withFocused, Window)
|
||||
import Control.Applicative((<$>))
|
||||
import Control.Monad(Monad(return, (>>)))
|
||||
import Data.List((\\), union)
|
||||
import Data.Maybe(Maybe(..), maybe, fromMaybe, listToMaybe,
|
||||
maybeToList)
|
||||
import qualified Data.Map as M
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad.Layout.LayoutModifier
|
||||
import XMonad.Util.Invisible
|
||||
|
||||
data BoringMessage = FocusUp | FocusDown | IsBoring Window | ClearBoring
|
||||
deriving ( Read, Show, Typeable )
|
||||
-- $usage
|
||||
-- You can use this module with the following in your
|
||||
-- @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Layout.BoringWindows
|
||||
--
|
||||
-- Then edit your @layoutHook@ by adding the layout modifier:
|
||||
--
|
||||
-- > myLayout = boringWindows (Full ||| etc..)
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayout }
|
||||
--
|
||||
-- Then to your keybindings, add:
|
||||
--
|
||||
-- > , ((modm, xK_j), focusUp)
|
||||
-- > , ((modm, xK_k), focusDown)
|
||||
-- > , ((modm, xK_m), focusMaster)
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
-- "XMonad.Doc.Extending#Editing_the_layout_hook"
|
||||
|
||||
|
||||
data BoringMessage = FocusUp | FocusDown | FocusMaster | IsBoring Window | ClearBoring
|
||||
| Replace String [Window]
|
||||
| Merge String [Window]
|
||||
deriving ( Read, Show, Typeable )
|
||||
|
||||
instance Message BoringMessage
|
||||
|
||||
markBoring, clearBoring, focusUp, focusDown :: X ()
|
||||
-- | UpdateBoring is sent before attempting to view another boring window, so
|
||||
-- that layouts have a chance to mark boring windows.
|
||||
data UpdateBoring = UpdateBoring
|
||||
deriving (Typeable)
|
||||
instance Message UpdateBoring
|
||||
|
||||
markBoring, clearBoring, focusUp, focusDown, focusMaster :: X ()
|
||||
markBoring = withFocused (sendMessage . IsBoring)
|
||||
clearBoring = sendMessage ClearBoring
|
||||
focusUp = sendMessage FocusUp
|
||||
focusDown = sendMessage FocusDown
|
||||
focusUp = sendMessage UpdateBoring >> sendMessage FocusUp
|
||||
focusDown = sendMessage UpdateBoring >> sendMessage FocusDown
|
||||
focusMaster = sendMessage UpdateBoring >> sendMessage FocusMaster
|
||||
|
||||
data BoringWindows a = BoringWindows (Invisible [] a) deriving ( Show, Read, Typeable )
|
||||
data BoringWindows a = BoringWindows
|
||||
{ namedBoring :: M.Map String [a] -- ^ store borings with a specific source
|
||||
, chosenBoring :: [a] -- ^ user-chosen borings
|
||||
, hiddenBoring :: Maybe [a] -- ^ maybe mark hidden windows
|
||||
} deriving (Show,Read,Typeable)
|
||||
|
||||
boringWindows :: (LayoutClass l a, Eq a) => l a -> ModifiedLayout BoringWindows l a
|
||||
boringWindows = ModifiedLayout (BoringWindows (I []))
|
||||
boringWindows = ModifiedLayout (BoringWindows M.empty [] Nothing)
|
||||
|
||||
-- | Mark windows that are not given rectangles as boring
|
||||
boringAuto :: (LayoutClass l a, Eq a) => l a -> ModifiedLayout BoringWindows l a
|
||||
boringAuto = ModifiedLayout (BoringWindows M.empty [] (Just []))
|
||||
|
||||
instance LayoutModifier BoringWindows Window where
|
||||
handleMessOrMaybeModifyIt (BoringWindows (I bs)) m
|
||||
| Just (IsBoring b) <- fromMessage m = return $ Just $ Left $ BoringWindows (I (b:bs))
|
||||
| Just ClearBoring <- fromMessage m = return $ Just $ Left $ BoringWindows (I [])
|
||||
| Just FocusUp <- fromMessage m = do windows $ W.modify' $ focusUp'
|
||||
return Nothing
|
||||
| Just FocusDown <- fromMessage m =
|
||||
do windows $ W.modify' (reverseStack . focusUp' . reverseStack)
|
||||
redoLayout (b@BoringWindows { hiddenBoring = bs }) _r mst arrs = do
|
||||
let bs' = W.integrate' mst \\ map fst arrs
|
||||
return (arrs, Just $ b { hiddenBoring = const bs' <$> bs } )
|
||||
|
||||
handleMessOrMaybeModifyIt bst@(BoringWindows nbs cbs lbs) m
|
||||
| Just (Replace k ws) <- fromMessage m
|
||||
, maybe True (ws/=) (M.lookup k nbs) =
|
||||
let nnb = if null ws then M.delete k nbs
|
||||
else M.insert k ws nbs
|
||||
in rjl bst { namedBoring = nnb }
|
||||
| Just (Merge k ws) <- fromMessage m
|
||||
, maybe True (not . null . (ws \\)) (M.lookup k nbs) =
|
||||
rjl bst { namedBoring = M.insertWith union k ws nbs }
|
||||
| Just (IsBoring w) <- fromMessage m , w `notElem` cbs =
|
||||
rjl bst { chosenBoring = w:cbs }
|
||||
| Just ClearBoring <- fromMessage m, not (null cbs) =
|
||||
rjl bst { namedBoring = M.empty, chosenBoring = []}
|
||||
| Just FocusUp <- fromMessage m =
|
||||
do windows $ W.modify' $ skipBoring W.focusUp'
|
||||
return Nothing
|
||||
where focusUp' (W.Stack t ls rs)
|
||||
| (a,l:ls') <- skipBoring ls = W.Stack l ls' (a++t:rs)
|
||||
| otherwise = case skipBoring (reverse (t:rs)++ls) of
|
||||
(a,x:xs) -> W.Stack x xs a
|
||||
_ -> W.Stack t ls rs
|
||||
skipBoring [] = ([],[])
|
||||
skipBoring (x:xs) | x `elem` bs = case skipBoring xs of
|
||||
(a,b) -> (x:a,b)
|
||||
| otherwise = ([],x:xs)
|
||||
| Just FocusDown <- fromMessage m =
|
||||
do windows $ W.modify' $ skipBoring W.focusDown'
|
||||
return Nothing
|
||||
| Just FocusMaster <- fromMessage m =
|
||||
do windows $ W.modify'
|
||||
$ skipBoring W.focusDown' -- wiggle focus to make sure
|
||||
. skipBoring W.focusUp' -- no boring window gets the focus
|
||||
. focusMaster'
|
||||
return Nothing
|
||||
where skipBoring f st = fromMaybe st $ listToMaybe
|
||||
$ filter ((`notElem` W.focus st:bs) . W.focus)
|
||||
$ take (length $ W.integrate st)
|
||||
$ iterate f st
|
||||
bs = concat $ cbs:maybeToList lbs ++ M.elems nbs
|
||||
rjl = return . Just . Left
|
||||
handleMessOrMaybeModifyIt _ _ = return Nothing
|
||||
|
||||
-- | reverse a stack: up becomes down and down becomes up.
|
||||
reverseStack :: W.Stack a -> W.Stack a
|
||||
reverseStack (W.Stack t ls rs) = W.Stack t rs ls
|
||||
-- | Variant of 'focusMaster' that works on a
|
||||
-- 'Stack' rather than an entire 'StackSet'.
|
||||
focusMaster' :: W.Stack a -> W.Stack a
|
||||
focusMaster' c@(W.Stack _ [] _) = c
|
||||
focusMaster' (W.Stack t ls rs) = W.Stack x [] (xs ++ t : rs) where (x:xs) = reverse ls
|
||||
|
@@ -3,15 +3,15 @@
|
||||
-- |
|
||||
-- Module : XMonad.Layout.CenteredMaster
|
||||
-- Copyright : (c) 2009 Ilya Portnov
|
||||
-- License : GNU GPL v3 or any later
|
||||
-- License : BSD-style (see xmonad/LICENSE)
|
||||
--
|
||||
-- Maintainer : Ilya Portnov <portnov84@rambler.ru>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Two layout modifiers. centerMaster places master window at center,
|
||||
-- on top of all other windows, which are managed by base layout.
|
||||
-- topRightMaster is similar, but places master window in top right corner
|
||||
-- Two layout modifiers. centerMaster places master window at center,
|
||||
-- on top of all other windows, which are managed by base layout.
|
||||
-- topRightMaster is similar, but places master window in top right corner
|
||||
-- instead of center.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
@@ -30,22 +30,22 @@ import qualified XMonad.StackSet as W
|
||||
|
||||
-- $usage
|
||||
-- This module defines two new layout modifiers: centerMaster and topRightMaster.
|
||||
-- centerMaster places master window at center of screen, on top of others.
|
||||
-- centerMaster places master window at center of screen, on top of others.
|
||||
-- All other windows in background are managed by base layout.
|
||||
-- topRightMaster is like centerMaster, but places master window in top right corner instead of center.
|
||||
--
|
||||
--
|
||||
-- Yo can use this module by adding folowing in your @xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Layout.CenteredMaster
|
||||
--
|
||||
--
|
||||
-- Then add layouts to your layoutHook:
|
||||
--
|
||||
--
|
||||
-- > myLayoutHook = centerMaster Grid ||| ...
|
||||
|
||||
-- | Function that decides where master window should be placed
|
||||
type Positioner = Rectangle -> Rectangle
|
||||
|
||||
-- | Data type for LayoutModifier
|
||||
-- | Data type for LayoutModifier
|
||||
data CenteredMaster a = CenteredMaster deriving (Read,Show)
|
||||
|
||||
instance LayoutModifier CenteredMaster Window where
|
||||
@@ -56,12 +56,12 @@ data TopRightMaster a = TopRightMaster deriving (Read,Show)
|
||||
instance LayoutModifier TopRightMaster Window where
|
||||
modifyLayout TopRightMaster = applyPosition (topRight (3/7) (1/2))
|
||||
|
||||
-- | Modifier that puts master window in center, other windows in background
|
||||
-- | Modifier that puts master window in center, other windows in background
|
||||
-- are managed by given layout
|
||||
centerMaster :: LayoutClass l a => l a -> ModifiedLayout CenteredMaster l a
|
||||
centerMaster = ModifiedLayout CenteredMaster
|
||||
|
||||
-- | Modifier that puts master window in top right corner, other windows in background
|
||||
-- | Modifier that puts master window in top right corner, other windows in background
|
||||
-- are managed by given layout
|
||||
topRightMaster :: LayoutClass l a => l a -> ModifiedLayout TopRightMaster l a
|
||||
topRightMaster = ModifiedLayout TopRightMaster
|
||||
|
@@ -31,8 +31,8 @@ import XMonad.StackSet (integrate, peek)
|
||||
--
|
||||
-- Then edit your @layoutHook@ by adding the Circle layout:
|
||||
--
|
||||
-- > myLayouts = Circle ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayouts }
|
||||
-- > myLayout = Circle ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayout }
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
|
71
XMonad/Layout/Column.hs
Normal file
71
XMonad/Layout/Column.hs
Normal file
@@ -0,0 +1,71 @@
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeSynonymInstances #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.Column
|
||||
-- Copyright : (c) 2009 Ilya Portnov
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Ilya Portnov <portnov84@rambler.ru>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Provides Column layout that places all windows in one column. Windows
|
||||
-- heights are calculated from equation: H1/H2 = H2/H3 = ... = q, where q is
|
||||
-- given. With Shrink/Expand messages you can change the q value.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Layout.Column (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
Column (..)
|
||||
) where
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
-- $usage
|
||||
-- This module defines layot named Column. It places all windows in one
|
||||
-- column. Windows heights are calculated from equation: H1/H2 = H2/H3 = ... =
|
||||
-- q, where `q' is given (thus, windows heights are members of geometric
|
||||
-- progression). With Shrink/Expand messages one can change the `q' value.
|
||||
--
|
||||
-- You can use this module by adding folowing in your @xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Layout.Column
|
||||
--
|
||||
-- Then add layouts to your layoutHook:
|
||||
--
|
||||
-- > myLayoutHook = Column 1.6 ||| ...
|
||||
--
|
||||
-- In this example, each next window will have height 1.6 times less then
|
||||
-- previous window.
|
||||
|
||||
data Column a = Column Float deriving (Read,Show)
|
||||
|
||||
instance LayoutClass Column a where
|
||||
pureLayout = columnLayout
|
||||
pureMessage = columnMessage
|
||||
|
||||
columnMessage :: Column a -> SomeMessage -> Maybe (Column a)
|
||||
columnMessage (Column q) m = fmap resize (fromMessage m)
|
||||
where resize Shrink = Column (q-0.1)
|
||||
resize Expand = Column (q+0.1)
|
||||
|
||||
columnLayout :: Column a -> Rectangle -> W.Stack a -> [(a,Rectangle)]
|
||||
columnLayout (Column q) rect stack = zip ws rects
|
||||
where ws = W.integrate stack
|
||||
n = length ws
|
||||
heights = map (xn n rect q) [1..n]
|
||||
ys = [fromIntegral $ sum $ take k heights | k <- [0..n-1]]
|
||||
rects = map (mkRect rect) $ zip heights ys
|
||||
|
||||
mkRect :: Rectangle -> (Dimension,Position) -> Rectangle
|
||||
mkRect (Rectangle xs ys ws _) (h,y) = Rectangle xs (ys+fromIntegral y) ws h
|
||||
|
||||
xn :: Int -> Rectangle -> Float -> Int -> Dimension
|
||||
xn n (Rectangle _ _ _ h) q k = if q==1 then
|
||||
h `div` (fromIntegral n)
|
||||
else
|
||||
round ((fromIntegral h)*q^(n-k)*(1-q)/(1-q^n))
|
||||
|
||||
|
@@ -25,7 +25,7 @@ module XMonad.Layout.Combo (
|
||||
import Data.List ( delete, intersect, (\\) )
|
||||
import Data.Maybe ( isJust )
|
||||
import XMonad hiding (focus)
|
||||
import XMonad.StackSet ( integrate, Workspace (..), Stack(..) )
|
||||
import XMonad.StackSet ( integrate', Workspace (..), Stack(..) )
|
||||
import XMonad.Layout.WindowNavigation ( MoveWindowToWindow(..) )
|
||||
import qualified XMonad.StackSet as W ( differentiate )
|
||||
|
||||
@@ -51,10 +51,10 @@ import qualified XMonad.StackSet as W ( differentiate )
|
||||
-- each sublayout. To do this, use "XMonad.Layout.WindowNavigation",
|
||||
-- and add the following key bindings (or something similar):
|
||||
--
|
||||
-- > , ((modMask x .|. controlMask .|. shiftMask, xK_Right), sendMessage $ Move R)
|
||||
-- > , ((modMask x .|. controlMask .|. shiftMask, xK_Left ), sendMessage $ Move L)
|
||||
-- > , ((modMask x .|. controlMask .|. shiftMask, xK_Up ), sendMessage $ Move U)
|
||||
-- > , ((modMask x .|. controlMask .|. shiftMask, xK_Down ), sendMessage $ Move D)
|
||||
-- > , ((modm .|. controlMask .|. shiftMask, xK_Right), sendMessage $ Move R)
|
||||
-- > , ((modm .|. controlMask .|. shiftMask, xK_Left ), sendMessage $ Move L)
|
||||
-- > , ((modm .|. controlMask .|. shiftMask, xK_Up ), sendMessage $ Move U)
|
||||
-- > , ((modm .|. controlMask .|. shiftMask, xK_Down ), sendMessage $ Move D)
|
||||
--
|
||||
-- For detailed instruction on editing the key binding see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
@@ -76,7 +76,7 @@ combineTwo = C2 [] []
|
||||
|
||||
instance (LayoutClass l (), LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a)
|
||||
=> LayoutClass (CombineTwo (l ()) l1 l2) a where
|
||||
doLayout (C2 f w2 super l1 l2) rinput s = arrange (integrate s)
|
||||
runLayout (Workspace _ (C2 f w2 super l1 l2) s) rinput = arrange (integrate' s)
|
||||
where arrange [] = do l1' <- maybe l1 id `fmap` handleMessage l1 (SomeMessage ReleaseResources)
|
||||
l2' <- maybe l2 id `fmap` handleMessage l2 (SomeMessage ReleaseResources)
|
||||
super' <- maybe super id `fmap`
|
||||
@@ -93,12 +93,11 @@ instance (LayoutClass l (), LayoutClass l1 a, LayoutClass l2 a, Read a, Show a,
|
||||
x -> case origws \\ x of
|
||||
[] -> init x
|
||||
_ -> x
|
||||
superstack = if focus s `elem` w2'
|
||||
then Stack { focus=(), up=[], down=[()] }
|
||||
else Stack { focus=(), up=[], down=[()] }
|
||||
superstack = Stack { focus=(), up=[], down=[()] }
|
||||
s1 = differentiate f' (origws \\ w2')
|
||||
s2 = differentiate f' w2'
|
||||
f' = focus s:delete (focus s) f
|
||||
f' = case s of (Just s') -> focus s':delete (focus s') f
|
||||
Nothing -> f
|
||||
([((),r1),((),r2)], msuper') <- runLayout (Workspace "" super (Just superstack)) rinput
|
||||
(wrs1, ml1') <- runLayout (Workspace "" l1 s1) r1
|
||||
(wrs2, ml2') <- runLayout (Workspace "" l2 s2) r2
|
||||
|
180
XMonad/Layout/ComboP.hs
Normal file
180
XMonad/Layout/ComboP.hs
Normal file
@@ -0,0 +1,180 @@
|
||||
{-# LANGUAGE TypeSynonymInstances, DeriveDataTypeable, MultiParamTypeClasses, FlexibleContexts, FlexibleInstances, PatternGuards #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.ComboP
|
||||
-- Copyright : (c) Konstantin Sobolev <konstantin.sobolev@gmail.com>
|
||||
-- License : BSD-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Konstantin Sobolev <konstantin.sobolev@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- A layout that combines multiple layouts and allows to specify where to put
|
||||
-- new windows.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Layout.ComboP (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
combineTwoP,
|
||||
CombineTwoP,
|
||||
SwapWindow(..),
|
||||
Property(..)
|
||||
) where
|
||||
|
||||
import Data.List ( delete, intersect, (\\) )
|
||||
import Data.Maybe ( isJust )
|
||||
import Control.Monad
|
||||
import XMonad hiding (focus)
|
||||
import XMonad.StackSet ( integrate, Workspace (..), Stack(..) )
|
||||
import XMonad.Layout.WindowNavigation
|
||||
import XMonad.Util.WindowProperties
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Layout.ComboP
|
||||
--
|
||||
-- and add something like
|
||||
--
|
||||
-- > combineTwoP (TwoPane 0.03 0.5) (tabbed shrinkText defaultTConf) (tabbed shrinkText defaultTConf) (ClassName "Firefox")
|
||||
--
|
||||
-- to your layouts. This way all windows with class = \"Firefox\" will always go
|
||||
-- to the left pane, all others - to the right.
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
-- "XMonad.Doc.Extending#Editing_the_layout_hook"
|
||||
--
|
||||
-- 'combineTwoP' is a simple layout combinator based on 'combineTwo' from Combo, with
|
||||
-- addition of a 'Property' which tells where to put new windows. Windows mathing
|
||||
-- the property will go into the first part, all others will go into the second
|
||||
-- part. It supports @Move@ messages as 'combineTwo' does, but it also introduces
|
||||
-- 'SwapWindow' message which sends focused window to the other part. It is
|
||||
-- required because @Move@ commands don't work when one of the parts is empty.
|
||||
-- To use it, import \"XMonad.Layout.WindowNavigation\", and add the following key
|
||||
-- bindings (or something similar):
|
||||
--
|
||||
-- > , ((modm .|. controlMask .|. shiftMask, xK_Right), sendMessage $ Move R)
|
||||
-- > , ((modm .|. controlMask .|. shiftMask, xK_Left ), sendMessage $ Move L)
|
||||
-- > , ((modm .|. controlMask .|. shiftMask, xK_Up ), sendMessage $ Move U)
|
||||
-- > , ((modm .|. controlMask .|. shiftMask, xK_Down ), sendMessage $ Move D)
|
||||
-- > , ((modm .|. controlMask .|. shiftMask, xK_s ), sendMessage $ SwapWindow)
|
||||
--
|
||||
-- For detailed instruction on editing the key binding see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
||||
data SwapWindow = SwapWindow -- ^ Swap window between panes
|
||||
| SwapWindowN Int -- ^ Swap window between panes in the N-th nested ComboP. @SwapWindowN 0@ equals to SwapWindow
|
||||
deriving (Read, Show, Typeable)
|
||||
instance Message SwapWindow
|
||||
|
||||
data CombineTwoP l l1 l2 a = C2P [a] [a] [a] l (l1 a) (l2 a) Property
|
||||
deriving (Read, Show)
|
||||
|
||||
combineTwoP :: (LayoutClass super(), LayoutClass l1 Window, LayoutClass l2 Window) =>
|
||||
super () -> l1 Window -> l2 Window -> Property -> CombineTwoP (super ()) l1 l2 Window
|
||||
combineTwoP = C2P [] [] []
|
||||
|
||||
instance (LayoutClass l (), LayoutClass l1 Window, LayoutClass l2 Window) =>
|
||||
LayoutClass (CombineTwoP (l ()) l1 l2) Window where
|
||||
doLayout (C2P f w1 w2 super l1 l2 prop) rinput s =
|
||||
let origws = W.integrate s -- passed in windows
|
||||
w1c = origws `intersect` w1 -- current windows in the first pane
|
||||
w2c = origws `intersect` w2 -- current windows in the second pane
|
||||
new = origws \\ (w1c ++ w2c) -- new windows
|
||||
superstack = Just Stack { focus=(), up=[], down=[()] }
|
||||
f' = focus s:delete (focus s) f -- list of focused windows, contains 2 elements at most
|
||||
in do
|
||||
matching <- (hasProperty prop) `filterM` new -- new windows matching predecate
|
||||
let w1' = w1c ++ matching -- updated first pane windows
|
||||
w2' = w2c ++ (new \\ matching) -- updated second pane windows
|
||||
s1 = differentiate f' w1' -- first pane stack
|
||||
s2 = differentiate f' w2' -- second pane stack
|
||||
([((),r1),((),r2)], msuper') <- runLayout (Workspace "" super superstack) rinput
|
||||
(wrs1, ml1') <- runLayout (Workspace "" l1 s1) r1
|
||||
(wrs2, ml2') <- runLayout (Workspace "" l2 s2) r2
|
||||
return (wrs1++wrs2, Just $ C2P f' w1' w2' (maybe super id msuper')
|
||||
(maybe l1 id ml1') (maybe l2 id ml2') prop)
|
||||
|
||||
handleMessage us@(C2P f ws1 ws2 super l1 l2 prop) m
|
||||
| Just SwapWindow <- fromMessage m = swap us
|
||||
| Just (SwapWindowN 0) <- fromMessage m = swap us
|
||||
| Just (SwapWindowN n) <- fromMessage m = forwardToFocused us $ SomeMessage $ SwapWindowN $ n-1
|
||||
|
||||
| Just (MoveWindowToWindow w1 w2) <- fromMessage m,
|
||||
w1 `elem` ws1,
|
||||
w2 `elem` ws2 = return $ Just $ C2P f (delete w1 ws1) (w1:ws2) super l1 l2 prop
|
||||
|
||||
| Just (MoveWindowToWindow w1 w2) <- fromMessage m,
|
||||
w1 `elem` ws2,
|
||||
w2 `elem` ws1 = return $ Just $ C2P f (w1:ws1) (delete w1 ws2) super l1 l2 prop
|
||||
|
||||
| otherwise = do ml1' <- handleMessage l1 m
|
||||
ml2' <- handleMessage l2 m
|
||||
msuper' <- handleMessage super m
|
||||
if isJust msuper' || isJust ml1' || isJust ml2'
|
||||
then return $ Just $ C2P f ws1 ws2
|
||||
(maybe super id msuper')
|
||||
(maybe l1 id ml1')
|
||||
(maybe l2 id ml2') prop
|
||||
else return Nothing
|
||||
|
||||
description (C2P _ _ _ super l1 l2 prop) = "combining " ++ description l1 ++ " and "++
|
||||
description l2 ++ " with " ++ description super ++ " using "++ (show prop)
|
||||
|
||||
-- send focused window to the other pane. Does nothing if we don't
|
||||
-- own the focused window
|
||||
swap :: (LayoutClass s a, LayoutClass l1 Window, LayoutClass l2 Window) =>
|
||||
CombineTwoP (s a) l1 l2 Window -> X (Maybe (CombineTwoP (s a) l1 l2 Window))
|
||||
swap (C2P f ws1 ws2 super l1 l2 prop) = do
|
||||
mst <- gets (W.stack . W.workspace . W.current . windowset)
|
||||
let (ws1', ws2') = case mst of
|
||||
Nothing -> (ws1, ws2)
|
||||
Just st -> if foc `elem` ws1
|
||||
then (foc `delete` ws1, foc:ws2)
|
||||
else if foc `elem` ws2
|
||||
then (foc:ws1, foc `delete` ws2)
|
||||
else (ws1, ws2)
|
||||
where foc = W.focus st
|
||||
if (ws1,ws2) == (ws1',ws2')
|
||||
then return Nothing
|
||||
else return $ Just $ C2P f ws1' ws2' super l1 l2 prop
|
||||
|
||||
|
||||
-- forwards the message to the sublayout which contains the focused window
|
||||
forwardToFocused :: (LayoutClass l1 Window, LayoutClass l2 Window, LayoutClass s a) =>
|
||||
CombineTwoP (s a) l1 l2 Window -> SomeMessage -> X (Maybe (CombineTwoP (s a) l1 l2 Window))
|
||||
forwardToFocused (C2P f ws1 ws2 super l1 l2 prop) m = do
|
||||
ml1 <- forwardIfFocused l1 ws1 m
|
||||
ml2 <- forwardIfFocused l2 ws2 m
|
||||
ms <- if isJust ml1 || isJust ml2
|
||||
then return Nothing
|
||||
else handleMessage super m
|
||||
if isJust ml1 || isJust ml2 || isJust ms
|
||||
then return $ Just $ C2P f ws1 ws2 (maybe super id ms) (maybe l1 id ml1) (maybe l2 id ml2) prop
|
||||
else return Nothing
|
||||
|
||||
-- forwards message m to layout l if focused window is among w
|
||||
forwardIfFocused :: (LayoutClass l Window) => l Window -> [Window] -> SomeMessage -> X (Maybe (l Window))
|
||||
forwardIfFocused l w m = do
|
||||
mst <- gets (W.stack . W.workspace . W.current . windowset)
|
||||
maybe (return Nothing) send mst where
|
||||
send st = if (W.focus st) `elem` w
|
||||
then handleMessage l m
|
||||
else return Nothing
|
||||
|
||||
-- code from CombineTwo
|
||||
-- given two sets of zs and xs takes the first z from zs that also belongs to xs
|
||||
-- and turns xs into a stack with z being current element. Acts as
|
||||
-- StackSet.differentiate if zs and xs don't intersect
|
||||
differentiate :: Eq q => [q] -> [q] -> Maybe (Stack q)
|
||||
differentiate (z:zs) xs | z `elem` xs = Just $ Stack { focus=z
|
||||
, up = reverse $ takeWhile (/=z) xs
|
||||
, down = tail $ dropWhile (/=z) xs }
|
||||
| otherwise = differentiate zs xs
|
||||
differentiate [] xs = W.differentiate xs
|
||||
|
||||
-- vim:ts=4:shiftwidth=4:softtabstop=4:expandtab:foldlevel=20:
|
112
XMonad/Layout/Cross.hs
Normal file
112
XMonad/Layout/Cross.hs
Normal file
@@ -0,0 +1,112 @@
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
|
||||
|
||||
-- |
|
||||
-- Module : XMonad.Layout.Cross
|
||||
-- Copyright : (c) Luis Cabellos <zhen.sydow@gmail.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Luis Cabellos <zhen.sydow@gmail.com>
|
||||
-- Stability : stable
|
||||
-- Portability : portable
|
||||
--
|
||||
-- A Cross Layout with the main window in the center.
|
||||
--
|
||||
module XMonad.Layout.Cross(
|
||||
-- * Usage
|
||||
-- $usage
|
||||
simpleCross
|
||||
, Cross(..) ) where
|
||||
|
||||
import XMonad( Dimension, Rectangle(..), LayoutClass(..), Resize(..), fromMessage )
|
||||
import XMonad.StackSet( focus, up, down )
|
||||
import Control.Monad( msum )
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Layout.Cross
|
||||
--
|
||||
-- Then edit your @layoutHook@ by adding one of the Cross layouts:
|
||||
--
|
||||
-- > myLayout = simpleCross ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayout }
|
||||
--
|
||||
|
||||
-- apply a factor to a Rectangle Dimension
|
||||
(<%>) :: Dimension -> Rational -> Dimension
|
||||
d <%> f = floor $ f * (fromIntegral d)
|
||||
|
||||
-- | The Cross Layout draws the focused window in the center of the screen
|
||||
-- and part of the other windows on the sides. The 'Shrink' and 'Expand'
|
||||
-- messages increment the size of the main window.
|
||||
--
|
||||
-- The focus keybindings change the center window, while other windows
|
||||
-- cycle through the side positions. With the Cross layout only four
|
||||
-- windows are shown around the focused window, two ups and two downs,
|
||||
-- no matter how many are in the current stack. I.e. focus down cycles the
|
||||
-- window below focused into the center; focus up cycles the window above.
|
||||
data Cross a = Cross {
|
||||
crossProp :: !Rational, -- ^ Proportion of screen occupied by the main window.
|
||||
crossInc :: !Rational -- ^ Percent of main window to increment by when resizing.
|
||||
}
|
||||
deriving( Show, Read )
|
||||
|
||||
-- | A simple Cross Layout. It places the focused window in the center.
|
||||
-- The proportion of the screen used by the main window is 4\/5.
|
||||
simpleCross :: Cross a
|
||||
simpleCross = Cross (4/5) (1/100)
|
||||
|
||||
instance LayoutClass Cross a where
|
||||
pureLayout (Cross f _) r s = [(focus s, mainRect r f)] ++
|
||||
(zip winCycle (upRects r f)) ++
|
||||
(zip (reverse winCycle) (downRects r f))
|
||||
where winCycle = (up s) ++ (reverse (down s))
|
||||
|
||||
pureMessage (Cross f d) m = msum [fmap resize (fromMessage m)]
|
||||
where resize Shrink = Cross (max (1/100) $ f - d) d
|
||||
resize Expand = Cross (min 1 $ f + d) d
|
||||
|
||||
description _ = "Cross"
|
||||
|
||||
-- get the Rectangle for the focused window
|
||||
mainRect :: Rectangle -> Rational -> Rectangle
|
||||
mainRect (Rectangle rx ry rw rh) f = Rectangle
|
||||
(rx + (fromIntegral (rw <%> invf)))
|
||||
(ry + (fromIntegral (rh <%> invf)))
|
||||
(rw <%> f) (rh <%> f)
|
||||
where invf = (1/2) * (1-f)
|
||||
|
||||
-- get the rectangles for the up windows
|
||||
upRects :: Rectangle -> Rational -> [Rectangle]
|
||||
upRects r f = [topRectangle r nf, rightRectangle r nf]
|
||||
where nf = f * (8/10)
|
||||
|
||||
-- get the rectangles for the down windows
|
||||
downRects :: Rectangle -> Rational -> [Rectangle]
|
||||
downRects r f = [bottomRectangle r nf, leftRectangle r nf]
|
||||
where nf = f * (8/10)
|
||||
|
||||
topRectangle :: Rectangle -> Rational -> Rectangle
|
||||
topRectangle (Rectangle rx ry rw rh) f = Rectangle
|
||||
(rx + (fromIntegral (rw <%> ((1-f)*(1/2)))))
|
||||
ry
|
||||
(rw <%> f) (rh <%> ((1-f)*(1/2)))
|
||||
|
||||
rightRectangle :: Rectangle -> Rational -> Rectangle
|
||||
rightRectangle (Rectangle rx ry rw rh) f = Rectangle
|
||||
(rx + (fromIntegral (rw - (rw <%> (1/2)))))
|
||||
(ry + (fromIntegral (rh <%> ((1-f)*(1/2)))))
|
||||
(rw <%> (1/2)) (rh <%> f)
|
||||
|
||||
bottomRectangle :: Rectangle -> Rational -> Rectangle
|
||||
bottomRectangle (Rectangle rx ry rw rh) f = Rectangle
|
||||
(rx + (fromIntegral (rw <%> ((1-f)*(1/2)))))
|
||||
(ry + (fromIntegral (rh - (rh <%> ((1-f)*(1/2))))))
|
||||
(rw <%> f) (rh <%> ((1-f)*(1/2)))
|
||||
|
||||
leftRectangle :: Rectangle -> Rational -> Rectangle
|
||||
leftRectangle (Rectangle rx ry rw rh) f = Rectangle
|
||||
rx
|
||||
(ry + (fromIntegral (rh <%> ((1-f)*(1/2)))))
|
||||
(rw <%> (1/2)) (rh <%> f)
|
||||
|
@@ -1,5 +1,4 @@
|
||||
{-# OPTIONS_GHC -fglasgow-exts #-} -- for deriving Typeable
|
||||
{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
|
||||
{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.Decoration
|
||||
@@ -26,6 +25,7 @@ module XMonad.Layout.Decoration
|
||||
, Shrinker (..), DefaultShrinker
|
||||
, shrinkText, CustomShrink ( CustomShrink ), shrinkWhile
|
||||
, isInStack, isVisible, isInvisible, isWithin, fi
|
||||
, findWindowByDecoration
|
||||
, module XMonad.Layout.LayoutModifier
|
||||
) where
|
||||
|
||||
@@ -307,6 +307,9 @@ lookFor w ((wr,(Just dw,dr)):dwrs) | w == dw = Just (wr,(dw,dr))
|
||||
lookFor w ((_, (Nothing, _)):dwrs) = lookFor w dwrs
|
||||
lookFor _ [] = Nothing
|
||||
|
||||
findWindowByDecoration :: Window -> DecorationState -> Maybe (OrigWin,(Window,Maybe Rectangle))
|
||||
findWindowByDecoration w ds = lookFor w (decos ds)
|
||||
|
||||
-- | Initialize the 'DecorationState' by initializing the font
|
||||
-- structure and by creating the needed decorations.
|
||||
initState :: DecorationStyle ds Window => Theme -> ds Window -> Rectangle
|
||||
|
@@ -127,8 +127,8 @@ import XMonad.Layout.SimpleFloat
|
||||
-- up the key bindings, please read the documentation of
|
||||
-- "XMonad.Layout.WindowArranger"
|
||||
--
|
||||
-- The deafult theme can be dynamically change with the xmonad theme
|
||||
-- selector. See "XMonad.Prompt.Theme". For more themse, look at
|
||||
-- The default theme can be dynamically change with the xmonad theme
|
||||
-- selector. See "XMonad.Prompt.Theme". For more themes, look at
|
||||
-- "XMonad.Util.Themes"
|
||||
|
||||
-- $circle
|
||||
@@ -541,12 +541,12 @@ mirrorTallTabbed s t = tabBar s t Top (resizeVertical (fi $ decoHeight t) mirror
|
||||
--
|
||||
-- <http://code.haskell.org/~arossato/xmonadShots/floatSimpleSimple.png>
|
||||
floatSimpleSimple :: (Show a, Eq a) => ModifiedLayout (Decoration SimpleDecoration DefaultShrinker)
|
||||
(ModifiedLayout MouseResize (ModifiedLayout WindowArranger SimpleFloat)) a
|
||||
(ModifiedLayout MouseResize (ModifiedLayout WindowArranger SimpleFloat)) a
|
||||
floatSimpleSimple = simpleFloat
|
||||
|
||||
floatSimple :: (Show a, Eq a, Shrinker s) => s -> Theme ->
|
||||
ModifiedLayout (Decoration SimpleDecoration s)
|
||||
(ModifiedLayout MouseResize (ModifiedLayout WindowArranger SimpleFloat)) a
|
||||
(ModifiedLayout MouseResize (ModifiedLayout WindowArranger SimpleFloat)) a
|
||||
floatSimple = simpleFloat'
|
||||
|
||||
-- | This version is decorated with the 'DefaultDecoration' style.
|
||||
@@ -555,14 +555,14 @@ floatSimple = simpleFloat'
|
||||
--
|
||||
-- <http://code.haskell.org/~arossato/xmonadShots/floatSimpleDefault.png>
|
||||
floatSimpleDefault :: (Show a, Eq a) => ModifiedLayout (Decoration DefaultDecoration DefaultShrinker)
|
||||
(ModifiedLayout MouseResize (ModifiedLayout WindowArranger SimpleFloat)) a
|
||||
(ModifiedLayout MouseResize (ModifiedLayout WindowArranger SimpleFloat)) a
|
||||
floatSimpleDefault = decoration shrinkText defaultTheme DefaultDecoration (mouseResize $ windowArrangeAll $ SF 20)
|
||||
|
||||
-- | Same as 'floatSimpleDefault', but with the possibility of setting a
|
||||
-- custom shrinker and a custom theme.
|
||||
floatDefault :: (Show a, Eq a, Shrinker s) => s -> Theme ->
|
||||
ModifiedLayout (Decoration DefaultDecoration s)
|
||||
(ModifiedLayout MouseResize (ModifiedLayout WindowArranger SimpleFloat)) a
|
||||
(ModifiedLayout MouseResize (ModifiedLayout WindowArranger SimpleFloat)) a
|
||||
floatDefault s t = decoration s t DefaultDecoration (mouseResize $ windowArrangeAll $ SF (decoHeight t))
|
||||
|
||||
-- | This version is decorated with the 'DwmStyle'. Note that this is
|
||||
@@ -572,14 +572,14 @@ floatDefault s t = decoration s t DefaultDecoration (mouseResize $ windowArrange
|
||||
--
|
||||
-- <http://code.haskell.org/~arossato/xmonadShots/floatSimpleDwmStyle.png>
|
||||
floatSimpleDwmStyle :: (Show a, Eq a) => ModifiedLayout (Decoration DwmStyle DefaultShrinker)
|
||||
(ModifiedLayout MouseResize (ModifiedLayout WindowArranger SimpleFloat)) a
|
||||
(ModifiedLayout MouseResize (ModifiedLayout WindowArranger SimpleFloat)) a
|
||||
floatSimpleDwmStyle = decoration shrinkText defaultTheme Dwm (mouseResize $ windowArrangeAll $ SF 20)
|
||||
|
||||
-- | Same as 'floatSimpleDwmStyle', but with the possibility of setting a
|
||||
-- custom shrinker and a custom theme.
|
||||
floatDwmStyle :: (Show a, Eq a, Shrinker s) => s -> Theme ->
|
||||
ModifiedLayout (Decoration DwmStyle s)
|
||||
(ModifiedLayout MouseResize (ModifiedLayout WindowArranger SimpleFloat)) a
|
||||
(ModifiedLayout MouseResize (ModifiedLayout WindowArranger SimpleFloat)) a
|
||||
floatDwmStyle s t = decoration s t Dwm (mouseResize $ windowArrangeAll $ SF (decoHeight t))
|
||||
|
||||
-- | This version is decorated with the 'TabbedDecoration' style.
|
||||
@@ -589,12 +589,12 @@ floatDwmStyle s t = decoration s t Dwm (mouseResize $ windowArrangeAll $ SF (dec
|
||||
--
|
||||
-- <http://code.haskell.org/~arossato/xmonadShots/floatSimpleTabbed.png>
|
||||
floatSimpleTabbed :: (Show a, Eq a) => ModifiedLayout (Decoration TabBarDecoration DefaultShrinker)
|
||||
(ModifiedLayout MouseResize (ModifiedLayout WindowArranger SimpleFloat)) a
|
||||
(ModifiedLayout MouseResize (ModifiedLayout WindowArranger SimpleFloat)) a
|
||||
floatSimpleTabbed = tabBar shrinkText defaultTheme Top (mouseResize $ windowArrangeAll $ SF 20)
|
||||
|
||||
-- | Same as 'floatSimpleTabbed', but with the possibility of setting a
|
||||
-- custom shrinker and a custom theme.
|
||||
floatTabbed :: (Show a, Eq a, Shrinker s) => s -> Theme ->
|
||||
ModifiedLayout (Decoration TabBarDecoration s)
|
||||
(ModifiedLayout MouseResize (ModifiedLayout WindowArranger SimpleFloat)) a
|
||||
(ModifiedLayout MouseResize (ModifiedLayout WindowArranger SimpleFloat)) a
|
||||
floatTabbed s t = tabBar s t Top (mouseResize $ windowArrangeAll $ SF (decoHeight t))
|
||||
|
@@ -33,8 +33,8 @@ import Control.Monad (ap)
|
||||
--
|
||||
-- Then edit your @layoutHook@ by adding the Dishes layout:
|
||||
--
|
||||
-- > myLayouts = Dishes 2 (1/6) ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayouts }
|
||||
-- > myLayout = Dishes 2 (1/6) ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayout }
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
|
@@ -1,5 +1,4 @@
|
||||
{-# OPTIONS_GHC -fglasgow-exts #-} -- For deriving Data/Typeable
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
|
||||
{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
@@ -8,7 +7,7 @@
|
||||
-- David Roundy <droundy@darcs.net>,
|
||||
-- Andrea Rossato <andrea.rossato@unibz.it>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
--
|
||||
-- Maintainer : Andrea Rossato <andrea.rossato@unibz.it>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
@@ -30,7 +29,7 @@ module XMonad.Layout.DragPane (
|
||||
import XMonad
|
||||
import Data.Unique
|
||||
|
||||
import qualified XMonad.StackSet as W
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad.Util.Invisible
|
||||
import XMonad.Util.XUtils
|
||||
|
||||
@@ -41,8 +40,8 @@ import XMonad.Util.XUtils
|
||||
--
|
||||
-- Then edit your @layoutHook@ by adding the DragPane layout:
|
||||
--
|
||||
-- > myLayouts = dragPane Horizontal 0.1 0.5 ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayouts }
|
||||
-- > myLayout = dragPane Horizontal 0.1 0.5 ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayout }
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
@@ -57,8 +56,8 @@ handleColor = "#000000"
|
||||
dragPane :: DragType -> Double -> Double -> DragPane a
|
||||
dragPane t x y = DragPane (I Nothing) t x y
|
||||
|
||||
data DragPane a =
|
||||
DragPane (Invisible Maybe (Window,Rectangle,Int)) DragType Double Double
|
||||
data DragPane a =
|
||||
DragPane (Invisible Maybe (Window,Rectangle,Int)) DragType Double Double
|
||||
deriving ( Show, Read )
|
||||
|
||||
data DragType = Horizontal | Vertical deriving ( Show, Read )
|
||||
@@ -87,7 +86,7 @@ handleMess d@(DragPane mb@(I (Just (win,_,ident))) ty delta split) x
|
||||
handleMess _ _ = return Nothing
|
||||
|
||||
handleEvent :: DragPane a -> Event -> X ()
|
||||
handleEvent (DragPane (I (Just (win,r,ident))) ty _ _)
|
||||
handleEvent (DragPane (I (Just (win,r,ident))) ty _ _)
|
||||
(ButtonEvent {ev_window = thisw, ev_subwindow = thisbw, ev_event_type = t })
|
||||
| t == buttonPress && thisw == win || thisbw == win = do
|
||||
mouseDrag (\ex ey -> do
|
||||
@@ -115,12 +114,12 @@ doLay mirror (DragPane mw ty delta split) r s = do
|
||||
[] -> case W.down s of
|
||||
(next:_) -> [(W.focus s,left),(next,right)]
|
||||
[] -> [(W.focus s, r)]
|
||||
if length wrs > 1
|
||||
if length wrs > 1
|
||||
then case mw of
|
||||
I (Just (w,_,ident)) -> do
|
||||
I (Just (w,_,ident)) -> do
|
||||
w' <- deleteWindow w >> newDragWin handr
|
||||
return (wrs, Just $ DragPane (I $ Just (w',r',ident)) ty delta split)
|
||||
I Nothing -> do
|
||||
I Nothing -> do
|
||||
w <- newDragWin handr
|
||||
i <- io $ newUnique
|
||||
return (wrs, Just $ DragPane (I $ Just (w,r',hashUnique i)) ty delta split)
|
||||
|
@@ -42,8 +42,8 @@ import XMonad.StackSet as W
|
||||
--
|
||||
-- Then edit your @layoutHook@ by adding the FixedColumn layout:
|
||||
--
|
||||
-- > myLayouts = FixedColumn 20 80 10 ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayouts }
|
||||
-- > myLayout = FixedColumn 1 20 80 10 ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayout }
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
|
@@ -1,8 +1,4 @@
|
||||
{-# OPTIONS_GHC -fglasgow-exts #-}
|
||||
|
||||
-- for now, use -fglasgow-exts for compatibility with ghc 6.6, which chokes
|
||||
-- on some of the LANGUAGE pragmas below
|
||||
{- LANGUAGE FlexibleInstances, MultiParamTypeClasses, DeriveDataTypeable, TypeSynonymInstances -}
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, DeriveDataTypeable, TypeSynonymInstances, PatternGuards #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
@@ -32,7 +28,7 @@
|
||||
module XMonad.Layout.Gaps (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
Direction(..),
|
||||
Direction2D(..),
|
||||
GapSpec, gaps, GapMessage(..)
|
||||
|
||||
) where
|
||||
@@ -40,8 +36,8 @@ module XMonad.Layout.Gaps (
|
||||
import XMonad.Core
|
||||
import Graphics.X11 (Rectangle(..))
|
||||
|
||||
import XMonad.Hooks.ManageDocks (Direction(..))
|
||||
import XMonad.Layout.LayoutModifier
|
||||
import XMonad.Util.Types (Direction2D(..))
|
||||
|
||||
import Data.List (delete)
|
||||
|
||||
@@ -58,10 +54,10 @@ import Data.List (delete)
|
||||
-- You can additionally add some keybindings to toggle or modify the gaps,
|
||||
-- for example:
|
||||
--
|
||||
-- > , ((modMask x .|. controlMask, xK_g), sendMessage $ ToggleGaps) -- toggle all gaps
|
||||
-- > , ((modMask x .|. controlMask, xK_t), sendMessage $ ToggleGap U) -- toggle the top gap
|
||||
-- > , ((modMask x .|. controlMask, xK_w), sendMessage $ IncGap R 5) -- increment the right-hand gap
|
||||
-- > , ((modMask x .|. controlMask, xK_q), sendMessage $ DecGap R 5) -- decrement the right-hand gap
|
||||
-- > , ((modm .|. controlMask, xK_g), sendMessage $ ToggleGaps) -- toggle all gaps
|
||||
-- > , ((modm .|. controlMask, xK_t), sendMessage $ ToggleGap U) -- toggle the top gap
|
||||
-- > , ((modm .|. controlMask, xK_w), sendMessage $ IncGap R 5) -- increment the right-hand gap
|
||||
-- > , ((modm .|. controlMask, xK_q), sendMessage $ DecGap R 5) -- decrement the right-hand gap
|
||||
--
|
||||
-- If you want complete control over all gaps, you could include
|
||||
-- something like this in your keybindings, assuming in this case you
|
||||
@@ -83,19 +79,19 @@ import Data.List (delete)
|
||||
|
||||
-- | A manual gap configuration. Each side of the screen on which a
|
||||
-- gap is enabled is paired with a size in pixels.
|
||||
type GapSpec = [(Direction,Int)]
|
||||
type GapSpec = [(Direction2D,Int)]
|
||||
|
||||
-- | The gap state. The first component is the configuration (which
|
||||
-- gaps are allowed, and their current size), the second is the gaps
|
||||
-- which are currently active.
|
||||
data Gaps a = Gaps GapSpec [Direction]
|
||||
data Gaps a = Gaps GapSpec [Direction2D]
|
||||
deriving (Show, Read)
|
||||
|
||||
-- | Messages which can be sent to a gap modifier.
|
||||
data GapMessage = ToggleGaps -- ^ Toggle all gaps.
|
||||
| ToggleGap !Direction -- ^ Toggle a single gap.
|
||||
| IncGap !Int !Direction -- ^ Increase a gap by a certain number of pixels.
|
||||
| DecGap !Int !Direction -- ^ Decrease a gap.
|
||||
| ToggleGap !Direction2D -- ^ Toggle a single gap.
|
||||
| IncGap !Int !Direction2D -- ^ Increase a gap by a certain number of pixels.
|
||||
| DecGap !Int !Direction2D -- ^ Decrease a gap.
|
||||
deriving (Typeable)
|
||||
|
||||
instance Message GapMessage
|
||||
@@ -125,16 +121,16 @@ applyGaps gs r = foldr applyGap r (activeGaps gs)
|
||||
activeGaps :: Gaps a -> GapSpec
|
||||
activeGaps (Gaps conf cur) = filter ((`elem` cur) . fst) conf
|
||||
|
||||
toggleGaps :: GapSpec -> [Direction] -> [Direction]
|
||||
toggleGaps :: GapSpec -> [Direction2D] -> [Direction2D]
|
||||
toggleGaps conf [] = map fst conf
|
||||
toggleGaps _ _ = []
|
||||
|
||||
toggleGap :: GapSpec -> [Direction] -> Direction -> [Direction]
|
||||
toggleGap :: GapSpec -> [Direction2D] -> Direction2D -> [Direction2D]
|
||||
toggleGap conf cur d | d `elem` cur = delete d cur
|
||||
| d `elem` (map fst conf) = d:cur
|
||||
| otherwise = cur
|
||||
|
||||
incGap :: GapSpec -> Direction -> Int -> GapSpec
|
||||
incGap :: GapSpec -> Direction2D -> Int -> GapSpec
|
||||
incGap gs d i = map (\(dir,j) -> if dir == d then (dir,max (j+i) 0) else (dir,j)) gs
|
||||
|
||||
fi :: (Num b, Integral a) => a -> b
|
||||
|
@@ -30,14 +30,14 @@ import XMonad.StackSet
|
||||
--
|
||||
-- Then edit your @layoutHook@ by adding the Grid layout:
|
||||
--
|
||||
-- > myLayouts = Grid ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayouts }
|
||||
-- > myLayout = Grid ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayout }
|
||||
--
|
||||
-- You can also specify an aspect ratio for Grid to strive for with the
|
||||
-- GridRatio constructor. For example, if you want Grid to try to make a grid
|
||||
-- four windows wide and three windows tall, you could use
|
||||
--
|
||||
-- > myLayouts = GridRatio (4/3) ||| etc.
|
||||
-- > myLayout = GridRatio (4/3) ||| etc.
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
|
@@ -22,6 +22,8 @@ module XMonad.Layout.GridVariants ( -- * Usage
|
||||
ChangeMasterGeom(..)
|
||||
, Grid(..)
|
||||
, TallGrid(..)
|
||||
, SplitGrid(..)
|
||||
, Orientation(..)
|
||||
) where
|
||||
|
||||
import Control.Monad
|
||||
@@ -31,7 +33,7 @@ import qualified XMonad.StackSet as W
|
||||
-- $usage
|
||||
-- This module can be used as follows:
|
||||
--
|
||||
-- > import XMonad.Layout.Master
|
||||
-- > import XMonad.Layout.GridVariants
|
||||
--
|
||||
-- Then add something like this to your layouts:
|
||||
--
|
||||
@@ -39,20 +41,20 @@ import qualified XMonad.StackSet as W
|
||||
--
|
||||
-- for a 16:10 aspect ratio grid, or
|
||||
--
|
||||
-- > TallGrid 2 3 (2/3) (16/10) (5/100)
|
||||
-- > SplitGrid L 2 3 (2/3) (16/10) (5/100)
|
||||
--
|
||||
-- for a layout with a 2x3 master grid that uses 2/3 of the screen,
|
||||
-- and a 16:10 aspect ratio slave grid. The last parameter is again
|
||||
-- the percentage by which the split between master and slave area
|
||||
-- changes in response to Expand/Shrink messages.
|
||||
-- and a 16:10 aspect ratio slave grid to its right. The last
|
||||
-- parameter is again the percentage by which the split between master
|
||||
-- and slave area changes in response to Expand/Shrink messages.
|
||||
--
|
||||
-- To be able to change the geometry of the master grid, add something
|
||||
-- like this to your keybindings:
|
||||
--
|
||||
-- > ((modMask .|. shiftMask, xK_equal), sendMessage $ IncMasterCols 1),
|
||||
-- > ((modMask .|. shiftMask, xK_minus), sendMessage $ IncMasterCols (-1)),
|
||||
-- > ((modMask .|. ctrlMask, xK_equal), sendMessage $ IncMasterRows 1),
|
||||
-- > ((modMask .|. ctrlMask, xK_minus), sendMessage $ IncMasterRows (-1))
|
||||
-- > ((modm .|. shiftMask, xK_equal), sendMessage $ IncMasterCols 1),
|
||||
-- > ((modm .|. shiftMask, xK_minus), sendMessage $ IncMasterCols (-1)),
|
||||
-- > ((modm .|. controlMask, xK_equal), sendMessage $ IncMasterRows 1),
|
||||
-- > ((modm .|. controlMask, xK_minus), sendMessage $ IncMasterRows (-1))
|
||||
|
||||
-- | Grid layout. The parameter is the desired x:y aspect ratio of windows
|
||||
data Grid a = Grid !Rational
|
||||
@@ -68,29 +70,35 @@ instance LayoutClass Grid a where
|
||||
|
||||
description _ = "Grid"
|
||||
|
||||
-- | TallGrid layout. Parameters are
|
||||
-- | SplitGrid layout. Parameters are
|
||||
--
|
||||
-- - side where the master is
|
||||
-- - number of master rows
|
||||
-- - number of master columns
|
||||
-- - portion of screen used for master grid
|
||||
-- - x:y aspect ratio of slave windows
|
||||
-- - increment for resize messages
|
||||
data TallGrid a = TallGrid !Int !Int !Rational !Rational !Rational
|
||||
deriving (Read, Show)
|
||||
data SplitGrid a = SplitGrid Orientation !Int !Int !Rational !Rational !Rational
|
||||
deriving (Read, Show)
|
||||
|
||||
instance LayoutClass TallGrid a where
|
||||
-- | Type to specify the side of the screen that holds
|
||||
-- the master area of a SplitGrid.
|
||||
data Orientation = T | B | L | R
|
||||
deriving (Eq, Read, Show)
|
||||
|
||||
pureLayout (TallGrid mrows mcols mfrac saspect _) rect st = zip wins rects
|
||||
instance LayoutClass SplitGrid a where
|
||||
|
||||
pureLayout (SplitGrid o mrows mcols mfrac saspect _) rect st = zip wins rects
|
||||
where
|
||||
wins = W.integrate st
|
||||
nwins = length wins
|
||||
rects = arrangeTallGrid rect nwins mrows mcols mfrac saspect
|
||||
rects = arrangeSplitGrid rect o nwins mrows mcols mfrac saspect
|
||||
|
||||
pureMessage layout msg =
|
||||
msum [ fmap (resizeMaster layout) (fromMessage msg)
|
||||
, fmap (changeMasterGrid layout) (fromMessage msg) ]
|
||||
|
||||
description _ = "TallGrid"
|
||||
description _ = "SplitGrid"
|
||||
|
||||
-- |The geometry change message understood by the master grid
|
||||
data ChangeMasterGeom
|
||||
@@ -100,19 +108,25 @@ data ChangeMasterGeom
|
||||
|
||||
instance Message ChangeMasterGeom
|
||||
|
||||
arrangeTallGrid :: Rectangle -> Int -> Int -> Int -> Rational -> Rational -> [Rectangle]
|
||||
arrangeTallGrid rect@(Rectangle rx ry rw rh) nwins mrows mcols mfrac saspect
|
||||
arrangeSplitGrid :: Rectangle -> Orientation -> Int -> Int -> Int -> Rational -> Rational -> [Rectangle]
|
||||
arrangeSplitGrid rect@(Rectangle rx ry rw rh) o nwins mrows mcols mfrac saspect
|
||||
| nwins <= mwins = arrangeMasterGrid rect nwins mcols
|
||||
| mwins == 0 = arrangeAspectGrid rect nwins saspect
|
||||
| otherwise = (arrangeMasterGrid mrect mwins mcols) ++
|
||||
(arrangeAspectGrid srect swins saspect)
|
||||
where
|
||||
mwins = mrows * mcols
|
||||
swins = nwins - mwins
|
||||
mrect = Rectangle rx ry rw mh
|
||||
srect = Rectangle rx (fromIntegral ry + fromIntegral mh) rw sh
|
||||
mh = ceiling (fromIntegral rh * mfrac)
|
||||
sh = rh - mh
|
||||
mwins = mrows * mcols
|
||||
swins = nwins - mwins
|
||||
mrect = Rectangle mx my mw mh
|
||||
srect = Rectangle sx sy sw sh
|
||||
(mh, sh, mw, sw) = if o `elem` [T, B] then
|
||||
(ceiling (fromIntegral rh * mfrac), rh - mh, rw, rw)
|
||||
else
|
||||
(rh, rh, ceiling (fromIntegral rw * mfrac), rw - mw)
|
||||
mx = fromIntegral rx + if o == R then fromIntegral sw else 0
|
||||
my = fromIntegral ry + if o == B then fromIntegral sh else 0
|
||||
sx = fromIntegral rx + if o == L then fromIntegral mw else 0
|
||||
sy = fromIntegral ry + if o == T then fromIntegral mh else 0
|
||||
|
||||
arrangeMasterGrid :: Rectangle -> Int -> Int -> [Rectangle]
|
||||
arrangeMasterGrid rect nwins mcols = arrangeGrid rect nwins (min nwins mcols)
|
||||
@@ -121,8 +135,18 @@ arrangeAspectGrid :: Rectangle -> Int -> Rational -> [Rectangle]
|
||||
arrangeAspectGrid rect@(Rectangle _ _ rw rh) nwins aspect =
|
||||
arrangeGrid rect nwins (min nwins ncols)
|
||||
where
|
||||
ncols = ceiling $ sqrt $ ( fromRational
|
||||
( (fromIntegral rw * fromIntegral nwins) / (fromIntegral rh * aspect) ) :: Double)
|
||||
scr_a = fromIntegral rw / fromIntegral rh
|
||||
fcols = sqrt ( fromRational $ scr_a * fromIntegral nwins / aspect ) :: Double
|
||||
cols1 = floor fcols :: Int
|
||||
cols2 = ceiling fcols :: Int
|
||||
rows1 = ceiling ( fromIntegral nwins / fromIntegral cols1 :: Rational ) :: Int
|
||||
rows2 = floor ( fromIntegral nwins / fromIntegral cols2 :: Rational ) :: Int
|
||||
a1 = scr_a * fromIntegral rows1 / fromIntegral cols1
|
||||
a2 = scr_a * fromIntegral rows2 / fromIntegral cols2
|
||||
ncols | cols1 == 0 = cols2
|
||||
| rows2 == 0 = cols1
|
||||
| a1 / aspect < aspect / a2 = cols1
|
||||
| otherwise = cols2
|
||||
|
||||
arrangeGrid :: Rectangle -> Int -> Int -> [Rectangle]
|
||||
arrangeGrid (Rectangle rx ry rw rh) nwins ncols =
|
||||
@@ -153,14 +177,48 @@ splitEvenly n parts = [ sz-off | (sz,off) <- zip sizes offsets]
|
||||
sizes = [i*size | i <- [1..parts]]
|
||||
offsets = (take (fromIntegral extra) [1..]) ++ [extra,extra..]
|
||||
|
||||
resizeMaster :: TallGrid a -> Resize -> TallGrid a
|
||||
resizeMaster (TallGrid mrows mcols mfrac saspect delta) Shrink =
|
||||
TallGrid mrows mcols (max 0 (mfrac - delta)) saspect delta
|
||||
resizeMaster (TallGrid mrows mcols mfrac saspect delta) Expand =
|
||||
TallGrid mrows mcols (min 1 (mfrac + delta)) saspect delta
|
||||
resizeMaster :: SplitGrid a -> Resize -> SplitGrid a
|
||||
resizeMaster (SplitGrid o mrows mcols mfrac saspect delta) Shrink =
|
||||
SplitGrid o mrows mcols (max 0 (mfrac - delta)) saspect delta
|
||||
resizeMaster (SplitGrid o mrows mcols mfrac saspect delta) Expand =
|
||||
SplitGrid o mrows mcols (min 1 (mfrac + delta)) saspect delta
|
||||
|
||||
changeMasterGrid :: TallGrid a -> ChangeMasterGeom -> TallGrid a
|
||||
changeMasterGrid (TallGrid mrows mcols mfrac saspect delta) (IncMasterRows d) =
|
||||
TallGrid (max 0 (mrows + d)) mcols mfrac saspect delta
|
||||
changeMasterGrid (TallGrid mrows mcols mfrac saspect delta) (IncMasterCols d) =
|
||||
TallGrid mrows (max 0 (mcols + d)) mfrac saspect delta
|
||||
changeMasterGrid :: SplitGrid a -> ChangeMasterGeom -> SplitGrid a
|
||||
changeMasterGrid (SplitGrid o mrows mcols mfrac saspect delta) (IncMasterRows d) =
|
||||
SplitGrid o (max 0 (mrows + d)) mcols mfrac saspect delta
|
||||
changeMasterGrid (SplitGrid o mrows mcols mfrac saspect delta) (IncMasterCols d) =
|
||||
SplitGrid o mrows (max 0 (mcols + d)) mfrac saspect delta
|
||||
|
||||
-- | TallGrid layout. Parameters are
|
||||
--
|
||||
-- - number of master rows
|
||||
-- - number of master columns
|
||||
-- - portion of screen used for master grid
|
||||
-- - x:y aspect ratio of slave windows
|
||||
-- - increment for resize messages
|
||||
--
|
||||
-- This exists mostly because it was introduced in an earlier version.
|
||||
-- It's a fairly thin wrapper around "SplitGrid L".
|
||||
data TallGrid a = TallGrid !Int !Int !Rational !Rational !Rational
|
||||
deriving (Read, Show)
|
||||
|
||||
instance LayoutClass TallGrid a where
|
||||
|
||||
pureLayout (TallGrid mrows mcols mfrac saspect _) rect st = zip wins rects
|
||||
where
|
||||
wins = W.integrate st
|
||||
nwins = length wins
|
||||
rects = arrangeSplitGrid rect L nwins mrows mcols mfrac saspect
|
||||
|
||||
pureMessage layout msg =
|
||||
msum [ fmap ((tallGridAdapter resizeMaster) layout) (fromMessage msg)
|
||||
, fmap ((tallGridAdapter changeMasterGrid) layout) (fromMessage msg) ]
|
||||
|
||||
description _ = "TallGrid"
|
||||
|
||||
tallGridAdapter :: (SplitGrid a -> b -> SplitGrid a) -> TallGrid a -> b -> TallGrid a
|
||||
tallGridAdapter f (TallGrid mrows mcols mfrac saspect delta) msg =
|
||||
TallGrid mrows' mcols' mfrac' saspect' delta'
|
||||
where
|
||||
SplitGrid _ mrows' mcols' mfrac' saspect' delta' =
|
||||
f (SplitGrid L mrows mcols mfrac saspect delta) msg
|
||||
|
@@ -41,13 +41,13 @@ infixr 9 .
|
||||
--
|
||||
-- Then edit your @layoutHook@ by adding the 'Grid' layout:
|
||||
--
|
||||
-- > myLayouts = Grid False ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayouts }
|
||||
-- > myLayout = Grid False ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayout }
|
||||
--
|
||||
-- You can also specify an aspect ratio for Grid to strive for with the
|
||||
-- GridRatio constructor:
|
||||
--
|
||||
-- > myLayouts = GridRatio (4/3) False ||| etc.
|
||||
-- > myLayout = GridRatio (4/3) False ||| etc.
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see
|
||||
-- "XMonad.Doc.Extending#Editing_the_layout_hook".
|
||||
|
@@ -10,7 +10,7 @@
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Layout modfier suitable for workspace with multi-windowed instant messanger
|
||||
-- Layout modfier suitable for workspace with multi-windowed instant messenger
|
||||
-- (like Psi or Tkabber).
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
@@ -45,8 +45,8 @@ import XMonad.Util.WindowProperties
|
||||
-- for managing your chat windows (Grid in this example, another useful choice
|
||||
-- to consider is Tabbed layout).
|
||||
--
|
||||
-- > myLayouts = withIM (1%7) (ClassName "Tkabber") Grid ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayouts }
|
||||
-- > myLayout = withIM (1%7) (ClassName "Tkabber") Grid ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayout }
|
||||
--
|
||||
-- Here @1%7@ is the part of the screen which your roster will occupy,
|
||||
-- @ClassName \"Tkabber\"@ tells xmonad which window is actually your roster.
|
||||
|
104
XMonad/Layout/IndependentScreens.hs
Normal file
104
XMonad/Layout/IndependentScreens.hs
Normal file
@@ -0,0 +1,104 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.IndependentScreens
|
||||
-- Copyright : (c) 2009 Daniel Wagner
|
||||
-- License : BSD3
|
||||
--
|
||||
-- Maintainer : <daniel@wagner-home.com>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Utility functions for simulating independent sets of workspaces on
|
||||
-- each screen (like dwm's workspace model), using internal tags to
|
||||
-- distinguish workspaces associated with each screen.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Layout.IndependentScreens (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
VirtualWorkspace, PhysicalWorkspace,
|
||||
workspaces',
|
||||
withScreens, onCurrentScreen,
|
||||
countScreens,
|
||||
marshall, unmarshall
|
||||
) where
|
||||
|
||||
-- for the screen stuff
|
||||
import Control.Arrow hiding ((|||))
|
||||
import Control.Monad
|
||||
import Control.Monad.Instances
|
||||
import Data.List
|
||||
import Graphics.X11.Xinerama
|
||||
import XMonad
|
||||
import XMonad.StackSet hiding (workspaces)
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Layout.IndependentScreens
|
||||
--
|
||||
-- You can define your workspaces by calling @withScreens@:
|
||||
--
|
||||
-- > myConfig = defaultConfig { workspaces = withScreens 2 ["web", "email", "irc"] }
|
||||
--
|
||||
-- This will create \"physical\" workspaces with distinct internal names for
|
||||
-- each (screen, virtual workspace) pair.
|
||||
--
|
||||
-- Then edit any keybindings that use the list of workspaces or refer
|
||||
-- to specific workspace names. In the default configuration, only
|
||||
-- the keybindings for changing workspace do this:
|
||||
--
|
||||
-- > [((m .|. modm, k), windows $ f i)
|
||||
-- > | (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9]
|
||||
-- > , (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
|
||||
--
|
||||
-- This should change to
|
||||
--
|
||||
-- > [((m .|. modm, k), windows $ onCurrentScreen f i)
|
||||
-- > | (i, k) <- zip (workspaces' conf) [xK_1 .. xK_9]
|
||||
-- > , (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
|
||||
--
|
||||
-- In particular, the analogue of @XMonad.workspaces@ is
|
||||
-- @workspaces'@, and you can use @onCurrentScreen@ to convert functions
|
||||
-- of virtual workspaces to functions of physical workspaces, which work
|
||||
-- by marshalling the virtual workspace name and the currently focused
|
||||
-- screen into a physical workspace name.
|
||||
|
||||
type VirtualWorkspace = WorkspaceId
|
||||
type PhysicalWorkspace = WorkspaceId
|
||||
|
||||
marshall :: ScreenId -> VirtualWorkspace -> PhysicalWorkspace
|
||||
marshall (S sc) vws = show sc ++ '_':vws
|
||||
|
||||
unmarshall :: PhysicalWorkspace -> (ScreenId, VirtualWorkspace)
|
||||
unmarshall = ((S . read) *** drop 1) . break (=='_')
|
||||
|
||||
-- ^ You shouldn't need to use @marshall@ and @unmarshall@ very much.
|
||||
-- They simply convert between the physical and virtual worlds. For
|
||||
-- example, you might want to use them as part of a status bar
|
||||
-- configuration. The function @snd . unmarshall@ would discard the
|
||||
-- screen information from an otherwise unsightly workspace name.
|
||||
|
||||
workspaces' :: XConfig l -> [VirtualWorkspace]
|
||||
workspaces' = nub . map (snd . unmarshall) . workspaces
|
||||
|
||||
withScreens :: ScreenId -- ^ The number of screens to make workspaces for
|
||||
-> [VirtualWorkspace] -- ^ The desired virtual workspace names
|
||||
-> [PhysicalWorkspace] -- ^ A list of all internal physical workspace names
|
||||
withScreens n vws = [marshall sc pws | pws <- vws, sc <- [0..n-1]]
|
||||
|
||||
onCurrentScreen :: (VirtualWorkspace -> WindowSet -> a) -> (PhysicalWorkspace -> WindowSet -> a)
|
||||
onCurrentScreen f vws = screen . current >>= f . flip marshall vws
|
||||
|
||||
-- | In case you don't know statically how many screens there will be, you can call this in main before starting xmonad. For example, part of my config reads
|
||||
--
|
||||
-- > main = do
|
||||
-- > nScreens <- countScreens
|
||||
-- > xmonad $ defaultConfig {
|
||||
-- > ...
|
||||
-- > workspaces = withScreens nScreens (workspaces defaultConfig),
|
||||
-- > ...
|
||||
-- > }
|
||||
--
|
||||
countScreens :: (MonadIO m, Integral i) => m i
|
||||
countScreens = liftM genericLength . liftIO $ openDisplay "" >>= getScreenInfo
|
277
XMonad/Layout/LayoutBuilder.hs
Normal file
277
XMonad/Layout/LayoutBuilder.hs
Normal file
@@ -0,0 +1,277 @@
|
||||
{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances, PatternGuards, DeriveDataTypeable #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.LayoutBuilder
|
||||
-- Copyright : (c) 2009 Anders Engstrom <ankaan@gmail.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Anders Engstrom <ankaan@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- A layout combinator that sends a specified number of windows to one rectangle
|
||||
-- and the rest to another.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Layout.LayoutBuilder (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
layoutN,
|
||||
layoutR,
|
||||
layoutAll,
|
||||
IncLayoutN (..),
|
||||
SubMeasure (..),
|
||||
SubBox (..),
|
||||
absBox,
|
||||
relBox
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Layout
|
||||
import qualified XMonad.StackSet as W
|
||||
import Graphics.X11.Xlib
|
||||
import Data.Maybe (isJust)
|
||||
import Control.Monad
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Layout.LayoutBuilder
|
||||
--
|
||||
-- Then edit your @layoutHook@ by adding something like:
|
||||
--
|
||||
-- > myLayout = ( (layoutN 1 (relBox 0 0 0.5 1) (Just $ relBox 0 0 1 1) $ simpleTabbed)
|
||||
-- > $ (layoutAll (relBox 0.5 0 1 1) $ simpleTabbed)
|
||||
-- > ) |||
|
||||
-- > ( (layoutN 1 (relBox (1/3) 0 (1/2) 1) (Just $ relBox 0 0 1 1) $ Tall 0 0.01 0.5)
|
||||
-- > $ (layoutR 0.1 0.5 (relBox (2/3) 0 1 1) Nothing $ Tall 0 0.01 0.5)
|
||||
-- > $ (layoutAll (relBox 0 0 (1/3) 1) $ Tall 0 0.01 0.5)
|
||||
-- > ) |||
|
||||
-- > ( (layoutN 1 (absBox (-512-200) 0 512 0) (Just $ relBox 0 0 1 1) $ simpleTabbed)
|
||||
-- > $ (layoutN 1 (absBox (-200) 0 0 0) Nothing $ simpleTabbed)
|
||||
-- > $ (layoutAll (absBox 0 0 (-512-200) 0) $ simpleTabbed)
|
||||
-- > ) ||| Full ||| etc...
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayout }
|
||||
--
|
||||
-- This will produce a layout similar to DragPane, but with the possibility to have multiple windows in the left half
|
||||
-- and tabs that show the available windows. It will also produce a layout similar to ThreeColMid and a special layout
|
||||
-- created for use with a 80 columns wide Emacs window, its sidebar and a tabbed area for all other windows.
|
||||
--
|
||||
-- This module can be used to create many different custom layouts, but there are limitations. The primary limitation
|
||||
-- can be observed in the second and third example when there are only two columns with windows in them. The leftmost
|
||||
-- area is left blank. These blank areas can be avoided by placing the rectangles appropriately.
|
||||
--
|
||||
-- These examples require "XMonad.Layout.Tabbed".
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
-- "XMonad.Doc.Extending#Editing_the_layout_hook"
|
||||
--
|
||||
-- You may wish to add the following keybindings:
|
||||
--
|
||||
-- > , ((modm .|. shiftMask, xK_h ), sendMessage $ IncLayoutN (-1))
|
||||
-- > , ((modm .|. shiftMask, xK_l ), sendMessage $ IncLayoutN 1)
|
||||
--
|
||||
-- For detailed instruction on editing the key binding see:
|
||||
--
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
||||
type WindowNum = Either Int (Rational,Rational)
|
||||
|
||||
-- | Use one layout in the specified area for a number of windows and possibly let another layout handle the rest.
|
||||
data LayoutN l1 l2 a =
|
||||
LayoutN (Maybe a) (Maybe a) WindowNum SubBox (Maybe SubBox) (l1 a) (Maybe (l2 a))
|
||||
deriving (Show,Read)
|
||||
|
||||
-- | Use the specified layout in the described area for N windows and send the rest of the windows to the next layout in the chain.
|
||||
-- It is possible to supply an alternative area that will then be used instead, if there are no windows to send to the next layout.
|
||||
layoutN :: (Read a, Eq a, LayoutClass l1 a, LayoutClass l2 a, LayoutClass l3 a) =>
|
||||
Int -- ^ The number of windows to handle
|
||||
-> SubBox -- ^ The box to place the windows in
|
||||
-> Maybe SubBox -- ^ Possibly an alternative box that is used when this layout handles all windows that are left
|
||||
-> l1 a -- ^ The layout to use in the specified area
|
||||
-> LayoutN l2 l3 a -- ^ Where to send the remaining windows
|
||||
-> LayoutN l1 (LayoutN l2 l3) a -- ^ The resulting layout
|
||||
layoutN num box mbox sub next = LayoutN Nothing Nothing (Left num) box mbox sub (Just next)
|
||||
|
||||
-- | As layoutN, but the number of windows is given relative to the total number of windows remaining to be handled. The first
|
||||
-- argument is how much to change the ratio when using IncLayoutN, and the second is the initial ratio.
|
||||
layoutR :: (Read a, Eq a, LayoutClass l1 a, LayoutClass l2 a, LayoutClass l3 a) =>
|
||||
Rational -- ^ How much to change the ratio with each IncLayoutN
|
||||
-> Rational -- ^ The ratio of the remaining windows to handle
|
||||
-> SubBox -- ^ The box to place the windows in
|
||||
-> Maybe SubBox -- ^ Possibly an alternative box that is used when this layout handles all windows that are left
|
||||
-> l1 a -- ^ The layout to use in the specified area
|
||||
-> LayoutN l2 l3 a -- ^ Where to send the remaining windows
|
||||
-> LayoutN l1 (LayoutN l2 l3) a -- ^ The resulting layout
|
||||
layoutR numdiff num box mbox sub next = LayoutN Nothing Nothing (Right (numdiff,num)) box mbox sub (Just next)
|
||||
|
||||
-- | Use the specified layout in the described area for all remaining windows.
|
||||
layoutAll :: (Read a, Eq a, LayoutClass l1 a) =>
|
||||
SubBox -- ^ The box to place the windows in
|
||||
-> l1 a -- ^ The layout to use in the specified area
|
||||
-> LayoutN l1 Full a -- ^ The resulting layout
|
||||
layoutAll box sub = LayoutN Nothing Nothing (Right (0,1)) box Nothing sub Nothing
|
||||
|
||||
-- | Change the number of windows handled by the focused layout.
|
||||
data IncLayoutN = IncLayoutN Int deriving Typeable
|
||||
instance Message IncLayoutN
|
||||
|
||||
-- | The absolute or relative measures used to describe the area a layout should be placed in. For negative absolute values
|
||||
-- the total remaining space will be added. For sizes, the remaining space will also be added for zeroes. Relative values
|
||||
-- are applied on the remaining space after the top-left corner of the box have been removed.
|
||||
data SubMeasure = Abs Int | Rel Rational deriving (Show,Read)
|
||||
|
||||
-- | A box to place a layout in. The stored values are xpos, ypos, width and height.
|
||||
data SubBox = SubBox SubMeasure SubMeasure SubMeasure SubMeasure deriving (Show,Read)
|
||||
|
||||
|
||||
-- | Create a box with only absolute measurements. If the values are negative, the total remaining space will be added. For
|
||||
-- sizes it will also be added for zeroes.
|
||||
absBox :: Int -- ^ Absolute X-Position
|
||||
-> Int -- ^ Absolute Y-Position
|
||||
-> Int -- ^ Absolute width
|
||||
-> Int -- ^ Absolute height
|
||||
-> SubBox -- ^ The resulting 'SubBox' describing the area
|
||||
absBox x y w h = SubBox (Abs x) (Abs y) (Abs w) (Abs h)
|
||||
|
||||
|
||||
-- | Create a box with only relative measurements.
|
||||
relBox :: Rational -- ^ Relative X-Position with respect to the surrounding area
|
||||
-> Rational -- ^ Relative Y-Position with respect to the surrounding area
|
||||
-> Rational -- ^ Relative width with respect to the remaining width
|
||||
-> Rational -- ^ Relative height with respect to the remaining height
|
||||
-> SubBox -- ^ The resulting 'SubBox' describing the area
|
||||
relBox x y w h = SubBox (Rel x) (Rel y) (Rel w) (Rel h)
|
||||
|
||||
|
||||
instance (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) =>
|
||||
LayoutClass (LayoutN l1 l2) a where
|
||||
|
||||
-- | Update window locations.
|
||||
runLayout (W.Workspace _ (LayoutN subf nextf num box mbox sub next) s) rect
|
||||
= do let (subs,nexts,subf',nextf') = splitStack s num subf nextf
|
||||
selBox = if isJust nextf'
|
||||
then box
|
||||
else maybe box id mbox
|
||||
|
||||
(sublist,sub') <- handle sub subs $ calcArea selBox rect
|
||||
|
||||
(nextlist,next') <- case next of Nothing -> return ([],Nothing)
|
||||
Just n -> do (res,l) <- handle n nexts rect
|
||||
return (res,Just l)
|
||||
|
||||
return (sublist++nextlist, Just $ LayoutN subf' nextf' num box mbox sub' next' )
|
||||
where
|
||||
handle l s' r = do (res,ml) <- runLayout (W.Workspace "" l s') r
|
||||
l' <- return $ maybe l id ml
|
||||
return (res,l')
|
||||
|
||||
-- | Propagate messages.
|
||||
handleMessage l m
|
||||
| Just (IncLayoutN _) <- fromMessage m = windowNum l m
|
||||
| Just (IncMasterN _) <- fromMessage m = sendFocus l m
|
||||
| Just (Shrink) <- fromMessage m = sendFocus l m
|
||||
| Just (Expand) <- fromMessage m = sendFocus l m
|
||||
| otherwise = sendBoth l m
|
||||
|
||||
-- | Descriptive name for layout.
|
||||
description (LayoutN _ _ _ _ _ sub Nothing) = "layoutAll "++ description sub
|
||||
description (LayoutN _ _ (Left _) _ _ sub (Just next)) = "layoutN "++ description sub ++" "++ description next
|
||||
description (LayoutN _ _ (Right _) _ _ sub (Just next)) = "layoutR "++ description sub ++" "++ description next
|
||||
|
||||
|
||||
windowNum :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) => LayoutN l1 l2 a -> SomeMessage -> X (Maybe (LayoutN l1 l2 a))
|
||||
windowNum l@(LayoutN subf nextf num box mbox subl nextl) m | (Just (IncLayoutN n)) <- fromMessage m =
|
||||
do foc <- isFocus subf
|
||||
if foc then do let newnum = case num of
|
||||
(Left oldnum) -> Left $ max 1 $ oldnum + n
|
||||
(Right (diff,oldnum)) -> Right (diff, min 1 $ max 0 $ oldnum + (fromIntegral n)*diff)
|
||||
return $ Just $ LayoutN subf nextf newnum box mbox subl nextl
|
||||
else sendNext l m
|
||||
windowNum l m = sendNext l m
|
||||
|
||||
sendSub :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) => LayoutN l1 l2 a -> SomeMessage -> X (Maybe (LayoutN l1 l2 a))
|
||||
sendSub (LayoutN subf nextf num box mbox sub next) m =
|
||||
do sub' <- handleMessage sub m
|
||||
return $ if isJust sub'
|
||||
then Just $ LayoutN subf nextf num box mbox (maybe sub id sub') next
|
||||
else Nothing
|
||||
|
||||
sendBoth :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) => LayoutN l1 l2 a -> SomeMessage -> X (Maybe (LayoutN l1 l2 a))
|
||||
sendBoth l@(LayoutN _ _ _ _ _ _ Nothing) m = sendSub l m
|
||||
sendBoth (LayoutN subf nextf num box mbox sub (Just next)) m =
|
||||
do sub' <- handleMessage sub m
|
||||
next' <- handleMessage next m
|
||||
return $ if isJust sub' || isJust next'
|
||||
then Just $ LayoutN subf nextf num box mbox (maybe sub id sub') (Just $ maybe next id next')
|
||||
else Nothing
|
||||
|
||||
sendNext :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) => LayoutN l1 l2 a -> SomeMessage -> X (Maybe (LayoutN l1 l2 a))
|
||||
sendNext (LayoutN _ _ _ _ _ _ Nothing) _ = return Nothing
|
||||
sendNext (LayoutN subf nextf num box mbox sub (Just next)) m =
|
||||
do next' <- handleMessage next m
|
||||
return $ if isJust next'
|
||||
then Just $ LayoutN subf nextf num box mbox sub next'
|
||||
else Nothing
|
||||
|
||||
sendFocus :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) => LayoutN l1 l2 a -> SomeMessage -> X (Maybe (LayoutN l1 l2 a))
|
||||
sendFocus l@(LayoutN subf _ _ _ _ _ _) m = do foc <- isFocus subf
|
||||
if foc then sendSub l m
|
||||
else sendNext l m
|
||||
|
||||
isFocus :: (Show a) => Maybe a -> X Bool
|
||||
isFocus Nothing = return False
|
||||
isFocus (Just w) = do ms <- (W.stack . W.workspace . W.current) `fmap` gets windowset
|
||||
return $ maybe False (\s -> show w == (show $ W.focus s)) ms
|
||||
|
||||
|
||||
calcNum :: Int -> WindowNum -> Int
|
||||
calcNum tot num = max 1 $ case num of Left i -> i
|
||||
Right (_,r) -> ceiling $ r * fromIntegral tot
|
||||
|
||||
splitStack :: Eq a => Maybe (W.Stack a) -> WindowNum -> Maybe a -> Maybe a -> (Maybe (W.Stack a),Maybe (W.Stack a),Maybe a,Maybe a)
|
||||
splitStack Nothing _ _ _ = (Nothing,Nothing,Nothing,Nothing)
|
||||
splitStack (Just s) num subf nextf = ( differentiate' subf' subl
|
||||
, differentiate' nextf' nextl
|
||||
, subf'
|
||||
, nextf'
|
||||
)
|
||||
where
|
||||
ws = W.integrate s
|
||||
n = calcNum (length ws) num
|
||||
subl = take n ws
|
||||
nextl = drop n ws
|
||||
subf' = foc subl subf
|
||||
nextf' = foc nextl nextf
|
||||
foc [] _ = Nothing
|
||||
foc l f = if W.focus s `elem` l
|
||||
then Just $ W.focus s
|
||||
else if maybe False (`elem` l) f
|
||||
then f
|
||||
else Just $ head l
|
||||
|
||||
calcArea :: SubBox -> Rectangle -> Rectangle
|
||||
calcArea (SubBox xpos ypos width height) rect = Rectangle (rect_x rect + fromIntegral xpos') (rect_y rect + fromIntegral ypos') width' height'
|
||||
where
|
||||
xpos' = calc False xpos $ rect_width rect
|
||||
ypos' = calc False ypos $ rect_height rect
|
||||
width' = calc True width $ rect_width rect - xpos'
|
||||
height' = calc True height $ rect_height rect - ypos'
|
||||
|
||||
calc zneg val tot = fromIntegral $ min (fromIntegral tot) $ max 0 $
|
||||
case val of Rel v -> floor $ v * fromIntegral tot
|
||||
Abs v -> if v<0 || (zneg && v==0)
|
||||
then (fromIntegral tot)+v
|
||||
else v
|
||||
|
||||
differentiate' :: Eq q => Maybe q -> [q] -> Maybe (W.Stack q)
|
||||
differentiate' _ [] = Nothing
|
||||
differentiate' Nothing w = W.differentiate w
|
||||
differentiate' (Just f) w
|
||||
| f `elem` w = Just $ W.Stack { W.focus = f
|
||||
, W.up = reverse $ takeWhile (/=f) w
|
||||
, W.down = tail $ dropWhile (/=f) w
|
||||
}
|
||||
| otherwise = W.differentiate w
|
@@ -1,5 +1,4 @@
|
||||
{-# OPTIONS_GHC -fglasgow-exts #-} -- For deriving Data/Typeable
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards #-}
|
||||
{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, MultiParamTypeClasses, PatternGuards #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.LayoutCombinators
|
||||
@@ -47,7 +46,7 @@ module XMonad.Layout.LayoutCombinators
|
||||
-- * New layout choice combinator and 'JumpToLayout'
|
||||
-- $jtl
|
||||
, (|||)
|
||||
, JumpToLayout(JumpToLayout)
|
||||
, JumpToLayout(..)
|
||||
) where
|
||||
|
||||
import Data.Maybe ( isJust, isNothing )
|
||||
@@ -65,8 +64,8 @@ import XMonad.Layout.DragPane
|
||||
-- Then edit your @layoutHook@ to use the new layout combinators. For
|
||||
-- example:
|
||||
--
|
||||
-- > myLayouts = (Tall 1 (3/100) (1/2) *//* Full) ||| (Tall 1 (3/100) (1/2) ***||** Full) ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayouts }
|
||||
-- > myLayout = (Tall 1 (3/100) (1/2) *//* Full) ||| (Tall 1 (3/100) (1/2) ***||** Full) ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayout }
|
||||
--
|
||||
-- For more detailed instructions on editing the @layoutHook@ see:
|
||||
--
|
||||
@@ -79,7 +78,7 @@ import XMonad.Layout.DragPane
|
||||
--
|
||||
-- Then bind some keys to a 'JumpToLayout' message:
|
||||
--
|
||||
-- > , ((modMask x .|. controlMask, xK_f), sendMessage $ JumpToLayout "Full") -- jump directly to the Full layout
|
||||
-- > , ((modm .|. controlMask, xK_f), sendMessage $ JumpToLayout "Full") -- jump directly to the Full layout
|
||||
--
|
||||
-- See below for more detailed documentation.
|
||||
|
||||
@@ -214,12 +213,12 @@ infixr 5 |||
|
||||
|
||||
data NewSelect l1 l2 a = NewSelect Bool (l1 a) (l2 a) deriving ( Read, Show )
|
||||
|
||||
data NoWrap = NextLayoutNoWrap | Wrap deriving ( Read, Show, Typeable )
|
||||
instance Message NoWrap
|
||||
|
||||
-- | A message to jump to a particular layout, specified by its
|
||||
-- description string.
|
||||
data JumpToLayout = JumpToLayout String deriving ( Read, Show, Typeable )
|
||||
-- |
|
||||
data JumpToLayout = JumpToLayout String -- ^ A message to jump to a particular layout
|
||||
-- , specified by its description string..
|
||||
| NextLayoutNoWrap
|
||||
| Wrap
|
||||
deriving ( Read, Show, Typeable )
|
||||
instance Message JumpToLayout
|
||||
|
||||
instance (LayoutClass l1 a, LayoutClass l2 a) => LayoutClass (NewSelect l1 l2) a where
|
||||
|
@@ -1,5 +1,5 @@
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeSynonymInstances #-}
|
||||
|
||||
{-# LANGUAGE ParallelListComp, PatternGuards #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.LayoutHints
|
||||
@@ -8,7 +8,7 @@
|
||||
--
|
||||
-- Maintainer : none
|
||||
-- Stability : unstable
|
||||
-- Portability : portable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Make layouts respect size hints.
|
||||
-----------------------------------------------------------------------------
|
||||
@@ -17,41 +17,221 @@ module XMonad.Layout.LayoutHints
|
||||
( -- * usage
|
||||
-- $usage
|
||||
layoutHints
|
||||
, layoutHintsWithPlacement
|
||||
, layoutHintsToCenter
|
||||
, LayoutHints
|
||||
) where
|
||||
) where
|
||||
|
||||
import XMonad hiding ( trace )
|
||||
import XMonad.Layout.LayoutModifier
|
||||
import XMonad.Layout.Decoration ( isInStack )
|
||||
import XMonad(LayoutClass(runLayout), mkAdjust, Window,
|
||||
Dimension, Position, Rectangle(Rectangle),D)
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import XMonad.Layout.Decoration(isInStack)
|
||||
import XMonad.Layout.LayoutModifier(ModifiedLayout(..),
|
||||
LayoutModifier(modifyLayout, redoLayout, modifierDescription))
|
||||
import XMonad.Util.Types(Direction2D(..))
|
||||
import Control.Applicative((<$>))
|
||||
import Control.Arrow(Arrow((***), first, second))
|
||||
import Control.Monad(Monad(return), mapM, join)
|
||||
import Data.Function(on)
|
||||
import Data.List(sortBy)
|
||||
|
||||
import Data.Set (Set)
|
||||
import qualified Data.Set as Set
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Layout.LayoutHints
|
||||
--
|
||||
-- Then edit your @layoutHook@ by adding the LayoutHints layout modifier
|
||||
-- Then edit your @layoutHook@ by adding the 'layoutHints' layout modifier
|
||||
-- to some layout:
|
||||
--
|
||||
-- > myLayouts = layoutHints (Tall 1 (3/100) (1/2)) ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayouts }
|
||||
-- > myLayout = layoutHints (Tall 1 (3/100) (1/2)) ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayout }
|
||||
--
|
||||
-- Or, to center the adapted window in its available area:
|
||||
--
|
||||
-- > myLayout = layoutHintsWithPlacement (0.5, 0.5) (Tall 1 (3/100) (1/2))
|
||||
-- > ||| Full ||| etc..
|
||||
--
|
||||
-- Or, to make a reasonable attempt to eliminate gaps between windows:
|
||||
--
|
||||
-- > myLayout = layoutHintsToCenter (Tall 1 (3/100) (1/2))
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
-- "XMonad.Doc.Extending#Editing_the_layout_hook"
|
||||
|
||||
layoutHints :: (LayoutClass l a) => l a -> ModifiedLayout LayoutHints l a
|
||||
layoutHints = ModifiedLayout LayoutHints
|
||||
layoutHints = ModifiedLayout (LayoutHints (0, 0))
|
||||
|
||||
data LayoutHints a = LayoutHints deriving (Read, Show)
|
||||
-- | @layoutHintsWithPlacement (rx, ry) layout@ will adapt the sizes of a layout's
|
||||
-- windows according to their size hints, and position them inside their
|
||||
-- originally assigned area according to the @rx@ and @ry@ parameters.
|
||||
-- (0, 0) places the window at the top left, (1, 0) at the top right, (0.5, 0.5)
|
||||
-- at the center, etc.
|
||||
layoutHintsWithPlacement :: (LayoutClass l a) => (Double, Double)
|
||||
-> l a -> ModifiedLayout LayoutHints l a
|
||||
layoutHintsWithPlacement rs = ModifiedLayout (LayoutHints rs)
|
||||
|
||||
-- | @layoutHintsToCenter layout@ applies hints, sliding the window to the
|
||||
-- center of the screen and expanding its neighbors to fill the gaps. Windows
|
||||
-- are never expanded in a way that increases overlap.
|
||||
--
|
||||
-- @layoutHintsToCenter@ only makes one pass at resizing the neighbors of
|
||||
-- hinted windows, so with some layouts (ex. the arrangement with two 'Mirror'
|
||||
-- 'Tall' stacked vertically), @layoutHintsToCenter@ may leave some gaps.
|
||||
-- Simple layouts like 'Tall' are unaffected.
|
||||
layoutHintsToCenter :: (LayoutClass l a) => l a -> ModifiedLayout LayoutHintsToCenter l a
|
||||
layoutHintsToCenter = ModifiedLayout LayoutHintsToCenter
|
||||
|
||||
data LayoutHints a = LayoutHints (Double, Double)
|
||||
deriving (Read, Show)
|
||||
|
||||
instance LayoutModifier LayoutHints Window where
|
||||
modifierDescription _ = "Hinted"
|
||||
redoLayout _ _ Nothing xs = return (xs, Nothing)
|
||||
redoLayout _ _ (Just s) xs = do
|
||||
xs' <- mapM applyHint xs
|
||||
return (xs', Nothing)
|
||||
redoLayout (LayoutHints al) _ (Just s) xs
|
||||
= do xs' <- mapM (\x@(_, r) -> second (placeRectangle al r) <$> applyHint x) xs
|
||||
return (xs', Nothing)
|
||||
where
|
||||
applyHint (w,r@(Rectangle a b c d)) = do
|
||||
adj <- mkAdjust w
|
||||
let (c',d') = adj (c,d)
|
||||
return (w, if isInStack s w then Rectangle a b c' d' else r)
|
||||
|
||||
-- | @placeRectangle (rx, ry) r0 r@ will return a new rectangle with the same dimensions
|
||||
-- as @r@, but positioned inside of @r0@ as specified by the (rx, ry) parameters (see
|
||||
-- 'layoutHintsWithPlacement').
|
||||
placeRectangle :: RealFrac r => (r, r) -> Rectangle -> Rectangle -> Rectangle
|
||||
placeRectangle (rx, ry) (Rectangle x0 y0 w h) (Rectangle _ _ dx dy)
|
||||
= Rectangle (align x0 dx w rx) (align y0 dy h ry) dx dy
|
||||
where align :: RealFrac r => Position -> Dimension -> Dimension -> r -> Position
|
||||
align z0 dz d r = z0 + truncate (fromIntegral (d - dz) * r)
|
||||
|
||||
fitting :: [Rectangle] -> Int
|
||||
fitting rects = sum $ do
|
||||
r <- rects
|
||||
return $ length $ filter (touching r) rects
|
||||
|
||||
applyOrder :: Rectangle -> [((Window, Rectangle),t)] -> [[((Window, Rectangle),t)]]
|
||||
applyOrder root wrs = do
|
||||
-- perhaps it would just be better to take all permutations, or apply the
|
||||
-- resizing multiple times
|
||||
f <- [maximum, minimum, sum, sum . map sq]
|
||||
return $ sortBy (compare `on` (f . distance)) wrs
|
||||
where distFC = uncurry ((+) `on` sq) . pairWise (-) (center root)
|
||||
distance = map distFC . corners . snd . fst
|
||||
pairWise f (a,b) (c,d) = (f a c, f b d)
|
||||
sq = join (*)
|
||||
|
||||
data LayoutHintsToCenter a = LayoutHintsToCenter deriving (Read, Show)
|
||||
|
||||
instance LayoutModifier LayoutHintsToCenter Window where
|
||||
modifyLayout _ ws@(W.Workspace _ _ Nothing) r = runLayout ws r
|
||||
modifyLayout _ ws@(W.Workspace _ _ (Just st)) r = do
|
||||
(arrs,ol) <- runLayout ws r
|
||||
flip (,) ol
|
||||
. head . reverse . sortBy (compare `on` (fitting . map snd))
|
||||
. map (applyHints st r) . applyOrder r
|
||||
<$> mapM (\x -> fmap ((,) x) $ mkAdjust (fst x)) arrs
|
||||
|
||||
-- apply hints to first, grow adjacent windows
|
||||
applyHints :: W.Stack Window -> Rectangle -> [((Window, Rectangle),(D -> D))] -> [(Window, Rectangle)]
|
||||
applyHints _ _ [] = []
|
||||
applyHints s root (((w,lrect@(Rectangle a b c d)),adj):xs) =
|
||||
let (c',d') = adj (c,d)
|
||||
redr = placeRectangle (centerPlacement root lrect :: (Double,Double)) lrect
|
||||
$ if isInStack s w then Rectangle a b c' d' else lrect
|
||||
|
||||
ds = (fromIntegral c - fromIntegral c',fromIntegral d - fromIntegral d')
|
||||
growOther' r = growOther ds lrect (freeDirs root lrect) r
|
||||
mapSnd f = map (first $ second f)
|
||||
next = applyHints s root $ mapSnd growOther' xs
|
||||
in (w,redr):next
|
||||
|
||||
growOther :: (Position, Position) -> Rectangle -> Set Direction2D -> Rectangle -> Rectangle
|
||||
growOther ds lrect fds r
|
||||
| dirs <- flipDir <$> Set.toList (Set.intersection adj fds)
|
||||
, not $ any (uncurry opposite) $ cross dirs =
|
||||
foldr (flip grow ds) r dirs
|
||||
| otherwise = r
|
||||
where
|
||||
adj = adjacent lrect r
|
||||
cross xs = [ (a,b) | a <- xs, b <- xs ]
|
||||
|
||||
flipDir :: Direction2D -> Direction2D
|
||||
flipDir d = case d of { L -> R; U -> D; R -> L; D -> U }
|
||||
|
||||
opposite :: Direction2D -> Direction2D -> Bool
|
||||
opposite x y = flipDir x == y
|
||||
|
||||
-- | Leave the opposite edges where they were
|
||||
grow :: Direction2D -> (Position,Position) -> Rectangle -> Rectangle
|
||||
grow L (px,_ ) (Rectangle x y w h) = Rectangle (x-px) y (w+fromIntegral px) h
|
||||
grow U (_ ,py) (Rectangle x y w h) = Rectangle x (y-py) w (h+fromIntegral py)
|
||||
grow R (px,_ ) (Rectangle x y w h) = Rectangle x y (w+fromIntegral px) h
|
||||
grow D (_ ,py) (Rectangle x y w h) = Rectangle x y w (h+fromIntegral py)
|
||||
|
||||
comparingEdges :: ([Position] -> [Position] -> Bool) -> Rectangle -> Rectangle -> Set Direction2D
|
||||
comparingEdges surrounds r1 r2 = Set.fromList $ map fst $ filter snd [ (\k -> (dir,k)) $
|
||||
any and [[dir `elem` [R,L], allEq [a,c,w,y], [b,d] `surrounds` [x,z]]
|
||||
,[dir `elem` [U,D], allEq [b,d,x,z], [a,c] `surrounds` [w,y]]]
|
||||
| ((a,b),(c,d)) <- edge $ corners r1
|
||||
| ((w,x),(y,z)) <- edge $ delay 2 $ corners r2
|
||||
| dir <- [U,R,D,L]]
|
||||
where edge (x:xs) = zip (x:xs) (xs ++ [x])
|
||||
edge [] = []
|
||||
delay n xs = drop n xs ++ take n xs
|
||||
allEq = all (uncurry (==)) . edge
|
||||
|
||||
-- | in what direction is the second window from the first that can expand if the
|
||||
-- first is shrunk, assuming that the root window is fully covered:
|
||||
-- one direction for a common edge
|
||||
-- two directions for a common corner
|
||||
adjacent :: Rectangle -> Rectangle -> Set Direction2D
|
||||
adjacent = comparingEdges (all . onClosedInterval)
|
||||
|
||||
-- | True whenever two edges touch. not (Set.null $ adjacent x y) ==> touching x y
|
||||
touching :: Rectangle -> Rectangle -> Bool
|
||||
touching a b = not . Set.null $ comparingEdges c a b
|
||||
where c x y = any (onClosedInterval x) y || any (onClosedInterval y) x
|
||||
|
||||
onClosedInterval :: Ord a => [a] -> a -> Bool
|
||||
onClosedInterval bds x = minimum bds <= x && maximum bds >= x
|
||||
|
||||
-- | starting top left going clockwise
|
||||
corners :: Rectangle -> [(Position, Position)]
|
||||
corners (Rectangle x y w h) = [(x,y)
|
||||
,(x+fromIntegral w, y)
|
||||
,(x+fromIntegral w, y+fromIntegral h)
|
||||
,(x, y+fromIntegral h)]
|
||||
|
||||
center :: Rectangle -> (Position, Position)
|
||||
center (Rectangle x y w h) = (avg x w, avg y h)
|
||||
where avg a b = a + fromIntegral b `div` 2
|
||||
|
||||
centerPlacement :: RealFrac r => Rectangle -> Rectangle -> (r, r)
|
||||
centerPlacement = centerPlacement' clamp
|
||||
where clamp n = case signum n of
|
||||
0 -> 0.5
|
||||
1 -> 1
|
||||
_ -> 0
|
||||
|
||||
freeDirs :: Rectangle -> Rectangle -> Set Direction2D
|
||||
freeDirs root = Set.fromList . uncurry (++) . (lr *** ud)
|
||||
. centerPlacement' signum root
|
||||
where
|
||||
lr 1 = [L]
|
||||
lr (-1) = [R]
|
||||
lr _ = [L,R]
|
||||
ud 1 = [U]
|
||||
ud (-1) = [D]
|
||||
ud _ = [U,D]
|
||||
|
||||
centerPlacement' :: (Position -> r) -> Rectangle -> Rectangle -> (r, r)
|
||||
centerPlacement' cf root assigned
|
||||
= (cf $ cx - cwx, cf $ cy - cwy)
|
||||
where (cx,cy) = center root
|
||||
(cwx,cwy) = center assigned
|
||||
|
@@ -1,5 +1,4 @@
|
||||
{-# OPTIONS_GHC -fglasgow-exts #-} -- For deriving Data/Typeable
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards #-}
|
||||
{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, MultiParamTypeClasses, PatternGuards #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
|
@@ -10,6 +10,7 @@
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Divide a single screen into multiple screens.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Layout.LayoutScreens (
|
||||
@@ -38,8 +39,8 @@ import qualified XMonad.StackSet as W
|
||||
--
|
||||
-- Then add some keybindings; for example:
|
||||
--
|
||||
-- > , ((modMask x .|. shiftMask, xK_space), layoutScreens 2 (TwoPane 0.5 0.5))
|
||||
-- > , ((modMask x .|. controlMask .|. shiftMask, xK_space), rescreen)
|
||||
-- > , ((modm .|. shiftMask, xK_space), layoutScreens 2 (TwoPane 0.5 0.5))
|
||||
-- > , ((modm .|. controlMask .|. shiftMask, xK_space), rescreen)
|
||||
--
|
||||
-- Another example use would be to handle a scenario where xrandr didn't
|
||||
-- work properly (e.g. a VNC X server in my case) and you want to be able
|
||||
@@ -47,9 +48,9 @@ import qualified XMonad.StackSet as W
|
||||
--
|
||||
-- > import XMonad.Layout.LayoutScreens
|
||||
--
|
||||
-- > , ((modMask x .|. shiftMask, xK_space),
|
||||
-- > , ((modm .|. shiftMask, xK_space),
|
||||
-- > layoutScreens 1 (fixedLayout [Rectangle 0 0 1024 768]))
|
||||
-- > , ((modMask x .|. controlMask .|. shiftMask, xK_space), rescreen)
|
||||
-- > , ((modm .|. controlMask .|. shiftMask, xK_space), rescreen)
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
160
XMonad/Layout/LimitWindows.hs
Normal file
160
XMonad/Layout/LimitWindows.hs
Normal file
@@ -0,0 +1,160 @@
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, DeriveDataTypeable, PatternGuards #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.LimitWindows
|
||||
-- Copyright : (c) 2009 Adam Vogt
|
||||
-- (c) 2009 Max Rabkin -- wrote limitSelect
|
||||
-- License : BSD-style (see xmonad/LICENSE)
|
||||
--
|
||||
-- Maintainer : vogt.adam@gmail.com
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- A layout modifier that limits the number of windows that can be shown.
|
||||
-- See "XMonad.Layout.Minimize" for manually setting hidden windows.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Layout.LimitWindows (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
|
||||
-- * Layout Modifiers
|
||||
limitWindows,limitSlice,limitSelect,
|
||||
|
||||
-- * Change the number of windows
|
||||
increaseLimit,decreaseLimit,setLimit
|
||||
) where
|
||||
|
||||
import XMonad.Layout.LayoutModifier
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad.Layout (IncMasterN (..))
|
||||
import Control.Monad((<=<),guard)
|
||||
import Control.Applicative((<$>))
|
||||
import Data.Maybe(fromJust)
|
||||
|
||||
-- $usage
|
||||
-- To use this module, add the following import to @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Layout.LimitWindows
|
||||
--
|
||||
-- > myLayout = limitWindows 6 $ Tall 1 0.03 0.5 ||| Full ||| RandomOtherLayout...
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayout }
|
||||
--
|
||||
-- You may also be interested in dynamically changing the number dynamically,
|
||||
-- by binding keys to the 'increaseLimit', 'decreaseLimit', or 'setLimit'
|
||||
-- actions.
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
--
|
||||
-- See also 'XMonad.Layout.BoringWindows.boringAuto' for keybindings that skip
|
||||
-- the hidden windows.
|
||||
|
||||
increaseLimit :: X ()
|
||||
increaseLimit = sendMessage $ LimitChange succ
|
||||
|
||||
decreaseLimit :: X ()
|
||||
decreaseLimit = sendMessage . LimitChange $ max 1 . pred
|
||||
|
||||
setLimit :: Int -> X ()
|
||||
setLimit tgt = sendMessage . LimitChange $ const tgt
|
||||
|
||||
-- | Only display the first @n@ windows.
|
||||
limitWindows :: Int -> l a -> ModifiedLayout LimitWindows l a
|
||||
limitWindows n = ModifiedLayout (LimitWindows FirstN n)
|
||||
|
||||
-- | Only display @n@ windows around the focused window. This makes sense with
|
||||
-- layouts that arrange windows linearily, like 'XMonad.Layout.Layout.Accordion'.
|
||||
limitSlice :: Int -> l a -> ModifiedLayout LimitWindows l a
|
||||
limitSlice n = ModifiedLayout (LimitWindows Slice n)
|
||||
|
||||
-- | Only display the first @m@ windows and @r@ others.
|
||||
-- The @IncMasterN@ message will change @m@, as well as passing it onto the
|
||||
-- underlying layout.
|
||||
limitSelect :: Int -> Int -> l a -> ModifiedLayout Selection l a
|
||||
limitSelect m r = ModifiedLayout Sel{ nMaster=m, start=m, nRest=r }
|
||||
|
||||
data LimitWindows a = LimitWindows SliceStyle Int deriving (Read,Show)
|
||||
|
||||
data SliceStyle = FirstN | Slice deriving (Read,Show)
|
||||
|
||||
data LimitChange = LimitChange { unLC :: (Int -> Int) } deriving (Typeable)
|
||||
|
||||
instance Message LimitChange
|
||||
|
||||
instance LayoutModifier LimitWindows a where
|
||||
pureMess (LimitWindows s n) =
|
||||
fmap (LimitWindows s) . pos <=< (`app` n) . unLC <=< fromMessage
|
||||
where pos x = guard (x>=1) >> return x
|
||||
app f x = guard (f x /= x) >> return (f x)
|
||||
|
||||
modifyLayout (LimitWindows style n) ws r =
|
||||
runLayout ws { W.stack = f n <$> W.stack ws } r
|
||||
where f = case style of
|
||||
FirstN -> firstN
|
||||
Slice -> slice
|
||||
|
||||
firstN :: Int -> W.Stack a -> W.Stack a
|
||||
firstN n st = upfocus $ fromJust $ W.differentiate $ take (max 1 n) $ W.integrate st
|
||||
where upfocus = foldr (.) id $ replicate (length (W.up st)) W.focusDown'
|
||||
|
||||
-- | A non-wrapping, fixed-size slice of a stack around the focused element
|
||||
slice :: Int -> W.Stack t -> W.Stack t
|
||||
slice n (W.Stack f u d) =
|
||||
W.Stack f (take (nu + unusedD) u)
|
||||
(take (nd + unusedU) d)
|
||||
where unusedD = max 0 $ nd - length d
|
||||
unusedU = max 0 $ nu - length u
|
||||
nd = div (n - 1) 2
|
||||
nu = uncurry (+) $ divMod (n - 1) 2
|
||||
|
||||
data Selection a = Sel { nMaster :: Int, start :: Int, nRest :: Int }
|
||||
deriving (Read, Show, Eq)
|
||||
|
||||
instance LayoutModifier Selection a where
|
||||
modifyLayout s w r =
|
||||
runLayout (w { W.stack = updateAndSelect s <$> W.stack w }) r
|
||||
|
||||
pureModifier sel _ stk wins = (wins, update sel <$> stk)
|
||||
|
||||
pureMess sel m
|
||||
| Just f <- unLC <$> fromMessage m =
|
||||
Just $ sel { nRest = max 0 (f (nMaster sel + nRest sel) - nMaster sel) }
|
||||
| Just (IncMasterN n) <- fromMessage m =
|
||||
Just $ sel { nMaster = max 0 (nMaster sel + n) }
|
||||
| otherwise =
|
||||
Nothing
|
||||
|
||||
select :: Selection l -> W.Stack a -> W.Stack a
|
||||
select s stk
|
||||
| lups < nMaster s
|
||||
= stk { W.down=take (nMaster s - lups - 1) downs ++
|
||||
(take (nRest s) . drop (start s - lups - 1) $ downs) }
|
||||
| otherwise
|
||||
= stk { W.up=reverse (take (nMaster s) ups ++ drop (start s) ups),
|
||||
W.down=take ((nRest s) - (lups - start s) - 1) downs }
|
||||
where
|
||||
downs = W.down stk
|
||||
ups = reverse $ W.up stk
|
||||
lups = length ups
|
||||
|
||||
updateStart :: Selection l -> W.Stack a -> Int
|
||||
updateStart s stk
|
||||
| lups < nMaster s -- the focussed window is in the master pane
|
||||
= start s `min` (lups + ldown - (nRest s) + 1) `max` nMaster s
|
||||
| otherwise
|
||||
= start s `min` lups
|
||||
`max` (lups - (nRest s) + 1)
|
||||
`min` (lups + ldown - (nRest s) + 1)
|
||||
`max` nMaster s
|
||||
where
|
||||
lups = length $ W.up stk
|
||||
ldown = length $ W.down stk
|
||||
|
||||
update :: Selection l -> W.Stack a -> Selection a
|
||||
update sel stk = sel { start=updateStart sel stk }
|
||||
|
||||
updateAndSelect :: Selection l -> W.Stack a -> W.Stack a
|
||||
updateAndSelect sel stk = select (update sel stk) stk
|
@@ -16,13 +16,21 @@
|
||||
module XMonad.Layout.MagicFocus
|
||||
(-- * Usage
|
||||
-- $usage
|
||||
magicFocus
|
||||
magicFocus,
|
||||
promoteWarp,
|
||||
promoteWarp',
|
||||
followOnlyIf,
|
||||
disableFollowOnWS
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.StackSet
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad.Layout.LayoutModifier
|
||||
|
||||
import XMonad.Actions.UpdatePointer(updatePointer, PointerPosition(TowardsCentre))
|
||||
import Data.Monoid(All(..))
|
||||
import qualified Data.Map as M
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
@@ -31,8 +39,9 @@ import XMonad.Layout.LayoutModifier
|
||||
-- Then edit your @layoutHook@ by adding the magicFocus layout
|
||||
-- modifier:
|
||||
--
|
||||
-- > myLayouts = magicFocus (Tall 1 (3/100) (1/2)) ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayouts }
|
||||
-- > myLayout = magicFocus (Tall 1 (3/100) (1/2)) ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayout,
|
||||
-- > handleEventHook = promoteWarp }
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
@@ -46,10 +55,53 @@ magicFocus = ModifiedLayout MagicFocus
|
||||
data MagicFocus a = MagicFocus deriving (Show, Read)
|
||||
|
||||
instance LayoutModifier MagicFocus Window where
|
||||
modifyLayout MagicFocus (Workspace i l s) r =
|
||||
modifyLayout MagicFocus (W.Workspace i l s) r =
|
||||
withWindowSet $ \wset ->
|
||||
runLayout (Workspace i l (s >>= \st -> Just $ swap st (peek wset))) r
|
||||
runLayout (W.Workspace i l (s >>= \st -> Just $ swap st (W.peek wset))) r
|
||||
|
||||
swap :: (Eq a) => Stack a -> Maybe a -> Stack a
|
||||
swap (Stack f u d) focused | Just f == focused = Stack f [] (reverse u ++ d)
|
||||
| otherwise = Stack f u d
|
||||
swap :: (Eq a) => W.Stack a -> Maybe a -> W.Stack a
|
||||
swap (W.Stack f u d) focused
|
||||
| Just f == focused = W.Stack f [] (reverse u ++ d)
|
||||
| otherwise = W.Stack f u d
|
||||
|
||||
-- | An eventHook that overrides the normal focusFollowsMouse. When the mouse
|
||||
-- it moved to another window, that window is replaced as the master, and the
|
||||
-- mouse is warped to inside the new master.
|
||||
--
|
||||
-- It prevents infinite loops when focusFollowsMouse is true (the default), and
|
||||
-- MagicFocus is in use when changing focus with the mouse.
|
||||
--
|
||||
-- This eventHook does nothing when there are floating windows on the current
|
||||
-- workspace.
|
||||
promoteWarp :: Event -> X All
|
||||
promoteWarp = promoteWarp' (TowardsCentre 0.15 0.15)
|
||||
|
||||
-- | promoteWarp' allows you to specify an arbitrary PointerPosition to apply
|
||||
-- when the mouse enters another window.
|
||||
promoteWarp' :: PointerPosition -> Event -> X All
|
||||
promoteWarp' pos e@(CrossingEvent {ev_window = w, ev_event_type = t})
|
||||
| t == enterNotify && ev_mode e == notifyNormal = do
|
||||
ws <- gets windowset
|
||||
let foc = W.peek ws
|
||||
st = W.integrate' . W.stack . W.workspace $ W.current ws
|
||||
wsFloats = M.filterWithKey (\k _ -> k `elem` st) $ W.floating ws
|
||||
if Just w /= foc && M.null wsFloats then do
|
||||
windows (W.swapMaster . W.focusWindow w)
|
||||
updatePointer pos
|
||||
return $ All False
|
||||
else return $ All True
|
||||
promoteWarp' _ _ = return $ All True
|
||||
|
||||
-- | 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
|
||||
-- focusFollowsMouse only for given workspaces or layouts.
|
||||
-- Beware that your focusFollowsMouse setting is ignored if you use this event hook.
|
||||
followOnlyIf :: X Bool -> Event -> X All
|
||||
followOnlyIf cond e@(CrossingEvent {ev_window = w, ev_event_type = t})
|
||||
| t == enterNotify && ev_mode e == notifyNormal
|
||||
= whenX cond (focus w) >> return (All False)
|
||||
followOnlyIf _ _ = return $ All True
|
||||
|
||||
-- | Disables focusFollow on the given workspaces:
|
||||
disableFollowOnWS :: [WorkspaceId] -> X Bool
|
||||
disableFollowOnWS wses = (`notElem` wses) `fmap` gets (W.currentTag . windowset)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user