mirror of
https://github.com/xmonad/xmonad.git
synced 2025-07-29 11:11:53 -07:00
Compare commits
210 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
4be3b39cd2 | ||
|
75889ab62e | ||
|
792add376e | ||
|
87c50a911f | ||
|
d16aa9975e | ||
|
f34642cbac | ||
|
008c3638a5 | ||
|
f5c40e9e12 | ||
|
bd82cc9150 | ||
|
a025912ab7 | ||
|
19c1759b35 | ||
|
92acd1eb74 | ||
|
db9f39d6af | ||
|
ebcd67efac | ||
|
387a253f62 | ||
|
4c83e8e097 | ||
|
ae59a5184f | ||
|
fa8fe9aca4 | ||
|
673c3e9ed9 | ||
|
6ba45cdb38 | ||
|
b995b430bc | ||
|
ba482a4611 | ||
|
684907bc77 | ||
|
ad4136df26 | ||
|
defe0c282e | ||
|
c7bdac1a7e | ||
|
17799f131a | ||
|
8cd66aa380 | ||
|
32ba0d4a0d | ||
|
77b3f62610 | ||
|
f3b07eb5dc | ||
|
4372c256ed | ||
|
34239a79de | ||
|
5866db4f0f | ||
|
46d039cde5 | ||
|
dd22717961 | ||
|
0beeb4164b | ||
|
0b435028ff | ||
|
84a988da82 | ||
|
dbd739e41e | ||
|
d5d8d551e6 | ||
|
a2ba4d8a6c | ||
|
557d3edb7d | ||
|
262db2367f | ||
|
eddb445307 | ||
|
d5aadf2538 | ||
|
a16bb44934 | ||
|
2b854ee47c | ||
|
02ed1cabdc | ||
|
02693d307c | ||
|
37dc284460 | ||
|
73e406f4a6 | ||
|
44bc9558d9 | ||
|
0eb84e4866 | ||
|
b4bf8de874 | ||
|
17c89e327e | ||
|
da71b6c8ac | ||
|
2621f3f6a8 | ||
|
8ec0bf3290 | ||
|
7e20d0d308 | ||
|
24d8de93d7 | ||
|
2dd6eeba7d | ||
|
72997cf982 | ||
|
7365d7bc11 | ||
|
36e20f689c | ||
|
cde261ed56 | ||
|
8d8cc8bcd8 | ||
|
ccb6ff92f2 | ||
|
e944a6c8d3 | ||
|
eb1e29c8bb | ||
|
66e7715ea6 | ||
|
d9d3e40112 | ||
|
7385793c65 | ||
|
72885e7e24 | ||
|
a931776e54 | ||
|
61568318d6 | ||
|
3caa989e20 | ||
|
09fd11d13b | ||
|
f33681de49 | ||
|
bf8bfc66a5 | ||
|
4075e2d9d3 | ||
|
78856e1a6f | ||
|
4222dd9ad3 | ||
|
34a547ce57 | ||
|
353e7cd681 | ||
|
72dece0769 | ||
|
6e1c5e9b49 | ||
|
bf8ba79090 | ||
|
5edfb1d262 | ||
|
0fecae0abc | ||
|
26f4f734f9 | ||
|
5e7df396b9 | ||
|
314ba78335 | ||
|
7aa78ecc75 | ||
|
ba8e26458e | ||
|
c627e8cc4d | ||
|
04f894275d | ||
|
edb752136f | ||
|
2b463a632f | ||
|
ca122dd2cb | ||
|
77657b65f9 | ||
|
28c57a837a | ||
|
afda20b56d | ||
|
0cc7b12fd0 | ||
|
15a78ae715 | ||
|
18444799e0 | ||
|
cc60fa73ad | ||
|
8881e2ac78 | ||
|
533031e3d6 | ||
|
76d4af15e4 | ||
|
74c6dd2721 | ||
|
b605fd9fce | ||
|
85202ebd47 | ||
|
328c660ce7 | ||
|
b185a439b1 | ||
|
0016e06984 | ||
|
339b2d0097 | ||
|
5f4d63ba71 | ||
|
942572c830 | ||
|
46ac2ca24b | ||
|
3830d7a571 | ||
|
5b3eaf663a | ||
|
c93b7c7c3b | ||
|
42dee4768e | ||
|
e847b350ed | ||
|
cccbfa21e4 | ||
|
870b3ad282 | ||
|
ab30d76578 | ||
|
d8d636e573 | ||
|
ba3987f299 | ||
|
5a19425e79 | ||
|
28431e18c8 | ||
|
43c2d26cdb | ||
|
c24016882e | ||
|
9dae87c537 | ||
|
b67026dd02 | ||
|
aa58eea6dc | ||
|
7db13a2a45 | ||
|
029e668dbc | ||
|
6f61c83623 | ||
|
bcbccbfafc | ||
|
04c8d62361 | ||
|
4890116e49 | ||
|
708084dd48 | ||
|
ef516142b9 | ||
|
cb51875da6 | ||
|
167a6e155b | ||
|
2b2774f81d | ||
|
16725dfe0d | ||
|
15db3c6f0a | ||
|
6db444eb1a | ||
|
46bc3bbd17 | ||
|
d948210935 | ||
|
db08970071 | ||
|
4c69a85b3f | ||
|
ac103b8472 | ||
|
029965e4d4 | ||
|
9fd1d4f9d0 | ||
|
dbbd934b0b | ||
|
750544fda9 | ||
|
90eae3fd63 | ||
|
d6233d0463 | ||
|
5f088f4e99 | ||
|
f8a7d8d381 | ||
|
f7686746c6 | ||
|
04ee55c3ca | ||
|
50ce362626 | ||
|
209b88f821 | ||
|
c5cca485df | ||
|
0593a282ca | ||
|
351de8d2b6 | ||
|
4bd9073937 | ||
|
79754fd5d3 | ||
|
b14de19e8b | ||
|
e97c326ff0 | ||
|
bc13b4ba07 | ||
|
5bea59a823 | ||
|
669a162cfc | ||
|
310c22694e | ||
|
1c930ba955 | ||
|
797204fe6c | ||
|
a3ecf5d304 | ||
|
1a4a4a5000 | ||
|
a8d3564653 | ||
|
d5955b023c | ||
|
4d9a6c2681 | ||
|
87193ff61e | ||
|
3303c6e05d | ||
|
9d9acba45f | ||
|
cc2754d82a | ||
|
cea3492d28 | ||
|
14d9a194ff | ||
|
e8d1d028ba | ||
|
695860f1fd | ||
|
261f742404 | ||
|
1de1bcded2 | ||
|
0c697ebbb4 | ||
|
a626083721 | ||
|
481e42ab72 | ||
|
e751c4b62f | ||
|
730984fd60 | ||
|
ad85e11a4a | ||
|
2da09787da | ||
|
162a54d992 | ||
|
d00d4ca046 | ||
|
0dd54885eb | ||
|
f80d593d57 | ||
|
10be8aaae0 | ||
|
66f623b656 | ||
|
b86351f3c3 |
4
CONFIG
4
CONFIG
@@ -51,9 +51,9 @@ Ok, looks good.
|
|||||||
|
|
||||||
To have xmonad start using your settings, type 'mod-q'. xmonad will
|
To have xmonad start using your settings, type 'mod-q'. xmonad will
|
||||||
then load this new file, and run it. If it is unable to, the defaults
|
then load this new file, and run it. If it is unable to, the defaults
|
||||||
are used.
|
are used.
|
||||||
|
|
||||||
To load succesfully, both 'xmonad' and 'ghc' must be in your $PATH
|
To load successfully, both 'xmonad' and 'ghc' must be in your $PATH
|
||||||
environment variable. If GHC isn't in your path, for some reason, you
|
environment variable. If GHC isn't in your path, for some reason, you
|
||||||
can compile the xmonad.hs file yourself:
|
can compile the xmonad.hs file yourself:
|
||||||
|
|
||||||
|
24
LICENSE
24
LICENSE
@@ -1,27 +1,31 @@
|
|||||||
Copyright (c) Spencer Janssen
|
Copyright (c) 2007,2008 Spencer Janssen
|
||||||
|
Copyright (c) 2007,2008 Don Stewart
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions
|
modification, are permitted provided that the following conditions
|
||||||
are met:
|
are met:
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright
|
1. Redistributions of source code must retain the above copyright
|
||||||
notice, this list of conditions and the following disclaimer.
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
notice, this list of conditions and the following disclaimer in the
|
notice, this list of conditions and the following disclaimer in the
|
||||||
documentation and/or other materials provided with the distribution.
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
3. Neither the name of the author nor the names of his contributors
|
3. Neither the name of the author nor the names of his contributors
|
||||||
may be used to endorse or promote products derived from this software
|
may be used to endorse or promote products derived from this software
|
||||||
without specific prior written permission.
|
without specific prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
|
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
SUCH DAMAGE.
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
67
Main.hs
67
Main.hs
@@ -16,11 +16,16 @@ module Main (main) where
|
|||||||
|
|
||||||
import XMonad
|
import XMonad
|
||||||
|
|
||||||
import Control.Exception (handle)
|
import Control.Monad (unless)
|
||||||
import System.IO
|
|
||||||
import System.Info
|
import System.Info
|
||||||
import System.Environment
|
import System.Environment
|
||||||
import System.Posix.Process (executeFile)
|
import System.Posix.Process (executeFile)
|
||||||
|
import System.Exit (exitFailure)
|
||||||
|
|
||||||
|
import Paths_xmonad (version)
|
||||||
|
import Data.Version (showVersion)
|
||||||
|
|
||||||
|
import Graphics.X11.Xinerama (compiledWithXinerama)
|
||||||
|
|
||||||
#ifdef TESTING
|
#ifdef TESTING
|
||||||
import qualified Properties
|
import qualified Properties
|
||||||
@@ -30,28 +35,59 @@ import qualified Properties
|
|||||||
-- for xmonad, and if it doesn't find one, just launches the default.
|
-- for xmonad, and if it doesn't find one, just launches the default.
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
|
installSignalHandlers -- important to ignore SIGCHLD to avoid zombies
|
||||||
args <- getArgs
|
args <- getArgs
|
||||||
let launch = handle (hPrint stderr) buildLaunch >> xmonad defaultConfig
|
let launch = catchIO buildLaunch >> xmonad defaultConfig
|
||||||
case args of
|
case args of
|
||||||
[] -> launch
|
[] -> launch
|
||||||
["--resume", _] -> launch
|
("--resume":_) -> launch
|
||||||
["--recompile"] -> recompile False >> return ()
|
["--help"] -> usage
|
||||||
["--recompile-force"] -> recompile True >> return ()
|
["--recompile"] -> recompile True >>= flip unless exitFailure
|
||||||
["--version"] -> putStrLn "xmonad 0.5"
|
["--replace"] -> launch
|
||||||
|
["--restart"] -> sendRestart >> return ()
|
||||||
|
["--version"] -> putStrLn $ unwords shortVersion
|
||||||
|
["--verbose-version"] -> putStrLn . unwords $ shortVersion ++ longVersion
|
||||||
#ifdef TESTING
|
#ifdef TESTING
|
||||||
("--run-tests":_) -> Properties.main
|
("--run-tests":_) -> Properties.main
|
||||||
#endif
|
#endif
|
||||||
_ -> fail "unrecognized flags"
|
_ -> fail "unrecognized flags"
|
||||||
|
where
|
||||||
|
shortVersion = ["xmonad", showVersion version]
|
||||||
|
longVersion = [ "compiled by", compilerName, showVersion compilerVersion
|
||||||
|
, "for", arch ++ "-" ++ os
|
||||||
|
, "\nXinerama:", show compiledWithXinerama ]
|
||||||
|
|
||||||
-- | Build "~/.xmonad/xmonad.hs" with ghc, then execute it. If there are no
|
usage :: IO ()
|
||||||
|
usage = do
|
||||||
|
self <- getProgName
|
||||||
|
putStr . unlines $
|
||||||
|
concat ["Usage: ", self, " [OPTION]"] :
|
||||||
|
"Options:" :
|
||||||
|
" --help Print this message" :
|
||||||
|
" --version Print the version number" :
|
||||||
|
" --recompile Recompile your ~/.xmonad/xmonad.hs" :
|
||||||
|
" --replace Replace the running window manager with xmonad" :
|
||||||
|
" --restart Request a running xmonad process to restart" :
|
||||||
|
#ifdef TESTING
|
||||||
|
" --run-tests Run the test suite" :
|
||||||
|
#endif
|
||||||
|
[]
|
||||||
|
|
||||||
|
-- | Build "~\/.xmonad\/xmonad.hs" with ghc, then execute it. If there are no
|
||||||
-- errors, this function does not return. An exception is raised in any of
|
-- errors, this function does not return. An exception is raised in any of
|
||||||
-- these cases:
|
-- these cases:
|
||||||
|
--
|
||||||
-- * ghc missing
|
-- * ghc missing
|
||||||
-- * ~/.xmonad/xmonad.hs missing
|
--
|
||||||
|
-- * both "~\/.xmonad\/xmonad.hs" and "~\/.xmonad\/xmonad-$arch-$os" missing
|
||||||
|
--
|
||||||
-- * xmonad.hs fails to compile
|
-- * xmonad.hs fails to compile
|
||||||
|
--
|
||||||
-- ** wrong ghc in path (fails to compile)
|
-- ** wrong ghc in path (fails to compile)
|
||||||
|
--
|
||||||
-- ** type error, syntax error, ..
|
-- ** type error, syntax error, ..
|
||||||
-- * Missing xmonad/XMonadContrib modules due to ghc upgrade
|
--
|
||||||
|
-- * Missing XMonad\/XMonadContrib modules due to ghc upgrade
|
||||||
--
|
--
|
||||||
buildLaunch :: IO ()
|
buildLaunch :: IO ()
|
||||||
buildLaunch = do
|
buildLaunch = do
|
||||||
@@ -60,3 +96,14 @@ buildLaunch = do
|
|||||||
args <- getArgs
|
args <- getArgs
|
||||||
executeFile (dir ++ "/xmonad-"++arch++"-"++os) False args Nothing
|
executeFile (dir ++ "/xmonad-"++arch++"-"++os) False args Nothing
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
|
sendRestart :: IO ()
|
||||||
|
sendRestart = do
|
||||||
|
dpy <- openDisplay ""
|
||||||
|
rw <- rootWindow dpy $ defaultScreen dpy
|
||||||
|
xmonad_restart <- internAtom dpy "XMONAD_RESTART" False
|
||||||
|
allocaXEvent $ \e -> do
|
||||||
|
setEventType e clientMessage
|
||||||
|
setClientMessageEvent e rw xmonad_restart 32 0 currentTime
|
||||||
|
sendEvent dpy rw False structureNotifyMask e
|
||||||
|
sync dpy False
|
||||||
|
51
README
51
README
@@ -12,20 +12,30 @@
|
|||||||
workspace. Xinerama is fully supported, allowing windows to be tiled
|
workspace. Xinerama is fully supported, allowing windows to be tiled
|
||||||
on several physical screens.
|
on several physical screens.
|
||||||
|
|
||||||
|
Quick start:
|
||||||
|
|
||||||
|
Obtain the dependent libraries, then build with:
|
||||||
|
|
||||||
|
runhaskell Setup.lhs configure --user --prefix=$HOME
|
||||||
|
runhaskell Setup.lhs build
|
||||||
|
runhaskell Setup.lhs install --user
|
||||||
|
|
||||||
|
For the full story, read on.
|
||||||
|
|
||||||
Building:
|
Building:
|
||||||
|
|
||||||
Building is quite straightforward, and requries a basic Haskell toolchain.
|
Building is quite straightforward, and requires a basic Haskell toolchain.
|
||||||
On many systems xmonad is available as a binary package in your
|
On many systems xmonad is available as a binary package in your
|
||||||
package system (e.g. on debian or gentoo). If at all possible, use this
|
package system (e.g. on Debian or Gentoo). If at all possible, use this
|
||||||
in preference to a source build, as the dependency resolution will be
|
in preference to a source build, as the dependency resolution will be
|
||||||
simpler.
|
simpler.
|
||||||
|
|
||||||
We'll now walk through the complete list of toolchain dependencies.
|
We'll now walk through the complete list of toolchain dependencies.
|
||||||
|
|
||||||
* GHC: the Glasgow Haskell Compiler
|
* GHC: the Glasgow Haskell Compiler
|
||||||
|
|
||||||
You first need a Haskell compiler. Your distribution's package
|
You first need a Haskell compiler. Your distribution's package
|
||||||
system will have binaries of GHC (the Glasgow Haskell Compiler), the
|
system will have binaries of GHC (the Glasgow Haskell Compiler), the
|
||||||
compiler we use, so install that first. If your operating system's
|
compiler we use, so install that first. If your operating system's
|
||||||
package system doesn't provide a binary version of GHC, you can find
|
package system doesn't provide a binary version of GHC, you can find
|
||||||
them here:
|
them here:
|
||||||
@@ -36,7 +46,7 @@ Building:
|
|||||||
|
|
||||||
apt-get install ghc6
|
apt-get install ghc6
|
||||||
|
|
||||||
It shouldn't be necessary to compile GHC from source -- every common
|
It shouldn't be necessary to compile GHC from source -- every common
|
||||||
system has a pre-build binary version.
|
system has a pre-build binary version.
|
||||||
|
|
||||||
* X11 libraries:
|
* X11 libraries:
|
||||||
@@ -50,7 +60,7 @@ Building:
|
|||||||
Typically you need: libXinerama libXext libX11
|
Typically you need: libXinerama libXext libX11
|
||||||
|
|
||||||
* Cabal
|
* Cabal
|
||||||
|
|
||||||
xmonad requires a recent version of Cabal, >= 1.2.0. If you're using
|
xmonad requires a recent version of Cabal, >= 1.2.0. If you're using
|
||||||
GHC 6.8, then it comes bundled with the right version. If you're
|
GHC 6.8, then it comes bundled with the right version. If you're
|
||||||
using GHC 6.6.x, you'll need to build and install Cabal from hackage
|
using GHC 6.6.x, you'll need to build and install Cabal from hackage
|
||||||
@@ -70,11 +80,11 @@ Building:
|
|||||||
provided. To check whether you've got a package run 'ghc-pkg list
|
provided. To check whether you've got a package run 'ghc-pkg list
|
||||||
some_package_name'. You will need the following packages:
|
some_package_name'. You will need the following packages:
|
||||||
|
|
||||||
mtl http://hackage.haskell.org/cgi-bin/hackage-scripts/package/mtl-1.0
|
mtl http://hackage.haskell.org/cgi-bin/hackage-scripts/package/mtl
|
||||||
unix http://hackage.haskell.org/cgi-bin/hackage-scripts/package/unix-2.0
|
unix http://hackage.haskell.org/cgi-bin/hackage-scripts/package/unix
|
||||||
X11 http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-1.4.1
|
X11 http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11
|
||||||
|
|
||||||
* Build xmonad:
|
* Build xmonad:
|
||||||
|
|
||||||
Once you've got all the dependencies in place (which should be
|
Once you've got all the dependencies in place (which should be
|
||||||
straightforward), build xmonad:
|
straightforward), build xmonad:
|
||||||
@@ -87,19 +97,6 @@ Building:
|
|||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
Notes for using the darcs version
|
|
||||||
|
|
||||||
If you're building the darcs version of xmonad, be sure to also
|
|
||||||
use the darcs version of the X11 library, which is developed
|
|
||||||
concurrently with xmonad.
|
|
||||||
|
|
||||||
darcs get http://darcs.haskell.org/X11
|
|
||||||
|
|
||||||
Not using X11 from darcs is the most common reason for the
|
|
||||||
darcs version of xmonad to fail to build.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
Running xmonad:
|
Running xmonad:
|
||||||
|
|
||||||
Add:
|
Add:
|
||||||
@@ -123,22 +120,22 @@ XMonadContrib
|
|||||||
prompt/program launcher, and various other useful modules.
|
prompt/program launcher, and various other useful modules.
|
||||||
XMonadContrib is available at:
|
XMonadContrib is available at:
|
||||||
|
|
||||||
0.5 release: http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmonad-contrib-0.5
|
latest release: http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmonad-contrib
|
||||||
|
|
||||||
darcs version: darcs get http://code.haskell.org/XMonadContrib
|
darcs version: darcs get http://code.haskell.org/XMonadContrib
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
Other useful programs:
|
Other useful programs:
|
||||||
|
|
||||||
A nicer xterm replacment, that supports resizing better:
|
A nicer xterm replacement, that supports resizing better:
|
||||||
|
|
||||||
urxvt http://software.schmorp.de/pkg/rxvt-unicode.html
|
urxvt http://software.schmorp.de/pkg/rxvt-unicode.html
|
||||||
|
|
||||||
For custom status bars:
|
For custom status bars:
|
||||||
|
|
||||||
dzen http://gotmor.googlepages.com/dzen
|
dzen http://gotmor.googlepages.com/dzen
|
||||||
xmobar http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmobar
|
xmobar http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmobar
|
||||||
|
|
||||||
For a program dispatch menu:
|
For a program dispatch menu:
|
||||||
|
|
||||||
|
4
STYLE
4
STYLE
@@ -2,7 +2,7 @@
|
|||||||
== Coding guidelines for contributing to
|
== Coding guidelines for contributing to
|
||||||
== xmonad and the xmonad contributed extensions
|
== xmonad and the xmonad contributed extensions
|
||||||
|
|
||||||
* Comment every top level function (particularly exported funtions), and
|
* Comment every top level function (particularly exported functions), and
|
||||||
provide a type signature; use Haddock syntax in the comments.
|
provide a type signature; use Haddock syntax in the comments.
|
||||||
|
|
||||||
* Follow the coding style of the other modules.
|
* Follow the coding style of the other modules.
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
* Tabs are illegal. Use 4 spaces for indenting.
|
* Tabs are illegal. Use 4 spaces for indenting.
|
||||||
|
|
||||||
* Any pure function added to the core should have QuickCheck properties
|
* Any pure function added to the core should have QuickCheck properties
|
||||||
precisely defining its behaviour.
|
precisely defining its behavior.
|
||||||
|
|
||||||
* New modules should identify the author, and be submitted under
|
* New modules should identify the author, and be submitted under
|
||||||
the same license as xmonad (BSD3 license or freer).
|
the same license as xmonad (BSD3 license or freer).
|
||||||
|
7
TODO
7
TODO
@@ -1,7 +1,7 @@
|
|||||||
- Write down invariants for the window life cycle, especially:
|
- Write down invariants for the window life cycle, especially:
|
||||||
- When are borders set? Prove that the current handling is sufficient.
|
- When are borders set? Prove that the current handling is sufficient.
|
||||||
|
|
||||||
- current floating layer handling is unoptimal. FocusUp should raise,
|
- current floating layer handling is nonoptimal. FocusUp should raise,
|
||||||
for example
|
for example
|
||||||
|
|
||||||
- Issues still with stacking order.
|
- Issues still with stacking order.
|
||||||
@@ -15,6 +15,9 @@
|
|||||||
* double check README build instructions
|
* double check README build instructions
|
||||||
* test core with 6.6 and 6.8
|
* test core with 6.6 and 6.8
|
||||||
* bump xmonad.cabal version and X11 version
|
* bump xmonad.cabal version and X11 version
|
||||||
* upload X11 and xmonad to hackage
|
* upload X11 and xmonad to Hackage
|
||||||
|
* update links to hackage in download.html
|
||||||
|
* update #xmonad topic
|
||||||
* check examples/text in user-facing Config.hs
|
* check examples/text in user-facing Config.hs
|
||||||
* check tour.html and intro.html are up to date, and mention all core bindings
|
* check tour.html and intro.html are up to date, and mention all core bindings
|
||||||
|
* confirm template config is type correct
|
||||||
|
@@ -25,22 +25,24 @@ module XMonad.Config (defaultConfig) where
|
|||||||
-- Useful imports
|
-- Useful imports
|
||||||
--
|
--
|
||||||
import XMonad.Core as XMonad hiding
|
import XMonad.Core as XMonad hiding
|
||||||
(workspaces,manageHook,numlockMask,keys,logHook,borderWidth,mouseBindings
|
(workspaces,manageHook,keys,logHook,startupHook,borderWidth,mouseBindings
|
||||||
,defaultGaps,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor
|
,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor,focusFollowsMouse
|
||||||
,focusFollowsMouse)
|
,handleEventHook)
|
||||||
import qualified XMonad.Core as XMonad
|
import qualified XMonad.Core as XMonad
|
||||||
(workspaces,manageHook,numlockMask,keys,logHook,borderWidth,mouseBindings
|
(workspaces,manageHook,keys,logHook,startupHook,borderWidth,mouseBindings
|
||||||
,defaultGaps,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor
|
,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor,focusFollowsMouse
|
||||||
,focusFollowsMouse)
|
,handleEventHook)
|
||||||
|
|
||||||
import XMonad.Layout
|
import XMonad.Layout
|
||||||
import XMonad.Operations
|
import XMonad.Operations
|
||||||
import XMonad.ManageHook
|
import XMonad.ManageHook
|
||||||
import qualified XMonad.StackSet as W
|
import qualified XMonad.StackSet as W
|
||||||
import Data.Bits ((.|.))
|
import Data.Bits ((.|.))
|
||||||
|
import Data.Monoid
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import System.Exit
|
import System.Exit
|
||||||
import Graphics.X11.Xlib
|
import Graphics.X11.Xlib
|
||||||
|
import Graphics.X11.Xlib.Extras
|
||||||
|
|
||||||
-- | The default number of workspaces (virtual screens) and their names.
|
-- | The default number of workspaces (virtual screens) and their names.
|
||||||
-- By default we use numeric strings, but any string may be used as a
|
-- By default we use numeric strings, but any string may be used as a
|
||||||
@@ -62,22 +64,6 @@ workspaces = map show [1 .. 9 :: Int]
|
|||||||
defaultModMask :: KeyMask
|
defaultModMask :: KeyMask
|
||||||
defaultModMask = mod1Mask
|
defaultModMask = mod1Mask
|
||||||
|
|
||||||
-- | The mask for the numlock key. Numlock status is "masked" from the
|
|
||||||
-- current modifier status, so the keybindings will work with numlock on or
|
|
||||||
-- off. You may need to change this on some systems.
|
|
||||||
--
|
|
||||||
-- You can find the numlock modifier by running "xmodmap" and looking for a
|
|
||||||
-- modifier with Num_Lock bound to it:
|
|
||||||
--
|
|
||||||
-- > $ xmodmap | grep Num
|
|
||||||
-- > mod2 Num_Lock (0x4d)
|
|
||||||
--
|
|
||||||
-- Set numlockMask = 0 if you don't have a numlock key, or want to treat
|
|
||||||
-- numlock status separately.
|
|
||||||
--
|
|
||||||
numlockMask :: KeyMask
|
|
||||||
numlockMask = mod2Mask
|
|
||||||
|
|
||||||
-- | Width of the window border in pixels.
|
-- | Width of the window border in pixels.
|
||||||
--
|
--
|
||||||
borderWidth :: Dimension
|
borderWidth :: Dimension
|
||||||
@@ -86,23 +72,8 @@ borderWidth = 1
|
|||||||
-- | Border colors for unfocused and focused windows, respectively.
|
-- | Border colors for unfocused and focused windows, respectively.
|
||||||
--
|
--
|
||||||
normalBorderColor, focusedBorderColor :: String
|
normalBorderColor, focusedBorderColor :: String
|
||||||
normalBorderColor = "#dddddd"
|
normalBorderColor = "gray" -- "#dddddd"
|
||||||
focusedBorderColor = "#ff0000"
|
focusedBorderColor = "red" -- "#ff0000" don't use hex, not <24 bit safe
|
||||||
|
|
||||||
-- | Default offset of drawable screen boundaries from each physical
|
|
||||||
-- screen. Anything non-zero here will leave a gap of that many pixels
|
|
||||||
-- on the given edge, on the that screen. A useful gap at top of screen
|
|
||||||
-- for a menu bar (e.g. 15)
|
|
||||||
--
|
|
||||||
-- An example, to set a top gap on monitor 1, and a gap on the bottom of
|
|
||||||
-- monitor 2, you'd use a list of geometries like so:
|
|
||||||
--
|
|
||||||
-- > defaultGaps = [(18,0,0,0),(0,18,0,0)] -- 2 gaps on 2 monitors
|
|
||||||
--
|
|
||||||
-- Fields are: top, bottom, left, right.
|
|
||||||
--
|
|
||||||
defaultGaps :: [(Int,Int,Int,Int)]
|
|
||||||
defaultGaps = [(0,0,0,0)] -- 15 for default dzen font
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
-- Window rules
|
-- Window rules
|
||||||
@@ -126,7 +97,9 @@ manageHook = composeAll
|
|||||||
|
|
||||||
-- | Perform an arbitrary action on each internal state change or X event.
|
-- | Perform an arbitrary action on each internal state change or X event.
|
||||||
-- Examples include:
|
-- Examples include:
|
||||||
|
--
|
||||||
-- * do nothing
|
-- * do nothing
|
||||||
|
--
|
||||||
-- * log the state to stdout
|
-- * log the state to stdout
|
||||||
--
|
--
|
||||||
-- See the 'DynamicLog' extension for examples.
|
-- See the 'DynamicLog' extension for examples.
|
||||||
@@ -134,6 +107,19 @@ manageHook = composeAll
|
|||||||
logHook :: X ()
|
logHook :: X ()
|
||||||
logHook = return ()
|
logHook = return ()
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
-- Event handling
|
||||||
|
|
||||||
|
-- | Defines a custom handler function for X Events. The function should
|
||||||
|
-- return (All True) if the default handler is to be run afterwards.
|
||||||
|
-- To combine event hooks, use mappend or mconcat from Data.Monoid.
|
||||||
|
handleEventHook :: Event -> X All
|
||||||
|
handleEventHook _ = return (All True)
|
||||||
|
|
||||||
|
-- | Perform an arbitrary action at xmonad startup.
|
||||||
|
startupHook :: X ()
|
||||||
|
startupHook = return ()
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
-- Extensible layouts
|
-- Extensible layouts
|
||||||
--
|
--
|
||||||
@@ -179,7 +165,7 @@ keys :: XConfig Layout -> M.Map (KeyMask, KeySym) (X ())
|
|||||||
keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
||||||
-- launching and killing programs
|
-- launching and killing programs
|
||||||
[ ((modMask .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf) -- %! Launch terminal
|
[ ((modMask .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf) -- %! Launch terminal
|
||||||
, ((modMask, xK_p ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"") -- %! Launch dmenu
|
, ((modMask, xK_p ), spawn "dmenu_run") -- %! Launch dmenu
|
||||||
, ((modMask .|. shiftMask, xK_p ), spawn "gmrun") -- %! Launch gmrun
|
, ((modMask .|. shiftMask, xK_p ), spawn "gmrun") -- %! Launch gmrun
|
||||||
, ((modMask .|. shiftMask, xK_c ), kill) -- %! Close the focused window
|
, ((modMask .|. shiftMask, xK_c ), kill) -- %! Close the focused window
|
||||||
|
|
||||||
@@ -190,6 +176,7 @@ keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
|||||||
|
|
||||||
-- move focus up or down the window stack
|
-- move focus up or down the window stack
|
||||||
, ((modMask, xK_Tab ), windows W.focusDown) -- %! Move focus to the next window
|
, ((modMask, xK_Tab ), windows W.focusDown) -- %! Move focus to the next window
|
||||||
|
, ((modMask .|. shiftMask, xK_Tab ), windows W.focusUp ) -- %! Move focus to the previous window
|
||||||
, ((modMask, xK_j ), windows W.focusDown) -- %! Move focus to the next window
|
, ((modMask, xK_j ), windows W.focusDown) -- %! Move focus to the next window
|
||||||
, ((modMask, xK_k ), windows W.focusUp ) -- %! Move focus to the previous window
|
, ((modMask, xK_k ), windows W.focusUp ) -- %! Move focus to the previous window
|
||||||
, ((modMask, xK_m ), windows W.focusMaster ) -- %! Move focus to the master window
|
, ((modMask, xK_m ), windows W.focusMaster ) -- %! Move focus to the master window
|
||||||
@@ -211,11 +198,11 @@ keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
|||||||
, ((modMask , xK_period), sendMessage (IncMasterN (-1))) -- %! Deincrement the number of windows in the master area
|
, ((modMask , xK_period), sendMessage (IncMasterN (-1))) -- %! Deincrement the number of windows in the master area
|
||||||
|
|
||||||
-- toggle the status bar gap
|
-- toggle the status bar gap
|
||||||
, ((modMask , xK_b ), modifyGap (\i n -> let x = (XMonad.defaultGaps conf ++ repeat (0,0,0,0)) !! i in if n == x then (0,0,0,0) else x)) -- %! Toggle the status bar gap
|
--, ((modMask , xK_b ), modifyGap (\i n -> let x = (XMonad.defaultGaps conf ++ repeat (0,0,0,0)) !! i in if n == x then (0,0,0,0) else x)) -- %! Toggle the status bar gap
|
||||||
|
|
||||||
-- quit, or restart
|
-- quit, or restart
|
||||||
, ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess)) -- %! Quit xmonad
|
, ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess)) -- %! Quit xmonad
|
||||||
, ((modMask , xK_q ), restart "xmonad" True) -- %! Restart xmonad
|
, ((modMask , xK_q ), spawn "if type xmonad; then xmonad --recompile && xmonad --restart; else xmessage xmonad not in \\$PATH: \"$PATH\"; fi") -- %! Restart xmonad
|
||||||
]
|
]
|
||||||
++
|
++
|
||||||
-- mod-[1..9] %! Switch to workspace N
|
-- mod-[1..9] %! Switch to workspace N
|
||||||
@@ -233,13 +220,15 @@ keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
|||||||
-- | Mouse bindings: default actions bound to mouse events
|
-- | Mouse bindings: default actions bound to mouse events
|
||||||
--
|
--
|
||||||
mouseBindings :: XConfig Layout -> M.Map (KeyMask, Button) (Window -> X ())
|
mouseBindings :: XConfig Layout -> M.Map (KeyMask, Button) (Window -> X ())
|
||||||
mouseBindings (XConfig {XMonad.modMask = modMask}) = M.fromList $
|
mouseBindings (XConfig {XMonad.modMask = modMask}) = M.fromList
|
||||||
-- mod-button1 %! Set the window to floating mode and move by dragging
|
-- mod-button1 %! Set the window to floating mode and move by dragging
|
||||||
[ ((modMask, button1), (\w -> focus w >> mouseMoveWindow w))
|
[ ((modMask, button1), \w -> focus w >> mouseMoveWindow w
|
||||||
|
>> windows W.shiftMaster)
|
||||||
-- mod-button2 %! Raise the window to the top of the stack
|
-- mod-button2 %! Raise the window to the top of the stack
|
||||||
, ((modMask, button2), (\w -> focus w >> windows W.swapMaster))
|
, ((modMask, button2), windows . (W.shiftMaster .) . W.focusWindow)
|
||||||
-- mod-button3 %! Set the window to floating mode and resize by dragging
|
-- mod-button3 %! Set the window to floating mode and resize by dragging
|
||||||
, ((modMask, button3), (\w -> focus w >> mouseResizeWindow w))
|
, ((modMask, button3), \w -> focus w >> mouseResizeWindow w
|
||||||
|
>> windows W.shiftMaster)
|
||||||
-- you may also bind events to the mouse scroll wheel (button4 and button5)
|
-- you may also bind events to the mouse scroll wheel (button4 and button5)
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -247,15 +236,16 @@ mouseBindings (XConfig {XMonad.modMask = modMask}) = M.fromList $
|
|||||||
defaultConfig = XConfig
|
defaultConfig = XConfig
|
||||||
{ XMonad.borderWidth = borderWidth
|
{ XMonad.borderWidth = borderWidth
|
||||||
, XMonad.workspaces = workspaces
|
, XMonad.workspaces = workspaces
|
||||||
, XMonad.defaultGaps = defaultGaps
|
|
||||||
, XMonad.layoutHook = layout
|
, XMonad.layoutHook = layout
|
||||||
, XMonad.terminal = terminal
|
, XMonad.terminal = terminal
|
||||||
, XMonad.normalBorderColor = normalBorderColor
|
, XMonad.normalBorderColor = normalBorderColor
|
||||||
, XMonad.focusedBorderColor = focusedBorderColor
|
, XMonad.focusedBorderColor = focusedBorderColor
|
||||||
, XMonad.numlockMask = numlockMask
|
|
||||||
, XMonad.modMask = defaultModMask
|
, XMonad.modMask = defaultModMask
|
||||||
, XMonad.keys = keys
|
, XMonad.keys = keys
|
||||||
, XMonad.logHook = logHook
|
, XMonad.logHook = logHook
|
||||||
|
, XMonad.startupHook = startupHook
|
||||||
, XMonad.mouseBindings = mouseBindings
|
, XMonad.mouseBindings = mouseBindings
|
||||||
, XMonad.manageHook = manageHook
|
, XMonad.manageHook = manageHook
|
||||||
, XMonad.focusFollowsMouse = focusFollowsMouse }
|
, XMonad.handleEventHook = handleEventHook
|
||||||
|
, XMonad.focusFollowsMouse = focusFollowsMouse
|
||||||
|
}
|
||||||
|
385
XMonad/Core.hs
385
XMonad/Core.hs
@@ -1,7 +1,5 @@
|
|||||||
{-# LANGUAGE ExistentialQuantification, FlexibleInstances, GeneralizedNewtypeDeriving,
|
{-# LANGUAGE ExistentialQuantification, FlexibleInstances, GeneralizedNewtypeDeriving,
|
||||||
MultiParamTypeClasses, TypeSynonymInstances #-}
|
MultiParamTypeClasses, TypeSynonymInstances, CPP, DeriveDataTypeable #-}
|
||||||
-- required for deriving Typeable
|
|
||||||
{-# OPTIONS_GHC -fglasgow-exts #-}
|
|
||||||
|
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
@@ -9,11 +7,11 @@
|
|||||||
-- Copyright : (c) Spencer Janssen 2007
|
-- Copyright : (c) Spencer Janssen 2007
|
||||||
-- License : BSD3-style (see LICENSE)
|
-- License : BSD3-style (see LICENSE)
|
||||||
--
|
--
|
||||||
-- Maintainer : sjanssen@cse.unl.edu
|
-- Maintainer : spencerjanssen@gmail.com
|
||||||
-- Stability : unstable
|
-- Stability : unstable
|
||||||
-- Portability : not portable, uses cunning newtype deriving
|
-- Portability : not portable, uses cunning newtype deriving
|
||||||
--
|
--
|
||||||
-- The X monad, a state monad transformer over IO, for the window
|
-- The 'X' monad, a state monad transformer over 'IO', for the window
|
||||||
-- manager state, and support routines.
|
-- manager state, and support routines.
|
||||||
--
|
--
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
@@ -23,42 +21,57 @@ module XMonad.Core (
|
|||||||
ScreenId(..), ScreenDetail(..), XState(..),
|
ScreenId(..), ScreenDetail(..), XState(..),
|
||||||
XConf(..), XConfig(..), LayoutClass(..),
|
XConf(..), XConfig(..), LayoutClass(..),
|
||||||
Layout(..), readsLayout, Typeable, Message,
|
Layout(..), readsLayout, Typeable, Message,
|
||||||
SomeMessage(..), fromMessage, runLayout, LayoutMessages(..),
|
SomeMessage(..), fromMessage, LayoutMessages(..),
|
||||||
runX, catchX, userCode, io, catchIO, doubleFork,
|
StateExtension(..), ExtensionClass(..),
|
||||||
withDisplay, withWindowSet, isRoot, runOnWorkspaces, broadcastMessage,
|
runX, catchX, userCode, userCodeDef, io, catchIO, installSignalHandlers, uninstallSignalHandlers,
|
||||||
getAtom, spawn, restart, getXMonadDir, recompile, trace, whenJust, whenX,
|
withDisplay, withWindowSet, isRoot, runOnWorkspaces,
|
||||||
atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, ManageHook, Query(..), runManageHook
|
getAtom, spawn, spawnPID, xfork, getXMonadDir, recompile, trace, whenJust, whenX,
|
||||||
|
atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, ManageHook, Query(..), runQuery
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import XMonad.StackSet hiding (modify)
|
import XMonad.StackSet hiding (modify)
|
||||||
|
|
||||||
import Prelude hiding ( catch )
|
import Prelude hiding ( catch )
|
||||||
import Control.Exception (catch, bracket, throw, Exception(ExitException))
|
import Codec.Binary.UTF8.String (encodeString)
|
||||||
|
import Control.Exception.Extensible (catch, fromException, try, bracket, throw, finally, SomeException(..))
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Control.Monad.State
|
import Control.Monad.State
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
|
import System.FilePath
|
||||||
import System.IO
|
import System.IO
|
||||||
import System.Info
|
import System.Info
|
||||||
import System.Posix.Process (executeFile, forkProcess, getProcessStatus, createSession)
|
import System.Posix.Process (executeFile, forkProcess, getAnyProcessStatus, createSession)
|
||||||
|
import System.Posix.Signals
|
||||||
|
import System.Posix.IO
|
||||||
|
import System.Posix.Types (ProcessID)
|
||||||
import System.Process
|
import System.Process
|
||||||
import System.Directory
|
import System.Directory
|
||||||
import System.Exit
|
import System.Exit
|
||||||
import Graphics.X11.Xlib
|
import Graphics.X11.Xlib
|
||||||
import Graphics.X11.Xlib.Extras (Event)
|
import Graphics.X11.Xlib.Extras (Event)
|
||||||
import Data.Typeable
|
import Data.Typeable
|
||||||
|
import Data.List ((\\))
|
||||||
|
import Data.Maybe (isJust,fromMaybe)
|
||||||
import Data.Monoid
|
import Data.Monoid
|
||||||
|
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
|
|
||||||
-- | XState, the window manager state.
|
-- | XState, the (mutable) window manager state.
|
||||||
-- Just the display, width, height and a window list
|
|
||||||
data XState = XState
|
data XState = XState
|
||||||
{ windowset :: !WindowSet -- ^ workspace list
|
{ windowset :: !WindowSet -- ^ workspace list
|
||||||
, mapped :: !(S.Set Window) -- ^ the Set of mapped windows
|
, mapped :: !(S.Set Window) -- ^ the Set of mapped windows
|
||||||
, waitingUnmap :: !(M.Map Window Int) -- ^ the number of expected UnmapEvents
|
, waitingUnmap :: !(M.Map Window Int) -- ^ the number of expected UnmapEvents
|
||||||
, dragging :: !(Maybe (Position -> Position -> X (), X ())) }
|
, dragging :: !(Maybe (Position -> Position -> X (), X ()))
|
||||||
|
, numberlockMask :: !KeyMask -- ^ The numlock modifier
|
||||||
|
, extensibleState :: !(M.Map String (Either String StateExtension))
|
||||||
|
-- ^ stores custom state information.
|
||||||
|
--
|
||||||
|
-- The module "XMonad.Utils.ExtensibleState" in xmonad-contrib
|
||||||
|
-- provides additional information and a simple interface for using this.
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | XConf, the (read-only) window manager configuration.
|
||||||
data XConf = XConf
|
data XConf = XConf
|
||||||
{ display :: Display -- ^ the X11 display
|
{ display :: Display -- ^ the X11 display
|
||||||
, config :: !(XConfig Layout) -- ^ initial user configuration
|
, config :: !(XConfig Layout) -- ^ initial user configuration
|
||||||
@@ -69,6 +82,10 @@ data XConf = XConf
|
|||||||
-- ^ a mapping of key presses to actions
|
-- ^ a mapping of key presses to actions
|
||||||
, buttonActions :: !(M.Map (KeyMask, Button) (Window -> X ()))
|
, buttonActions :: !(M.Map (KeyMask, Button) (Window -> X ()))
|
||||||
-- ^ a mapping of button presses to actions
|
-- ^ a mapping of button presses to actions
|
||||||
|
, mouseFocused :: !Bool -- ^ was refocus caused by mouse action?
|
||||||
|
, mousePosition :: !(Maybe (Position, Position))
|
||||||
|
-- ^ position of the mouse according to
|
||||||
|
-- the event currently being processed
|
||||||
}
|
}
|
||||||
|
|
||||||
-- todo, better name
|
-- todo, better name
|
||||||
@@ -78,9 +95,10 @@ data XConfig l = XConfig
|
|||||||
, terminal :: !String -- ^ The preferred terminal application. Default: \"xterm\"
|
, terminal :: !String -- ^ The preferred terminal application. Default: \"xterm\"
|
||||||
, layoutHook :: !(l Window) -- ^ The available layouts
|
, layoutHook :: !(l Window) -- ^ The available layouts
|
||||||
, manageHook :: !ManageHook -- ^ The action to run when a new window is opened
|
, manageHook :: !ManageHook -- ^ The action to run when a new window is opened
|
||||||
|
, handleEventHook :: !(Event -> X All) -- ^ Handle an X event, returns (All True) if the default handler
|
||||||
|
-- should also be run afterwards. mappend should be used for combining
|
||||||
|
-- event hooks in most cases.
|
||||||
, workspaces :: ![String] -- ^ The list of workspaces' names
|
, workspaces :: ![String] -- ^ The list of workspaces' names
|
||||||
, defaultGaps :: ![(Int,Int,Int,Int)] -- ^ The list of gaps, per screen
|
|
||||||
, numlockMask :: !KeyMask -- ^ The numlock modifier
|
|
||||||
, modMask :: !KeyMask -- ^ the mod modifier
|
, modMask :: !KeyMask -- ^ the mod modifier
|
||||||
, keys :: !(XConfig Layout -> M.Map (ButtonMask,KeySym) (X ()))
|
, keys :: !(XConfig Layout -> M.Map (ButtonMask,KeySym) (X ()))
|
||||||
-- ^ The key binding: a map from key presses and actions
|
-- ^ The key binding: a map from key presses and actions
|
||||||
@@ -88,6 +106,7 @@ data XConfig l = XConfig
|
|||||||
-- ^ The mouse bindings
|
-- ^ The mouse bindings
|
||||||
, borderWidth :: !Dimension -- ^ The border width
|
, borderWidth :: !Dimension -- ^ The border width
|
||||||
, logHook :: !(X ()) -- ^ The action to perform when the windows set is changed
|
, logHook :: !(X ()) -- ^ The action to perform when the windows set is changed
|
||||||
|
, startupHook :: !(X ()) -- ^ The action to perform on startup
|
||||||
, focusFollowsMouse :: !Bool -- ^ Whether window entry events can change focus
|
, focusFollowsMouse :: !Bool -- ^ Whether window entry events can change focus
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,30 +114,31 @@ data XConfig l = XConfig
|
|||||||
type WindowSet = StackSet WorkspaceId (Layout Window) Window ScreenId ScreenDetail
|
type WindowSet = StackSet WorkspaceId (Layout Window) Window ScreenId ScreenDetail
|
||||||
type WindowSpace = Workspace WorkspaceId (Layout Window) Window
|
type WindowSpace = Workspace WorkspaceId (Layout Window) Window
|
||||||
|
|
||||||
-- | Virtual workspace indicies
|
-- | Virtual workspace indices
|
||||||
type WorkspaceId = String
|
type WorkspaceId = String
|
||||||
|
|
||||||
-- | Physical screen indicies
|
-- | Physical screen indices
|
||||||
newtype ScreenId = S Int deriving (Eq,Ord,Show,Read,Enum,Num,Integral,Real)
|
newtype ScreenId = S Int deriving (Eq,Ord,Show,Read,Enum,Num,Integral,Real)
|
||||||
|
|
||||||
-- | The 'Rectangle' with screen dimensions and the list of gaps
|
-- | The 'Rectangle' with screen dimensions
|
||||||
data ScreenDetail = SD { screenRect :: !Rectangle
|
data ScreenDetail = SD { screenRect :: !Rectangle } deriving (Eq,Show, Read)
|
||||||
, statusGap :: !(Int,Int,Int,Int) -- ^ width of status bar on the screen
|
|
||||||
} deriving (Eq,Show, Read)
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
-- | The X monad, a StateT transformer over IO encapsulating the window
|
-- | The X monad, 'ReaderT' and 'StateT' transformers over 'IO'
|
||||||
-- manager state
|
-- encapsulating the window manager configuration and state,
|
||||||
|
-- respectively.
|
||||||
--
|
--
|
||||||
-- Dynamic components may be retrieved with 'get', static components
|
-- Dynamic components may be retrieved with 'get', static components
|
||||||
-- with 'ask'. With newtype deriving we get readers and state monads
|
-- with 'ask'. With newtype deriving we get readers and state monads
|
||||||
-- instantiated on XConf and XState automatically.
|
-- instantiated on 'XConf' and 'XState' automatically.
|
||||||
--
|
--
|
||||||
newtype X a = X (ReaderT XConf (StateT XState IO) a)
|
newtype X a = X (ReaderT XConf (StateT XState IO) a)
|
||||||
#ifndef __HADDOCK__
|
deriving (Functor, Monad, MonadIO, MonadState XState, MonadReader XConf, Typeable)
|
||||||
deriving (Functor, Monad, MonadIO, MonadState XState, MonadReader XConf)
|
|
||||||
#endif
|
instance Applicative X where
|
||||||
|
pure = return
|
||||||
|
(<*>) = ap
|
||||||
|
|
||||||
instance (Monoid a) => Monoid (X a) where
|
instance (Monoid a) => Monoid (X a) where
|
||||||
mempty = return mempty
|
mempty = return mempty
|
||||||
@@ -126,38 +146,41 @@ instance (Monoid a) => Monoid (X a) where
|
|||||||
|
|
||||||
type ManageHook = Query (Endo WindowSet)
|
type ManageHook = Query (Endo WindowSet)
|
||||||
newtype Query a = Query (ReaderT Window X a)
|
newtype Query a = Query (ReaderT Window X a)
|
||||||
#ifndef __HADDOCK__
|
|
||||||
deriving (Functor, Monad, MonadReader Window, MonadIO)
|
deriving (Functor, Monad, MonadReader Window, MonadIO)
|
||||||
#endif
|
|
||||||
|
|
||||||
runManageHook :: ManageHook -> Window -> X (WindowSet -> WindowSet)
|
runQuery :: Query a -> Window -> X a
|
||||||
runManageHook (Query m) w = appEndo <$> runReaderT m w
|
runQuery (Query m) w = runReaderT m w
|
||||||
|
|
||||||
instance Monoid a => Monoid (Query a) where
|
instance Monoid a => Monoid (Query a) where
|
||||||
mempty = return mempty
|
mempty = return mempty
|
||||||
mappend = liftM2 mappend
|
mappend = liftM2 mappend
|
||||||
|
|
||||||
-- | Run the X monad, given a chunk of X monad code, and an initial state
|
-- | Run the 'X' monad, given a chunk of 'X' monad code, and an initial state
|
||||||
-- Return the result, and final state
|
-- Return the result, and final state
|
||||||
runX :: XConf -> XState -> X a -> IO (a, XState)
|
runX :: XConf -> XState -> X a -> IO (a, XState)
|
||||||
runX c st (X a) = runStateT (runReaderT a c) st
|
runX c st (X a) = runStateT (runReaderT a c) st
|
||||||
|
|
||||||
-- | Run in the X monad, and in case of exception, and catch it and log it
|
-- | Run in the 'X' monad, and in case of exception, and catch it and log it
|
||||||
-- to stderr, and run the error case.
|
-- to stderr, and run the error case.
|
||||||
catchX :: X a -> X a -> X a
|
catchX :: X a -> X a -> X a
|
||||||
catchX job errcase = do
|
catchX job errcase = do
|
||||||
st <- get
|
st <- get
|
||||||
c <- ask
|
c <- ask
|
||||||
(a, s') <- io $ runX c st job `catch` \e -> case e of
|
(a, s') <- io $ runX c st job `catch` \e -> case fromException e of
|
||||||
ExitException {} -> throw e
|
Just x -> throw e `const` (x `asTypeOf` ExitSuccess)
|
||||||
_ -> do hPrint stderr e; runX c st errcase
|
_ -> do hPrint stderr e; runX c st errcase
|
||||||
put s'
|
put s'
|
||||||
return a
|
return a
|
||||||
|
|
||||||
-- | Execute the argument, catching all exceptions. Either this function or
|
-- | Execute the argument, catching all exceptions. Either this function or
|
||||||
-- catchX should be used at all callsites of user customized code.
|
-- 'catchX' should be used at all callsites of user customized code.
|
||||||
userCode :: X () -> X ()
|
userCode :: X a -> X (Maybe a)
|
||||||
userCode a = catchX (a >> return ()) (return ())
|
userCode a = catchX (Just `liftM` a) (return Nothing)
|
||||||
|
|
||||||
|
-- | Same as userCode but with a default argument to return instead of using
|
||||||
|
-- Maybe, provided for convenience.
|
||||||
|
userCodeDef :: a -> X a -> X a
|
||||||
|
userCodeDef def a = fromMaybe def `liftM` userCode a
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
-- Convenient wrappers to state
|
-- Convenient wrappers to state
|
||||||
@@ -185,91 +208,135 @@ atom_WM_DELETE_WINDOW = getAtom "WM_DELETE_WINDOW"
|
|||||||
atom_WM_STATE = getAtom "WM_STATE"
|
atom_WM_STATE = getAtom "WM_STATE"
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
-- | LayoutClass handling. See particular instances in Operations.hs
|
-- LayoutClass handling. See particular instances in Operations.hs
|
||||||
|
|
||||||
-- | An existential type that can hold any object that is in Read and LayoutClass.
|
-- | An existential type that can hold any object that is in 'Read'
|
||||||
|
-- and 'LayoutClass'.
|
||||||
data Layout a = forall l. (LayoutClass l a, Read (l a)) => Layout (l a)
|
data Layout a = forall l. (LayoutClass l a, Read (l a)) => Layout (l a)
|
||||||
|
|
||||||
-- | Using the 'Layout' as a witness, parse existentially wrapped windows
|
-- | Using the 'Layout' as a witness, parse existentially wrapped windows
|
||||||
-- from a 'String'
|
-- from a 'String'.
|
||||||
readsLayout :: Layout a -> String -> [(Layout a, String)]
|
readsLayout :: Layout a -> String -> [(Layout a, String)]
|
||||||
readsLayout (Layout l) s = [(Layout (asTypeOf x l), rs) | (x, rs) <- reads s]
|
readsLayout (Layout l) s = [(Layout (asTypeOf x l), rs) | (x, rs) <- reads s]
|
||||||
|
|
||||||
-- | The different layout modes
|
-- | Every layout must be an instance of 'LayoutClass', which defines
|
||||||
|
-- the basic layout operations along with a sensible default for each.
|
||||||
--
|
--
|
||||||
-- 'doLayout': given a Rectangle and a Stack, layout the stack elements
|
-- Minimal complete definition:
|
||||||
-- inside the given Rectangle. If an element is not given a Rectangle
|
|
||||||
-- by 'doLayout', then it is not shown on screen. Windows are restacked
|
|
||||||
-- according to the order they are returned by 'doLayout'.
|
|
||||||
--
|
--
|
||||||
|
-- * 'runLayout' || (('doLayout' || 'pureLayout') && 'emptyLayout'), and
|
||||||
|
--
|
||||||
|
-- * 'handleMessage' || 'pureMessage'
|
||||||
|
--
|
||||||
|
-- You should also strongly consider implementing 'description',
|
||||||
|
-- although it is not required.
|
||||||
|
--
|
||||||
|
-- Note that any code which /uses/ 'LayoutClass' methods should only
|
||||||
|
-- ever call 'runLayout', 'handleMessage', and 'description'! In
|
||||||
|
-- other words, the only calls to 'doLayout', 'pureMessage', and other
|
||||||
|
-- such methods should be from the default implementations of
|
||||||
|
-- 'runLayout', 'handleMessage', and so on. This ensures that the
|
||||||
|
-- proper methods will be used, regardless of the particular methods
|
||||||
|
-- that any 'LayoutClass' instance chooses to define.
|
||||||
class Show (layout a) => LayoutClass layout a where
|
class Show (layout a) => LayoutClass layout a where
|
||||||
|
|
||||||
-- | Given a Rectangle in which to place the windows, and a Stack of
|
-- | By default, 'runLayout' calls 'doLayout' if there are any
|
||||||
-- windows, return a list of windows and their corresponding Rectangles.
|
-- windows to be laid out, and 'emptyLayout' otherwise. Most
|
||||||
-- The order of windows in this list should be the desired stacking order.
|
-- instances of 'LayoutClass' probably do not need to implement
|
||||||
-- Also return a modified layout, if this layout needs to be modified
|
-- 'runLayout'; it is only useful for layouts which wish to make
|
||||||
-- (e.g. if we keep track of the windows we have displayed).
|
-- use of more of the 'Workspace' information (for example,
|
||||||
doLayout :: layout a -> Rectangle -> Stack a -> X ([(a, Rectangle)], Maybe (layout a))
|
-- "XMonad.Layout.PerWorkspace").
|
||||||
|
runLayout :: Workspace WorkspaceId (layout a) a
|
||||||
|
-> Rectangle
|
||||||
|
-> X ([(a, Rectangle)], Maybe (layout a))
|
||||||
|
runLayout (Workspace _ l ms) r = maybe (emptyLayout l r) (doLayout l r) ms
|
||||||
|
|
||||||
|
-- | Given a 'Rectangle' in which to place the windows, and a 'Stack'
|
||||||
|
-- of windows, return a list of windows and their corresponding
|
||||||
|
-- Rectangles. If an element is not given a Rectangle by
|
||||||
|
-- 'doLayout', then it is not shown on screen. The order of
|
||||||
|
-- windows in this list should be the desired stacking order.
|
||||||
|
--
|
||||||
|
-- Also possibly return a modified layout (by returning @Just
|
||||||
|
-- newLayout@), if this layout needs to be modified (e.g. if it
|
||||||
|
-- keeps track of some sort of state). Return @Nothing@ if the
|
||||||
|
-- layout does not need to be modified.
|
||||||
|
--
|
||||||
|
-- Layouts which do not need access to the 'X' monad ('IO', window
|
||||||
|
-- manager state, or configuration) and do not keep track of their
|
||||||
|
-- own state should implement 'pureLayout' instead of 'doLayout'.
|
||||||
|
doLayout :: layout a -> Rectangle -> Stack a
|
||||||
|
-> X ([(a, Rectangle)], Maybe (layout a))
|
||||||
doLayout l r s = return (pureLayout l r s, Nothing)
|
doLayout l r s = return (pureLayout l r s, Nothing)
|
||||||
|
|
||||||
-- | This is a pure version of doLayout, for cases where we don't need
|
-- | This is a pure version of 'doLayout', for cases where we
|
||||||
-- access to the X monad to determine how to layout the windows, and
|
-- don't need access to the 'X' monad to determine how to lay out
|
||||||
-- we don't need to modify our layout itself.
|
-- the windows, and we don't need to modify the layout itself.
|
||||||
pureLayout :: layout a -> Rectangle -> Stack a -> [(a, Rectangle)]
|
pureLayout :: layout a -> Rectangle -> Stack a -> [(a, Rectangle)]
|
||||||
pureLayout _ r s = [(focus s, r)]
|
pureLayout _ r s = [(focus s, r)]
|
||||||
|
|
||||||
-- | 'handleMessage' performs message handling for that layout. If
|
-- | 'emptyLayout' is called when there are no windows.
|
||||||
-- 'handleMessage' returns Nothing, then the layout did not respond to
|
emptyLayout :: layout a -> Rectangle -> X ([(a, Rectangle)], Maybe (layout a))
|
||||||
-- that message and the screen is not refreshed. Otherwise, 'handleMessage'
|
emptyLayout _ _ = return ([], Nothing)
|
||||||
-- returns an updated 'Layout' and the screen is refreshed.
|
|
||||||
|
-- | 'handleMessage' performs message handling. If
|
||||||
|
-- 'handleMessage' returns @Nothing@, then the layout did not
|
||||||
|
-- respond to the message and the screen is not refreshed.
|
||||||
|
-- Otherwise, 'handleMessage' returns an updated layout and the
|
||||||
|
-- screen is refreshed.
|
||||||
--
|
--
|
||||||
|
-- Layouts which do not need access to the 'X' monad to decide how
|
||||||
|
-- to handle messages should implement 'pureMessage' instead of
|
||||||
|
-- 'handleMessage' (this restricts the risk of error, and makes
|
||||||
|
-- testing much easier).
|
||||||
handleMessage :: layout a -> SomeMessage -> X (Maybe (layout a))
|
handleMessage :: layout a -> SomeMessage -> X (Maybe (layout a))
|
||||||
handleMessage l = return . pureMessage l
|
handleMessage l = return . pureMessage l
|
||||||
|
|
||||||
-- | Respond to a message by (possibly) changing our layout, but taking
|
-- | Respond to a message by (possibly) changing our layout, but
|
||||||
-- no other action. If the layout changes, the screen will be refreshed.
|
-- taking no other action. If the layout changes, the screen will
|
||||||
|
-- be refreshed.
|
||||||
pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
|
pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
|
||||||
pureMessage _ _ = Nothing
|
pureMessage _ _ = Nothing
|
||||||
|
|
||||||
-- | This should be a human-readable string that is used when selecting
|
-- | This should be a human-readable string that is used when
|
||||||
-- layouts by name.
|
-- selecting layouts by name. The default implementation is
|
||||||
|
-- 'show', which is in some cases a poor default.
|
||||||
description :: layout a -> String
|
description :: layout a -> String
|
||||||
description = show
|
description = show
|
||||||
|
|
||||||
instance LayoutClass Layout Window where
|
instance LayoutClass Layout Window where
|
||||||
|
runLayout (Workspace i (Layout l) ms) r = fmap (fmap Layout) `fmap` runLayout (Workspace i l ms) r
|
||||||
doLayout (Layout l) r s = fmap (fmap Layout) `fmap` doLayout l r s
|
doLayout (Layout l) r s = fmap (fmap Layout) `fmap` doLayout l r s
|
||||||
|
emptyLayout (Layout l) r = fmap (fmap Layout) `fmap` emptyLayout l r
|
||||||
handleMessage (Layout l) = fmap (fmap Layout) . handleMessage l
|
handleMessage (Layout l) = fmap (fmap Layout) . handleMessage l
|
||||||
description (Layout l) = description l
|
description (Layout l) = description l
|
||||||
|
|
||||||
instance Show (Layout a) where show (Layout l) = show l
|
instance Show (Layout a) where show (Layout l) = show l
|
||||||
|
|
||||||
-- | This calls doLayout if there are any windows to be laid out.
|
-- | Based on ideas in /An Extensible Dynamically-Typed Hierarchy of
|
||||||
runLayout :: LayoutClass l a => l a -> Rectangle -> Maybe (Stack a) -> X ([(a, Rectangle)], Maybe (l a))
|
-- Exceptions/, Simon Marlow, 2006. Use extensible messages to the
|
||||||
runLayout l r = maybe (return ([], Nothing)) (doLayout l r)
|
-- 'handleMessage' handler.
|
||||||
|
|
||||||
-- | Based on ideas in /An Extensible Dynamically-Typed Hierarchy of Exceptions/,
|
|
||||||
-- Simon Marlow, 2006. Use extensible messages to the handleMessage handler.
|
|
||||||
--
|
--
|
||||||
-- User-extensible messages must be a member of this class.
|
-- User-extensible messages must be a member of this class.
|
||||||
--
|
--
|
||||||
class Typeable a => Message a
|
class Typeable a => Message a
|
||||||
|
|
||||||
-- |
|
-- |
|
||||||
-- A wrapped value of some type in the Message class.
|
-- A wrapped value of some type in the 'Message' class.
|
||||||
--
|
--
|
||||||
data SomeMessage = forall a. Message a => SomeMessage a
|
data SomeMessage = forall a. Message a => SomeMessage a
|
||||||
|
|
||||||
-- |
|
-- |
|
||||||
-- And now, unwrap a given, unknown Message type, performing a (dynamic)
|
-- And now, unwrap a given, unknown 'Message' type, performing a (dynamic)
|
||||||
-- type check on the result.
|
-- type check on the result.
|
||||||
--
|
--
|
||||||
fromMessage :: Message m => SomeMessage -> Maybe m
|
fromMessage :: Message m => SomeMessage -> Maybe m
|
||||||
fromMessage (SomeMessage m) = cast m
|
fromMessage (SomeMessage m) = cast m
|
||||||
|
|
||||||
-- | X Events are valid Messages
|
-- X Events are valid Messages.
|
||||||
instance Message Event
|
instance Message Event
|
||||||
|
|
||||||
-- | LayoutMessages are core messages that all layouts (especially stateful
|
-- | 'LayoutMessages' are core messages that all layouts (especially stateful
|
||||||
-- layouts) should consider handling.
|
-- layouts) should consider handling.
|
||||||
data LayoutMessages = Hide -- ^ sent when a layout becomes non-visible
|
data LayoutMessages = Hide -- ^ sent when a layout becomes non-visible
|
||||||
| ReleaseResources -- ^ sent when xmonad is exiting or restarting
|
| ReleaseResources -- ^ sent when xmonad is exiting or restarting
|
||||||
@@ -277,40 +344,69 @@ data LayoutMessages = Hide -- ^ sent when a layout becomes non-visi
|
|||||||
|
|
||||||
instance Message LayoutMessages
|
instance Message LayoutMessages
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------------------
|
||||||
|
-- Extensible state
|
||||||
|
--
|
||||||
|
|
||||||
|
-- | Every module must make the data it wants to store
|
||||||
|
-- an instance of this class.
|
||||||
|
--
|
||||||
|
-- Minimal complete definition: initialValue
|
||||||
|
class Typeable a => ExtensionClass a where
|
||||||
|
-- | Defines an initial value for the state extension
|
||||||
|
initialValue :: a
|
||||||
|
-- | Specifies whether the state extension should be
|
||||||
|
-- persistent. Setting this method to 'PersistentExtension'
|
||||||
|
-- will make the stored data survive restarts, but
|
||||||
|
-- requires a to be an instance of Read and Show.
|
||||||
|
--
|
||||||
|
-- It defaults to 'StateExtension', i.e. no persistence.
|
||||||
|
extensionType :: a -> StateExtension
|
||||||
|
extensionType = StateExtension
|
||||||
|
|
||||||
|
-- | Existential type to store a state extension.
|
||||||
|
data StateExtension =
|
||||||
|
forall a. ExtensionClass a => StateExtension a
|
||||||
|
-- ^ Non-persistent state extension
|
||||||
|
| forall a. (Read a, Show a, ExtensionClass a) => PersistentExtension a
|
||||||
|
-- ^ Persistent extension
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
-- | General utilities
|
-- | General utilities
|
||||||
--
|
--
|
||||||
-- Lift an IO action into the X monad
|
-- Lift an 'IO' action into the 'X' monad
|
||||||
io :: MonadIO m => IO a -> m a
|
io :: MonadIO m => IO a -> m a
|
||||||
io = liftIO
|
io = liftIO
|
||||||
|
|
||||||
-- | Lift an IO action into the X monad. If the action results in an IO
|
-- | Lift an 'IO' action into the 'X' monad. If the action results in an 'IO'
|
||||||
-- exception, log the exception to stderr and continue normal execution.
|
-- exception, log the exception to stderr and continue normal execution.
|
||||||
catchIO :: IO () -> X ()
|
catchIO :: MonadIO m => IO () -> m ()
|
||||||
catchIO f = io (f `catch` \e -> hPrint stderr e >> hFlush stderr)
|
catchIO f = io (f `catch` \(SomeException e) -> hPrint stderr e >> hFlush stderr)
|
||||||
|
|
||||||
-- | spawn. Launch an external application
|
-- | spawn. Launch an external application. Specifically, it double-forks and
|
||||||
|
-- runs the 'String' you pass as a command to \/bin\/sh.
|
||||||
|
--
|
||||||
|
-- Note this function assumes your locale uses utf8.
|
||||||
spawn :: MonadIO m => String -> m ()
|
spawn :: MonadIO m => String -> m ()
|
||||||
spawn x = doubleFork $ executeFile "/bin/sh" False ["-c", x] Nothing
|
spawn x = spawnPID x >> return ()
|
||||||
|
|
||||||
-- | Double fork and execute an IO action (usually one of the exec family of
|
-- | Like 'spawn', but returns the 'ProcessID' of the launched application
|
||||||
-- functions)
|
spawnPID :: MonadIO m => String -> m ProcessID
|
||||||
doubleFork :: MonadIO m => IO () -> m ()
|
spawnPID x = xfork $ executeFile "/bin/sh" False ["-c", encodeString x] Nothing
|
||||||
doubleFork m = io $ do
|
|
||||||
pid <- forkProcess $ do
|
|
||||||
forkProcess (createSession >> m)
|
|
||||||
exitWith ExitSuccess
|
|
||||||
getProcessStatus True False pid
|
|
||||||
return ()
|
|
||||||
|
|
||||||
-- | Send a message to all visible layouts, without necessarily refreshing.
|
-- | A replacement for 'forkProcess' which resets default signal handlers.
|
||||||
-- This is how we implement the hooks, such as UnDoLayout.
|
xfork :: MonadIO m => IO () -> m ProcessID
|
||||||
broadcastMessage :: Message a => a -> X ()
|
xfork x = io . forkProcess . finally nullStdin $ do
|
||||||
broadcastMessage a = runOnWorkspaces $ \w -> do
|
uninstallSignalHandlers
|
||||||
ml' <- handleMessage (layout w) (SomeMessage a) `catchX` return Nothing
|
createSession
|
||||||
return $ w { layout = maybe (layout w) id ml' }
|
x
|
||||||
|
where
|
||||||
|
nullStdin = do
|
||||||
|
fd <- openFd "/dev/null" ReadOnly Nothing defaultFileFlags
|
||||||
|
dupTo fd stdInput
|
||||||
|
closeFd fd
|
||||||
|
|
||||||
-- | This is basically a map function, running a function in the X monad on
|
-- | This is basically a map function, running a function in the 'X' monad on
|
||||||
-- each workspace with the output of that function being the modified workspace.
|
-- each workspace with the output of that function being the modified workspace.
|
||||||
runOnWorkspaces :: (WindowSpace -> X WindowSpace) -> X ()
|
runOnWorkspaces :: (WindowSpace -> X WindowSpace) -> X ()
|
||||||
runOnWorkspaces job = do
|
runOnWorkspaces job = do
|
||||||
@@ -320,72 +416,101 @@ runOnWorkspaces job = do
|
|||||||
$ current ws : visible ws
|
$ current ws : visible ws
|
||||||
modify $ \s -> s { windowset = ws { current = c, visible = v, hidden = h } }
|
modify $ \s -> s { windowset = ws { current = c, visible = v, hidden = h } }
|
||||||
|
|
||||||
-- | @restart name resume@. Attempt to restart xmonad by executing the program
|
|
||||||
-- @name@. If @resume@ is 'True', restart with the current window state.
|
|
||||||
-- When executing another window manager, @resume@ should be 'False'.
|
|
||||||
--
|
|
||||||
restart :: String -> Bool -> X ()
|
|
||||||
restart prog resume = do
|
|
||||||
broadcastMessage ReleaseResources
|
|
||||||
io . flush =<< asks display
|
|
||||||
args <- if resume then gets (("--resume":) . return . showWs . windowset) else return []
|
|
||||||
catchIO (executeFile prog True args Nothing)
|
|
||||||
where showWs = show . mapLayout show
|
|
||||||
|
|
||||||
-- | Return the path to @~\/.xmonad@.
|
-- | Return the path to @~\/.xmonad@.
|
||||||
getXMonadDir :: MonadIO m => m String
|
getXMonadDir :: MonadIO m => m String
|
||||||
getXMonadDir = io $ getAppUserDataDirectory "xmonad"
|
getXMonadDir = io $ getAppUserDataDirectory "xmonad"
|
||||||
|
|
||||||
-- | 'recompile force', recompile ~\/.xmonad\/xmonad.hs when any of the
|
-- | 'recompile force', recompile @~\/.xmonad\/xmonad.hs@ when any of the
|
||||||
-- following apply:
|
-- following apply:
|
||||||
-- * force is True
|
|
||||||
-- * the xmonad executable does not exist
|
|
||||||
-- * the xmonad executable is older than xmonad.hs
|
|
||||||
--
|
--
|
||||||
-- The -i flag is used to restrict recompilation to the xmonad.hs file only.
|
-- * force is 'True'
|
||||||
|
--
|
||||||
|
-- * the xmonad executable does not exist
|
||||||
|
--
|
||||||
|
-- * the xmonad executable is older than xmonad.hs or any file in
|
||||||
|
-- ~\/.xmonad\/lib
|
||||||
|
--
|
||||||
|
-- The -i flag is used to restrict recompilation to the xmonad.hs file only,
|
||||||
|
-- and any files in the ~\/.xmonad\/lib directory.
|
||||||
--
|
--
|
||||||
-- Compilation errors (if any) are logged to ~\/.xmonad\/xmonad.errors. If
|
-- Compilation errors (if any) are logged to ~\/.xmonad\/xmonad.errors. If
|
||||||
-- GHC indicates failure with a non-zero exit code, an xmessage displaying
|
-- GHC indicates failure with a non-zero exit code, an xmessage displaying
|
||||||
-- that file is spawned.
|
-- that file is spawned.
|
||||||
--
|
--
|
||||||
-- False is returned if there is compilation errors.
|
-- 'False' is returned if there are compilation errors.
|
||||||
--
|
--
|
||||||
recompile :: MonadIO m => Bool -> m Bool
|
recompile :: MonadIO m => Bool -> m Bool
|
||||||
recompile force = io $ do
|
recompile force = io $ do
|
||||||
dir <- getXMonadDir
|
dir <- getXMonadDir
|
||||||
let binn = "xmonad-"++arch++"-"++os
|
let binn = "xmonad-"++arch++"-"++os
|
||||||
bin = dir ++ "/" ++ binn
|
bin = dir </> binn
|
||||||
base = dir ++ "/" ++ "xmonad"
|
base = dir </> "xmonad"
|
||||||
err = base ++ ".errors"
|
err = base ++ ".errors"
|
||||||
src = base ++ ".hs"
|
src = base ++ ".hs"
|
||||||
|
lib = dir </> "lib"
|
||||||
|
libTs <- mapM getModTime . Prelude.filter isSource =<< allFiles lib
|
||||||
srcT <- getModTime src
|
srcT <- getModTime src
|
||||||
binT <- getModTime bin
|
binT <- getModTime bin
|
||||||
if (force || srcT > binT)
|
if force || any (binT <) (srcT : libTs)
|
||||||
then do
|
then do
|
||||||
status <- bracket (openFile err WriteMode) hClose $ \h -> do
|
-- temporarily disable SIGCHLD ignoring:
|
||||||
waitForProcess =<< runProcess "ghc" ["--make", "xmonad.hs", "-i", "-no-recomp", "-v0", "-o",binn] (Just dir)
|
uninstallSignalHandlers
|
||||||
|
status <- bracket (openFile err WriteMode) hClose $ \h ->
|
||||||
|
waitForProcess =<< runProcess "ghc" ["--make", "xmonad.hs", "-i", "-ilib", "-fforce-recomp", "-v0", "-o",binn] (Just dir)
|
||||||
Nothing Nothing Nothing (Just h)
|
Nothing Nothing Nothing (Just h)
|
||||||
|
|
||||||
|
-- re-enable SIGCHLD:
|
||||||
|
installSignalHandlers
|
||||||
|
|
||||||
-- now, if it fails, run xmessage to let the user know:
|
-- now, if it fails, run xmessage to let the user know:
|
||||||
when (status /= ExitSuccess) $ do
|
when (status /= ExitSuccess) $ do
|
||||||
ghcErr <- readFile err
|
ghcErr <- readFile err
|
||||||
let msg = unlines $
|
let msg = unlines $
|
||||||
["Error detected while loading xmonad configuration file: " ++ src]
|
["Error detected while loading xmonad configuration file: " ++ src]
|
||||||
++ lines ghcErr ++ ["","Please check the file for errors."]
|
++ lines (if null ghcErr then show status else ghcErr)
|
||||||
doubleFork $ executeFile "xmessage" True [msg] Nothing
|
++ ["","Please check the file for errors."]
|
||||||
|
-- nb, the ordering of printing, then forking, is crucial due to
|
||||||
|
-- lazy evaluation
|
||||||
|
hPutStrLn stderr msg
|
||||||
|
forkProcess $ executeFile "xmessage" True ["-default", "okay", msg] Nothing
|
||||||
|
return ()
|
||||||
return (status == ExitSuccess)
|
return (status == ExitSuccess)
|
||||||
else return True
|
else return True
|
||||||
where getModTime f = catch (Just <$> getModificationTime f) (const $ return Nothing)
|
where getModTime f = catch (Just <$> getModificationTime f) (\(SomeException _) -> return Nothing)
|
||||||
|
isSource = flip elem [".hs",".lhs",".hsc"]
|
||||||
|
allFiles t = do
|
||||||
|
let prep = map (t</>) . Prelude.filter (`notElem` [".",".."])
|
||||||
|
cs <- prep <$> catch (getDirectoryContents t) (\(SomeException _) -> return [])
|
||||||
|
ds <- filterM doesDirectoryExist cs
|
||||||
|
concat . ((cs \\ ds):) <$> mapM allFiles ds
|
||||||
|
|
||||||
-- | Run a side effecting action with the current workspace. Like 'when' but
|
-- | Conditionally run an action, using a @Maybe a@ to decide.
|
||||||
whenJust :: Monad m => Maybe a -> (a -> m ()) -> m ()
|
whenJust :: Monad m => Maybe a -> (a -> m ()) -> m ()
|
||||||
whenJust mg f = maybe (return ()) f mg
|
whenJust mg f = maybe (return ()) f mg
|
||||||
|
|
||||||
-- | Conditionally run an action, using a X event to decide
|
-- | Conditionally run an action, using a 'X' event to decide
|
||||||
whenX :: X Bool -> X () -> X ()
|
whenX :: X Bool -> X () -> X ()
|
||||||
whenX a f = a >>= \b -> when b f
|
whenX a f = a >>= \b -> when b f
|
||||||
|
|
||||||
-- | A 'trace' for the X monad. Logs a string to stderr. The result may
|
-- | A 'trace' for the 'X' monad. Logs a string to stderr. The result may
|
||||||
-- be found in your .xsession-errors file
|
-- be found in your .xsession-errors file
|
||||||
trace :: MonadIO m => String -> m ()
|
trace :: MonadIO m => String -> m ()
|
||||||
trace = io . hPutStrLn stderr
|
trace = io . hPutStrLn stderr
|
||||||
|
|
||||||
|
-- | Ignore SIGPIPE to avoid termination when a pipe is full, and SIGCHLD to
|
||||||
|
-- avoid zombie processes, and clean up any extant zombie processes.
|
||||||
|
installSignalHandlers :: MonadIO m => m ()
|
||||||
|
installSignalHandlers = io $ do
|
||||||
|
installHandler openEndedPipe Ignore Nothing
|
||||||
|
installHandler sigCHLD Ignore Nothing
|
||||||
|
(try :: IO a -> IO (Either SomeException a))
|
||||||
|
$ fix $ \more -> do
|
||||||
|
x <- getAnyProcessStatus False False
|
||||||
|
when (isJust x) more
|
||||||
|
return ()
|
||||||
|
|
||||||
|
uninstallSignalHandlers :: MonadIO m => m ()
|
||||||
|
uninstallSignalHandlers = io $ do
|
||||||
|
installHandler openEndedPipe Default Nothing
|
||||||
|
installHandler sigCHLD Default Nothing
|
||||||
|
return ()
|
||||||
|
249
XMonad/Layout.hs
249
XMonad/Layout.hs
@@ -1,5 +1,4 @@
|
|||||||
{-# OPTIONS_GHC -fglasgow-exts #-} -- For deriving Data/Typeable
|
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances, DeriveDataTypeable #-}
|
||||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
|
|
||||||
|
|
||||||
-- --------------------------------------------------------------------------
|
-- --------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
@@ -7,7 +6,7 @@
|
|||||||
-- Copyright : (c) Spencer Janssen 2007
|
-- Copyright : (c) Spencer Janssen 2007
|
||||||
-- License : BSD3-style (see LICENSE)
|
-- License : BSD3-style (see LICENSE)
|
||||||
--
|
--
|
||||||
-- Maintainer : sjanssen@cse.unl.edu
|
-- Maintainer : spencerjanssen@gmail.com
|
||||||
-- Stability : unstable
|
-- Stability : unstable
|
||||||
-- Portability : not portable, Typeable deriving, mtl, posix
|
-- Portability : not portable, Typeable deriving, mtl, posix
|
||||||
--
|
--
|
||||||
@@ -15,9 +14,15 @@
|
|||||||
--
|
--
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
module XMonad.Layout (ChangeLayout(..), Choose, (|||), Resize(..), IncMasterN(..),
|
module XMonad.Layout (
|
||||||
Full(..), Tall(..), Mirror(..), mirrorRect, splitVertically,
|
Full(..), Tall(..), Mirror(..),
|
||||||
splitHorizontally, splitHorizontallyBy, splitVerticallyBy) where
|
Resize(..), IncMasterN(..), Choose, (|||), ChangeLayout(..),
|
||||||
|
mirrorRect, splitVertically,
|
||||||
|
splitHorizontally, splitHorizontallyBy, splitVerticallyBy,
|
||||||
|
|
||||||
|
tile
|
||||||
|
|
||||||
|
) where
|
||||||
|
|
||||||
import XMonad.Core
|
import XMonad.Core
|
||||||
|
|
||||||
@@ -27,128 +32,59 @@ import Control.Arrow ((***), second)
|
|||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Data.Maybe (fromMaybe)
|
import Data.Maybe (fromMaybe)
|
||||||
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
-- LayoutClass selection manager
|
|
||||||
|
|
||||||
-- | A layout that allows users to switch between various layout options.
|
-- | Change the size of the master pane.
|
||||||
|
|
||||||
-- | Messages to change the current layout.
|
|
||||||
data ChangeLayout = FirstLayout | NextLayout deriving (Eq, Show, Typeable)
|
|
||||||
|
|
||||||
instance Message ChangeLayout
|
|
||||||
|
|
||||||
-- | The layout choice combinator
|
|
||||||
(|||) :: (LayoutClass l a, LayoutClass r a) => l a -> r a -> Choose l r a
|
|
||||||
(|||) = flip SLeft
|
|
||||||
infixr 5 |||
|
|
||||||
|
|
||||||
data Choose l r a = SLeft (r a) (l a)
|
|
||||||
| SRight (l a) (r a) deriving (Read, Show)
|
|
||||||
|
|
||||||
data NextNoWrap = NextNoWrap deriving (Eq, Show, Typeable)
|
|
||||||
instance Message NextNoWrap
|
|
||||||
|
|
||||||
-- This has lots of pseudo duplicated code, we must find a better way
|
|
||||||
instance (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a where
|
|
||||||
doLayout (SLeft r l) = (fmap (second . fmap $ SLeft r) .) . doLayout l
|
|
||||||
doLayout (SRight l r) = (fmap (second . fmap $ SRight l) .) . doLayout r
|
|
||||||
|
|
||||||
description (SLeft _ l) = description l
|
|
||||||
description (SRight _ r) = description r
|
|
||||||
|
|
||||||
handleMessage lr m | Just FirstLayout <- fromMessage m = case lr of
|
|
||||||
SLeft {} -> return Nothing
|
|
||||||
SRight l r -> fmap (Just . flip SLeft l . fromMaybe r)
|
|
||||||
$ handleMessage r (SomeMessage Hide)
|
|
||||||
|
|
||||||
handleMessage lr m | Just NextLayout <- fromMessage m = do
|
|
||||||
mlr <- handleMessage lr $ SomeMessage NextNoWrap
|
|
||||||
maybe (handleMessage lr $ SomeMessage FirstLayout) (return . Just) mlr
|
|
||||||
|
|
||||||
handleMessage (SLeft r l) m | Just NextNoWrap <- fromMessage m = do
|
|
||||||
handleMessage l (SomeMessage Hide)
|
|
||||||
mr <- handleMessage r (SomeMessage FirstLayout)
|
|
||||||
return . Just . SRight l $ fromMaybe r mr
|
|
||||||
|
|
||||||
handleMessage lr m | Just ReleaseResources <- fromMessage m =
|
|
||||||
liftM2 ((Just .) . cons)
|
|
||||||
(fmap (fromMaybe l) $ handleMessage l m)
|
|
||||||
(fmap (fromMaybe r) $ handleMessage r m)
|
|
||||||
where (cons, l, r) = case lr of
|
|
||||||
(SLeft r' l') -> (flip SLeft, l', r')
|
|
||||||
(SRight l' r') -> (SRight, l', r')
|
|
||||||
|
|
||||||
-- The default cases for left and right:
|
|
||||||
handleMessage (SLeft r l) m = fmap (fmap $ SLeft r) $ handleMessage l m
|
|
||||||
handleMessage (SRight l r) m = fmap (fmap $ SRight l) $ handleMessage r m
|
|
||||||
|
|
||||||
--
|
|
||||||
-- | Builtin layout algorithms:
|
|
||||||
--
|
|
||||||
-- > fullscreen mode
|
|
||||||
-- > tall mode
|
|
||||||
--
|
|
||||||
-- The latter algorithms support the following operations:
|
|
||||||
--
|
|
||||||
-- > Shrink
|
|
||||||
-- > Expand
|
|
||||||
--
|
|
||||||
data Resize = Shrink | Expand deriving Typeable
|
data Resize = Shrink | Expand deriving Typeable
|
||||||
|
|
||||||
-- | You can also increase the number of clients in the master pane
|
-- | Increase the number of clients in the master pane.
|
||||||
data IncMasterN = IncMasterN Int deriving Typeable
|
data IncMasterN = IncMasterN !Int deriving Typeable
|
||||||
|
|
||||||
instance Message Resize
|
instance Message Resize
|
||||||
instance Message IncMasterN
|
instance Message IncMasterN
|
||||||
|
|
||||||
-- | Simple fullscreen mode, just render all windows fullscreen.
|
-- | Simple fullscreen mode. Renders the focused window fullscreen.
|
||||||
data Full a = Full deriving (Show, Read)
|
data Full a = Full deriving (Show, Read)
|
||||||
|
|
||||||
instance LayoutClass Full a
|
instance LayoutClass Full a
|
||||||
|
|
||||||
-- | The inbuilt tiling mode of xmonad, and its operations.
|
-- | The builtin tiling mode of xmonad. Supports 'Shrink', 'Expand' and
|
||||||
data Tall a = Tall Int Rational Rational deriving (Show, Read)
|
-- 'IncMasterN'.
|
||||||
|
data Tall a = Tall { tallNMaster :: !Int -- ^ The default number of windows in the master pane (default: 1)
|
||||||
|
, tallRatioIncrement :: !Rational -- ^ Percent of screen to increment by when resizing panes (default: 3/100)
|
||||||
|
, tallRatio :: !Rational -- ^ Default proportion of screen occupied by master pane (default: 1/2)
|
||||||
|
}
|
||||||
|
deriving (Show, Read)
|
||||||
|
-- TODO should be capped [0..1] ..
|
||||||
|
|
||||||
|
-- a nice pure layout, lots of properties for the layout, and its messages, in Properties.hs
|
||||||
instance LayoutClass Tall a where
|
instance LayoutClass Tall a where
|
||||||
pureLayout (Tall nmaster _ frac) r s = zip ws rs
|
pureLayout (Tall nmaster _ frac) r s = zip ws rs
|
||||||
where ws = W.integrate s
|
where ws = W.integrate s
|
||||||
rs = tile frac r nmaster (length ws)
|
rs = tile frac r nmaster (length ws)
|
||||||
|
|
||||||
pureMessage (Tall nmaster delta frac) m = msum [fmap resize (fromMessage m)
|
pureMessage (Tall nmaster delta frac) m =
|
||||||
,fmap incmastern (fromMessage m)]
|
msum [fmap resize (fromMessage m)
|
||||||
where resize Shrink = Tall nmaster delta (max 0 $ frac-delta)
|
,fmap incmastern (fromMessage m)]
|
||||||
resize Expand = Tall nmaster delta (min 1 $ frac+delta)
|
|
||||||
incmastern (IncMasterN d) = Tall (max 0 (nmaster+d)) delta frac
|
where resize Shrink = Tall nmaster delta (max 0 $ frac-delta)
|
||||||
|
resize Expand = Tall nmaster delta (min 1 $ frac+delta)
|
||||||
|
incmastern (IncMasterN d) = Tall (max 0 (nmaster+d)) delta frac
|
||||||
|
|
||||||
description _ = "Tall"
|
description _ = "Tall"
|
||||||
|
|
||||||
-- | Mirror a rectangle
|
-- | Compute the positions for windows using the default two-pane tiling
|
||||||
mirrorRect :: Rectangle -> Rectangle
|
-- algorithm.
|
||||||
mirrorRect (Rectangle rx ry rw rh) = (Rectangle ry rx rh rw)
|
|
||||||
|
|
||||||
-- | Mirror a layout, compute its 90 degree rotated form.
|
|
||||||
data Mirror l a = Mirror (l a) deriving (Show, Read)
|
|
||||||
|
|
||||||
instance LayoutClass l a => LayoutClass (Mirror l) a where
|
|
||||||
doLayout (Mirror l) r s = (map (second mirrorRect) *** fmap Mirror)
|
|
||||||
`fmap` doLayout l (mirrorRect r) s
|
|
||||||
handleMessage (Mirror l) = fmap (fmap Mirror) . handleMessage l
|
|
||||||
description (Mirror l) = "Mirror "++ description l
|
|
||||||
|
|
||||||
-- | tile. Compute the positions for windows using the default 2 pane tiling algorithm.
|
|
||||||
--
|
--
|
||||||
-- The screen is divided (currently) into two panes. all clients are
|
-- The screen is divided into two panes. All clients are
|
||||||
-- then partioned between these two panes. one pane, the `master', by
|
-- then partioned between these two panes. One pane, the master, by
|
||||||
-- convention has the least number of windows in it (by default, 1).
|
-- convention has the least number of windows in it.
|
||||||
-- the variable `nmaster' controls how many windows are rendered in the
|
tile
|
||||||
-- master pane.
|
:: Rational -- ^ @frac@, what proportion of the screen to devote to the master area
|
||||||
--
|
-> Rectangle -- ^ @r@, the rectangle representing the screen
|
||||||
-- `delta' specifies the ratio of the screen to resize by.
|
-> Int -- ^ @nmaster@, the number of windows in the master pane
|
||||||
--
|
-> Int -- ^ @n@, the total number of windows to tile
|
||||||
-- 'frac' specifies what proportion of the screen to devote to the
|
-> [Rectangle]
|
||||||
-- master area.
|
|
||||||
--
|
|
||||||
tile :: Rational -> Rectangle -> Int -> Int -> [Rectangle]
|
|
||||||
tile f r nmaster n = if n <= nmaster || nmaster == 0
|
tile f r nmaster n = if n <= nmaster || nmaster == 0
|
||||||
then splitVertically n r
|
then splitVertically n r
|
||||||
else splitVertically nmaster r1 ++ splitVertically (n-nmaster) r2 -- two columns
|
else splitVertically nmaster r1 ++ splitVertically (n-nmaster) r2 -- two columns
|
||||||
@@ -163,6 +99,7 @@ splitVertically n (Rectangle sx sy sw sh) = Rectangle sx sy sw smallh :
|
|||||||
splitVertically (n-1) (Rectangle sx (sy+fromIntegral smallh) sw (sh-smallh))
|
splitVertically (n-1) (Rectangle sx (sy+fromIntegral smallh) sw (sh-smallh))
|
||||||
where smallh = sh `div` fromIntegral n --hmm, this is a fold or map.
|
where smallh = sh `div` fromIntegral n --hmm, this is a fold or map.
|
||||||
|
|
||||||
|
-- Not used in the core, but exported
|
||||||
splitHorizontally n = map mirrorRect . splitVertically n . mirrorRect
|
splitHorizontally n = map mirrorRect . splitVertically n . mirrorRect
|
||||||
|
|
||||||
-- Divide the screen into two rectangles, using a rational to specify the ratio
|
-- Divide the screen into two rectangles, using a rational to specify the ratio
|
||||||
@@ -172,4 +109,102 @@ splitHorizontallyBy f (Rectangle sx sy sw sh) =
|
|||||||
, Rectangle (sx + fromIntegral leftw) sy (sw-fromIntegral leftw) sh)
|
, Rectangle (sx + fromIntegral leftw) sy (sw-fromIntegral leftw) sh)
|
||||||
where leftw = floor $ fromIntegral sw * f
|
where leftw = floor $ fromIntegral sw * f
|
||||||
|
|
||||||
|
-- Not used in the core, but exported
|
||||||
splitVerticallyBy f = (mirrorRect *** mirrorRect) . splitHorizontallyBy f . mirrorRect
|
splitVerticallyBy f = (mirrorRect *** mirrorRect) . splitHorizontallyBy f . mirrorRect
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | Mirror a layout, compute its 90 degree rotated form.
|
||||||
|
newtype Mirror l a = Mirror (l a) deriving (Show, Read)
|
||||||
|
|
||||||
|
instance LayoutClass l a => LayoutClass (Mirror l) a where
|
||||||
|
runLayout (W.Workspace i (Mirror l) ms) r = (map (second mirrorRect) *** fmap Mirror)
|
||||||
|
`fmap` runLayout (W.Workspace i l ms) (mirrorRect r)
|
||||||
|
handleMessage (Mirror l) = fmap (fmap Mirror) . handleMessage l
|
||||||
|
description (Mirror l) = "Mirror "++ description l
|
||||||
|
|
||||||
|
-- | Mirror a rectangle.
|
||||||
|
mirrorRect :: Rectangle -> Rectangle
|
||||||
|
mirrorRect (Rectangle rx ry rw rh) = Rectangle ry rx rh rw
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
-- LayoutClass selection manager
|
||||||
|
-- Layouts that transition between other layouts
|
||||||
|
|
||||||
|
-- | Messages to change the current layout.
|
||||||
|
data ChangeLayout = FirstLayout | NextLayout deriving (Eq, Show, Typeable)
|
||||||
|
|
||||||
|
instance Message ChangeLayout
|
||||||
|
|
||||||
|
-- | The layout choice combinator
|
||||||
|
(|||) :: (LayoutClass l a, LayoutClass r a) => l a -> r a -> Choose l r a
|
||||||
|
(|||) = Choose L
|
||||||
|
infixr 5 |||
|
||||||
|
|
||||||
|
-- | A layout that allows users to switch between various layout options.
|
||||||
|
data Choose l r a = Choose LR (l a) (r a) deriving (Read, Show)
|
||||||
|
|
||||||
|
-- | Are we on the left or right sub-layout?
|
||||||
|
data LR = L | R deriving (Read, Show, Eq)
|
||||||
|
|
||||||
|
data NextNoWrap = NextNoWrap deriving (Eq, Show, Typeable)
|
||||||
|
instance Message NextNoWrap
|
||||||
|
|
||||||
|
-- | A small wrapper around handleMessage, as it is tedious to write
|
||||||
|
-- SomeMessage repeatedly.
|
||||||
|
handle :: (LayoutClass l a, Message m) => l a -> m -> X (Maybe (l a))
|
||||||
|
handle l m = handleMessage l (SomeMessage m)
|
||||||
|
|
||||||
|
-- | A smart constructor that takes some potential modifications, returns a
|
||||||
|
-- new structure if any fields have changed, and performs any necessary cleanup
|
||||||
|
-- on newly non-visible layouts.
|
||||||
|
choose :: (LayoutClass l a, LayoutClass r a)
|
||||||
|
=> Choose l r a-> LR -> Maybe (l a) -> Maybe (r a) -> X (Maybe (Choose l r a))
|
||||||
|
choose (Choose d _ _) d' Nothing Nothing | d == d' = return Nothing
|
||||||
|
choose (Choose d l r) d' ml mr = f lr
|
||||||
|
where
|
||||||
|
(l', r') = (fromMaybe l ml, fromMaybe r mr)
|
||||||
|
lr = case (d, d') of
|
||||||
|
(L, R) -> (hide l' , return r')
|
||||||
|
(R, L) -> (return l', hide r' )
|
||||||
|
(_, _) -> (return l', return r')
|
||||||
|
f (x,y) = fmap Just $ liftM2 (Choose d') x y
|
||||||
|
hide x = fmap (fromMaybe x) $ handle x Hide
|
||||||
|
|
||||||
|
instance (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a where
|
||||||
|
runLayout (W.Workspace i (Choose L l r) ms) =
|
||||||
|
fmap (second . fmap $ flip (Choose L) r) . runLayout (W.Workspace i l ms)
|
||||||
|
runLayout (W.Workspace i (Choose R l r) ms) =
|
||||||
|
fmap (second . fmap $ Choose R l) . runLayout (W.Workspace i r ms)
|
||||||
|
|
||||||
|
description (Choose L l _) = description l
|
||||||
|
description (Choose R _ r) = description r
|
||||||
|
|
||||||
|
handleMessage lr m | Just NextLayout <- fromMessage m = do
|
||||||
|
mlr' <- handle lr NextNoWrap
|
||||||
|
maybe (handle lr FirstLayout) (return . Just) mlr'
|
||||||
|
|
||||||
|
handleMessage c@(Choose d l r) m | Just NextNoWrap <- fromMessage m =
|
||||||
|
case d of
|
||||||
|
L -> do
|
||||||
|
ml <- handle l NextNoWrap
|
||||||
|
case ml of
|
||||||
|
Just _ -> choose c L ml Nothing
|
||||||
|
Nothing -> choose c R Nothing =<< handle r FirstLayout
|
||||||
|
|
||||||
|
R -> choose c R Nothing =<< handle r NextNoWrap
|
||||||
|
|
||||||
|
handleMessage c@(Choose _ l _) m | Just FirstLayout <- fromMessage m =
|
||||||
|
flip (choose c L) Nothing =<< handle l FirstLayout
|
||||||
|
|
||||||
|
handleMessage c@(Choose d l r) m | Just ReleaseResources <- fromMessage m =
|
||||||
|
join $ liftM2 (choose c d) (handle l ReleaseResources) (handle r ReleaseResources)
|
||||||
|
|
||||||
|
handleMessage c@(Choose d l r) m = do
|
||||||
|
ml' <- case d of
|
||||||
|
L -> handleMessage l m
|
||||||
|
R -> return Nothing
|
||||||
|
mr' <- case d of
|
||||||
|
L -> return Nothing
|
||||||
|
R -> handleMessage r m
|
||||||
|
choose c d ml' mr'
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts #-}
|
{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, ForeignFunctionInterface #-}
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Main
|
-- Module : XMonad.Main
|
||||||
-- Copyright : (c) Spencer Janssen 2007
|
-- Copyright : (c) Spencer Janssen 2007
|
||||||
-- License : BSD3-style (see LICENSE)
|
-- License : BSD3-style (see LICENSE)
|
||||||
--
|
--
|
||||||
-- Maintainer : sjanssen@cse.unl.edu
|
-- Maintainer : spencerjanssen@gmail.com
|
||||||
-- Stability : unstable
|
-- Stability : unstable
|
||||||
-- Portability : not portable, uses mtl, X11, posix
|
-- Portability : not portable, uses mtl, X11, posix
|
||||||
--
|
--
|
||||||
@@ -15,12 +15,19 @@
|
|||||||
|
|
||||||
module XMonad.Main (xmonad) where
|
module XMonad.Main (xmonad) where
|
||||||
|
|
||||||
|
import Control.Arrow (second)
|
||||||
import Data.Bits
|
import Data.Bits
|
||||||
|
import Data.List ((\\))
|
||||||
|
import Data.Function
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Control.Monad.State
|
import Control.Monad.State
|
||||||
import Data.Maybe (fromMaybe)
|
import Data.Maybe (fromMaybe)
|
||||||
|
import Data.Monoid (getAll)
|
||||||
|
|
||||||
|
import Foreign.C
|
||||||
|
import Foreign.Ptr
|
||||||
|
|
||||||
import System.Environment (getArgs)
|
import System.Environment (getArgs)
|
||||||
|
|
||||||
@@ -28,33 +35,69 @@ import Graphics.X11.Xlib hiding (refreshKeyboardMapping)
|
|||||||
import Graphics.X11.Xlib.Extras
|
import Graphics.X11.Xlib.Extras
|
||||||
|
|
||||||
import XMonad.Core
|
import XMonad.Core
|
||||||
|
import qualified XMonad.Config as Default
|
||||||
import XMonad.StackSet (new, floating, member)
|
import XMonad.StackSet (new, floating, member)
|
||||||
import qualified XMonad.StackSet as W
|
import qualified XMonad.StackSet as W
|
||||||
import XMonad.Operations
|
import XMonad.Operations
|
||||||
|
|
||||||
import System.IO
|
import System.IO
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
-- Locale support
|
||||||
|
|
||||||
|
#include <locale.h>
|
||||||
|
|
||||||
|
foreign import ccall unsafe "locale.h setlocale"
|
||||||
|
c_setlocale :: CInt -> Ptr CChar -> IO (Ptr CChar)
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
-- |
|
-- |
|
||||||
-- The main entry point
|
-- The main entry point
|
||||||
--
|
--
|
||||||
xmonad :: (LayoutClass l Window, Read (l Window)) => XConfig l -> IO ()
|
xmonad :: (LayoutClass l Window, Read (l Window)) => XConfig l -> IO ()
|
||||||
xmonad initxmc = do
|
xmonad initxmc = do
|
||||||
|
-- setup locale information from environment
|
||||||
|
withCString "" $ c_setlocale (#const LC_ALL)
|
||||||
|
-- ignore SIGPIPE and SIGCHLD
|
||||||
|
installSignalHandlers
|
||||||
-- First, wrap the layout in an existential, to keep things pretty:
|
-- First, wrap the layout in an existential, to keep things pretty:
|
||||||
let xmc = initxmc { layoutHook = Layout $ layoutHook initxmc }
|
let xmc = initxmc { layoutHook = Layout $ layoutHook initxmc }
|
||||||
dpy <- openDisplay ""
|
dpy <- openDisplay ""
|
||||||
let dflt = defaultScreen dpy
|
let dflt = defaultScreen dpy
|
||||||
|
|
||||||
rootw <- rootWindow dpy dflt
|
rootw <- rootWindow dpy dflt
|
||||||
xinesc <- getCleanedScreenInfo dpy
|
|
||||||
nbc <- initColor dpy $ normalBorderColor xmc
|
|
||||||
fbc <- initColor dpy $ focusedBorderColor xmc
|
|
||||||
hSetBuffering stdout NoBuffering
|
|
||||||
args <- getArgs
|
args <- getArgs
|
||||||
|
|
||||||
|
when ("--replace" `elem` args) $ replace dpy dflt rootw
|
||||||
|
|
||||||
|
-- If another WM is running, a BadAccess error will be returned. The
|
||||||
|
-- default error handler will write the exception to stderr and exit with
|
||||||
|
-- an error.
|
||||||
|
selectInput dpy rootw $ substructureRedirectMask .|. substructureNotifyMask
|
||||||
|
.|. enterWindowMask .|. leaveWindowMask .|. structureNotifyMask
|
||||||
|
.|. buttonPressMask
|
||||||
|
sync dpy False -- sync to ensure all outstanding errors are delivered
|
||||||
|
|
||||||
|
-- turn off the default handler in favor of one that ignores all errors
|
||||||
|
-- (ugly, I know)
|
||||||
|
xSetErrorHandler -- in C, I'm too lazy to write the binding: dons
|
||||||
|
|
||||||
|
xinesc <- getCleanedScreenInfo dpy
|
||||||
|
nbc <- do v <- initColor dpy $ normalBorderColor xmc
|
||||||
|
~(Just nbc_) <- initColor dpy $ normalBorderColor Default.defaultConfig
|
||||||
|
return (fromMaybe nbc_ v)
|
||||||
|
|
||||||
|
fbc <- do v <- initColor dpy $ focusedBorderColor xmc
|
||||||
|
~(Just fbc_) <- initColor dpy $ focusedBorderColor Default.defaultConfig
|
||||||
|
return (fromMaybe fbc_ v)
|
||||||
|
|
||||||
|
hSetBuffering stdout NoBuffering
|
||||||
|
|
||||||
let layout = layoutHook xmc
|
let layout = layoutHook xmc
|
||||||
lreads = readsLayout layout
|
lreads = readsLayout layout
|
||||||
initialWinset = new layout (workspaces xmc) $ zipWith SD xinesc gaps
|
initialWinset = new layout (workspaces xmc) $ map SD xinesc
|
||||||
|
|
||||||
maybeRead reads' s = case reads' s of
|
maybeRead reads' s = case reads' s of
|
||||||
[(x, "")] -> Just x
|
[(x, "")] -> Just x
|
||||||
_ -> Nothing
|
_ -> Nothing
|
||||||
@@ -64,8 +107,10 @@ xmonad initxmc = do
|
|||||||
ws <- maybeRead reads s
|
ws <- maybeRead reads s
|
||||||
return . W.ensureTags layout (workspaces xmc)
|
return . W.ensureTags layout (workspaces xmc)
|
||||||
$ W.mapLayout (fromMaybe layout . maybeRead lreads) ws
|
$ W.mapLayout (fromMaybe layout . maybeRead lreads) ws
|
||||||
|
extState = fromMaybe M.empty $ do
|
||||||
gaps = take (length xinesc) $ defaultGaps xmc ++ repeat (0,0,0,0)
|
("--resume" : _ : dyns : _) <- return args
|
||||||
|
vals <- maybeRead reads dyns
|
||||||
|
return . M.fromList . map (second Left) $ vals
|
||||||
|
|
||||||
cf = XConf
|
cf = XConf
|
||||||
{ display = dpy
|
{ display = dpy
|
||||||
@@ -74,45 +119,62 @@ xmonad initxmc = do
|
|||||||
, normalBorder = nbc
|
, normalBorder = nbc
|
||||||
, focusedBorder = fbc
|
, focusedBorder = fbc
|
||||||
, keyActions = keys xmc xmc
|
, keyActions = keys xmc xmc
|
||||||
, buttonActions = mouseBindings xmc xmc }
|
, buttonActions = mouseBindings xmc xmc
|
||||||
|
, mouseFocused = False
|
||||||
|
, mousePosition = Nothing }
|
||||||
|
|
||||||
st = XState
|
st = XState
|
||||||
{ windowset = initialWinset
|
{ windowset = initialWinset
|
||||||
, mapped = S.empty
|
, numberlockMask = 0
|
||||||
, waitingUnmap = M.empty
|
, mapped = S.empty
|
||||||
, dragging = Nothing }
|
, waitingUnmap = M.empty
|
||||||
|
, dragging = Nothing
|
||||||
xSetErrorHandler -- in C, I'm too lazy to write the binding: dons
|
, extensibleState = extState
|
||||||
|
}
|
||||||
-- setup initial X environment
|
|
||||||
sync dpy False
|
|
||||||
selectInput dpy rootw $ substructureRedirectMask .|. substructureNotifyMask
|
|
||||||
.|. enterWindowMask .|. leaveWindowMask .|. structureNotifyMask
|
|
||||||
|
|
||||||
allocaXEvent $ \e ->
|
allocaXEvent $ \e ->
|
||||||
runX cf st $ do
|
runX cf st $ do
|
||||||
|
|
||||||
|
setNumlockMask
|
||||||
grabKeys
|
grabKeys
|
||||||
grabButtons
|
grabButtons
|
||||||
|
|
||||||
io $ sync dpy False
|
io $ sync dpy False
|
||||||
|
|
||||||
|
ws <- io $ scan dpy rootw
|
||||||
|
|
||||||
-- bootstrap the windowset, Operations.windows will identify all
|
-- bootstrap the windowset, Operations.windows will identify all
|
||||||
-- the windows in winset as new and set initial properties for
|
-- the windows in winset as new and set initial properties for
|
||||||
-- those windows
|
-- those windows. Remove all windows that are no longer top-level
|
||||||
windows (const winset)
|
-- children of the root, they may have disappeared since
|
||||||
|
-- restarting.
|
||||||
|
windows . const . foldr W.delete winset $ W.allWindows winset \\ ws
|
||||||
|
|
||||||
-- scan for all top-level windows, add the unmanaged ones to the
|
-- manage the as-yet-unmanaged windows
|
||||||
-- windowset
|
mapM_ manage (ws \\ W.allWindows winset)
|
||||||
ws <- io $ scan dpy rootw
|
|
||||||
mapM_ manage ws
|
userCode $ startupHook initxmc
|
||||||
|
|
||||||
-- main loop, for all you HOF/recursion fans out there.
|
-- main loop, for all you HOF/recursion fans out there.
|
||||||
forever_ $ handle =<< io (nextEvent dpy e >> getEvent e)
|
forever $ prehandle =<< io (nextEvent dpy e >> getEvent e)
|
||||||
|
|
||||||
return ()
|
return ()
|
||||||
where forever_ a = a >> forever_ a
|
where
|
||||||
|
-- if the event gives us the position of the pointer, set mousePosition
|
||||||
|
prehandle e = let mouse = do guard (ev_event_type e `elem` evs)
|
||||||
|
return (fromIntegral (ev_x_root e)
|
||||||
|
,fromIntegral (ev_y_root e))
|
||||||
|
in local (\c -> c { mousePosition = mouse }) (handleWithHook e)
|
||||||
|
evs = [ keyPress, keyRelease, enterNotify, leaveNotify
|
||||||
|
, buttonPress, buttonRelease]
|
||||||
|
|
||||||
|
|
||||||
|
-- | Runs handleEventHook from the configuration and runs the default handler
|
||||||
|
-- function if it returned True.
|
||||||
|
handleWithHook :: Event -> X ()
|
||||||
|
handleWithHook e = do
|
||||||
|
evHook <- asks (handleEventHook . config)
|
||||||
|
whenX (userCodeDef True $ getAll `fmap` evHook e) (handle e)
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
-- | Event handler. Map X events onto calls into Operations.hs, which
|
-- | Event handler. Map X events onto calls into Operations.hs, which
|
||||||
-- modify our internal model of the window manager state.
|
-- modify our internal model of the window manager state.
|
||||||
@@ -131,7 +193,7 @@ handle (KeyEvent {ev_event_type = t, ev_state = m, ev_keycode = code})
|
|||||||
s <- io $ keycodeToKeysym dpy code 0
|
s <- io $ keycodeToKeysym dpy code 0
|
||||||
mClean <- cleanMask m
|
mClean <- cleanMask m
|
||||||
ks <- asks keyActions
|
ks <- asks keyActions
|
||||||
userCode $ whenJust (M.lookup (mClean, s) ks) id
|
userCodeDef () $ whenJust (M.lookup (mClean, s) ks) id
|
||||||
|
|
||||||
-- manage a new window
|
-- manage a new window
|
||||||
handle (MapRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
|
handle (MapRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
|
||||||
@@ -160,7 +222,9 @@ handle (UnmapEvent {ev_window = w, ev_send_event = synthetic}) = whenX (isClient
|
|||||||
-- set keyboard mapping
|
-- set keyboard mapping
|
||||||
handle e@(MappingNotifyEvent {}) = do
|
handle e@(MappingNotifyEvent {}) = do
|
||||||
io $ refreshKeyboardMapping e
|
io $ refreshKeyboardMapping e
|
||||||
when (ev_request e == mappingKeyboard) grabKeys
|
when (ev_request e `elem` [mappingKeyboard, mappingModifier]) $ do
|
||||||
|
setNumlockMask
|
||||||
|
grabKeys
|
||||||
|
|
||||||
-- handle button release, which may finish dragging.
|
-- handle button release, which may finish dragging.
|
||||||
handle e@(ButtonEvent {ev_event_type = t})
|
handle e@(ButtonEvent {ev_event_type = t})
|
||||||
@@ -185,15 +249,16 @@ handle e@(ButtonEvent {ev_window = w,ev_event_type = t,ev_button = b })
|
|||||||
-- grabbed in grabButtons. Otherwise, it's click-to-focus.
|
-- grabbed in grabButtons. Otherwise, it's click-to-focus.
|
||||||
isr <- isRoot w
|
isr <- isRoot w
|
||||||
m <- cleanMask $ ev_state e
|
m <- cleanMask $ ev_state e
|
||||||
ba <- asks buttonActions
|
mact <- asks (M.lookup (m, b) . buttonActions)
|
||||||
if isr then userCode $ whenJust (M.lookup (m, b) ba) ($ ev_subwindow e)
|
case mact of
|
||||||
else focus w
|
(Just act) | isr -> act $ ev_subwindow e
|
||||||
|
_ -> focus w
|
||||||
broadcastMessage e -- Always send button events.
|
broadcastMessage e -- Always send button events.
|
||||||
|
|
||||||
-- entered a normal window, makes this focused.
|
-- entered a normal window: focus it if focusFollowsMouse is set to
|
||||||
|
-- True in the user's config.
|
||||||
handle e@(CrossingEvent {ev_window = w, ev_event_type = t})
|
handle e@(CrossingEvent {ev_window = w, ev_event_type = t})
|
||||||
| t == enterNotify && ev_mode e == notifyNormal
|
| t == enterNotify && ev_mode e == notifyNormal
|
||||||
&& ev_detail e /= notifyInferior
|
|
||||||
= whenX (asks $ focusFollowsMouse . config) (focus w)
|
= whenX (asks $ focusFollowsMouse . config) (focus w)
|
||||||
|
|
||||||
-- left a window, check if we need to focus root
|
-- left a window, check if we need to focus root
|
||||||
@@ -232,8 +297,15 @@ handle e@(ConfigureRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
|
|||||||
handle (ConfigureEvent {ev_window = w}) = whenX (isRoot w) rescreen
|
handle (ConfigureEvent {ev_window = w}) = whenX (isRoot w) rescreen
|
||||||
|
|
||||||
-- property notify
|
-- property notify
|
||||||
handle PropertyEvent { ev_event_type = t, ev_atom = a }
|
handle event@(PropertyEvent { ev_event_type = t, ev_atom = a })
|
||||||
| t == propertyNotify && a == wM_NAME = userCode =<< asks (logHook . config)
|
| t == propertyNotify && a == wM_NAME = asks (logHook . config) >>= userCodeDef () >>
|
||||||
|
broadcastMessage event
|
||||||
|
|
||||||
|
handle e@ClientMessageEvent { ev_message_type = mt } = do
|
||||||
|
a <- getAtom "XMONAD_RESTART"
|
||||||
|
if (mt == a)
|
||||||
|
then restart "xmonad" True
|
||||||
|
else broadcastMessage e
|
||||||
|
|
||||||
handle e = broadcastMessage e -- trace (eventName e) -- ignoring
|
handle e = broadcastMessage e -- trace (eventName e) -- ignoring
|
||||||
|
|
||||||
@@ -259,6 +331,18 @@ scan dpy rootw = do
|
|||||||
return $ not (wa_override_redirect wa)
|
return $ not (wa_override_redirect wa)
|
||||||
&& (wa_map_state wa == waIsViewable || ic)
|
&& (wa_map_state wa == waIsViewable || ic)
|
||||||
|
|
||||||
|
setNumlockMask :: X ()
|
||||||
|
setNumlockMask = do
|
||||||
|
dpy <- asks display
|
||||||
|
ms <- io $ getModifierMapping dpy
|
||||||
|
xs <- sequence [ do
|
||||||
|
ks <- io $ keycodeToKeysym dpy kc 0
|
||||||
|
if ks == xK_Num_Lock
|
||||||
|
then return (setBit 0 (fromIntegral m))
|
||||||
|
else return (0 :: KeyMask)
|
||||||
|
| (m, kcs) <- ms, kc <- kcs, kc /= 0]
|
||||||
|
modify (\s -> s { numberlockMask = foldr (.|.) 0 xs })
|
||||||
|
|
||||||
-- | Grab the keys back
|
-- | Grab the keys back
|
||||||
grabKeys :: X ()
|
grabKeys :: X ()
|
||||||
grabKeys = do
|
grabKeys = do
|
||||||
@@ -270,7 +354,7 @@ grabKeys = do
|
|||||||
kc <- io $ keysymToKeycode dpy sym
|
kc <- io $ keysymToKeycode dpy sym
|
||||||
-- "If the specified KeySym is not defined for any KeyCode,
|
-- "If the specified KeySym is not defined for any KeyCode,
|
||||||
-- XKeysymToKeycode() returns zero."
|
-- XKeysymToKeycode() returns zero."
|
||||||
when (kc /= '\0') $ mapM_ (grab kc . (mask .|.)) =<< extraModifiers
|
when (kc /= 0) $ mapM_ (grab kc . (mask .|.)) =<< extraModifiers
|
||||||
|
|
||||||
-- | XXX comment me
|
-- | XXX comment me
|
||||||
grabButtons :: X ()
|
grabButtons :: X ()
|
||||||
@@ -282,3 +366,36 @@ grabButtons = do
|
|||||||
ems <- extraModifiers
|
ems <- extraModifiers
|
||||||
ba <- asks buttonActions
|
ba <- asks buttonActions
|
||||||
mapM_ (\(m,b) -> mapM_ (grab b . (m .|.)) ems) (M.keys $ ba)
|
mapM_ (\(m,b) -> mapM_ (grab b . (m .|.)) ems) (M.keys $ ba)
|
||||||
|
|
||||||
|
-- | @replace@ to signals compliant window managers to exit.
|
||||||
|
replace :: Display -> ScreenNumber -> Window -> IO ()
|
||||||
|
replace dpy dflt rootw = do
|
||||||
|
-- check for other WM
|
||||||
|
wmSnAtom <- internAtom dpy ("WM_S" ++ show dflt) False
|
||||||
|
currentWmSnOwner <- xGetSelectionOwner dpy wmSnAtom
|
||||||
|
when (currentWmSnOwner /= 0) $ do
|
||||||
|
-- prepare to receive destroyNotify for old WM
|
||||||
|
selectInput dpy currentWmSnOwner structureNotifyMask
|
||||||
|
|
||||||
|
-- create off-screen window
|
||||||
|
netWmSnOwner <- allocaSetWindowAttributes $ \attributes -> do
|
||||||
|
set_override_redirect attributes True
|
||||||
|
set_event_mask attributes propertyChangeMask
|
||||||
|
let screen = defaultScreenOfDisplay dpy
|
||||||
|
visual = defaultVisualOfScreen screen
|
||||||
|
attrmask = cWOverrideRedirect .|. cWEventMask
|
||||||
|
createWindow dpy rootw (-100) (-100) 1 1 0 copyFromParent copyFromParent visual attrmask attributes
|
||||||
|
|
||||||
|
-- try to acquire wmSnAtom, this should signal the old WM to terminate
|
||||||
|
xSetSelectionOwner dpy wmSnAtom netWmSnOwner currentTime
|
||||||
|
|
||||||
|
-- SKIPPED: check if we acquired the selection
|
||||||
|
-- SKIPPED: send client message indicating that we are now the WM
|
||||||
|
|
||||||
|
-- wait for old WM to go away
|
||||||
|
fix $ \again -> do
|
||||||
|
evt <- allocaXEvent $ \event -> do
|
||||||
|
windowEvent dpy currentWmSnOwner structureNotifyMask event
|
||||||
|
get_EventType event
|
||||||
|
|
||||||
|
when (evt /= destroyNotify) again
|
@@ -6,7 +6,7 @@
|
|||||||
-- Copyright : (c) Spencer Janssen 2007
|
-- Copyright : (c) Spencer Janssen 2007
|
||||||
-- License : BSD3-style (see LICENSE)
|
-- License : BSD3-style (see LICENSE)
|
||||||
--
|
--
|
||||||
-- Maintainer : sjanssen@cse.unl.edu
|
-- Maintainer : spencerjanssen@gmail.com
|
||||||
-- Stability : unstable
|
-- Stability : unstable
|
||||||
-- Portability : not portable, uses cunning newtype deriving
|
-- Portability : not portable, uses cunning newtype deriving
|
||||||
--
|
--
|
||||||
@@ -18,55 +18,92 @@
|
|||||||
|
|
||||||
module XMonad.ManageHook where
|
module XMonad.ManageHook where
|
||||||
|
|
||||||
|
import Prelude hiding (catch)
|
||||||
import XMonad.Core
|
import XMonad.Core
|
||||||
import Graphics.X11.Xlib.Extras
|
import Graphics.X11.Xlib.Extras
|
||||||
|
import Graphics.X11.Xlib (Display, Window, internAtom, wM_NAME)
|
||||||
|
import Control.Exception.Extensible (bracket, catch, SomeException(..))
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.Monoid
|
import Data.Monoid
|
||||||
import qualified XMonad.StackSet as W
|
import qualified XMonad.StackSet as W
|
||||||
import XMonad.Operations (floatLocation, reveal)
|
import XMonad.Operations (floatLocation, reveal)
|
||||||
|
|
||||||
|
-- | Lift an 'X' action to a 'Query'.
|
||||||
liftX :: X a -> Query a
|
liftX :: X a -> Query a
|
||||||
liftX = Query . lift
|
liftX = Query . lift
|
||||||
|
|
||||||
-- | The identity hook that returns the WindowSet unchanged.
|
-- | The identity hook that returns the WindowSet unchanged.
|
||||||
idHook :: ManageHook
|
idHook :: Monoid m => m
|
||||||
idHook = doF id
|
idHook = mempty
|
||||||
|
|
||||||
-- | Compose two 'ManageHook's
|
-- | Infix 'mappend'. Compose two 'ManageHook' from right to left.
|
||||||
(<+>) :: ManageHook -> ManageHook -> ManageHook
|
(<+>) :: Monoid m => m -> m -> m
|
||||||
(<+>) = mappend
|
(<+>) = mappend
|
||||||
|
|
||||||
-- | Compose the list of 'ManageHook's
|
-- | Compose the list of 'ManageHook's.
|
||||||
composeAll :: [ManageHook] -> ManageHook
|
composeAll :: Monoid m => [m] -> m
|
||||||
composeAll = mconcat
|
composeAll = mconcat
|
||||||
|
|
||||||
-- | 'p --> x'. If 'p' returns 'True', execute the 'ManageHook'.
|
infix 0 -->
|
||||||
(-->) :: Query Bool -> ManageHook -> ManageHook
|
|
||||||
p --> f = p >>= \b -> if b then f else mempty
|
|
||||||
|
|
||||||
-- | 'q =? x'. if the result of 'q' equals 'x', return 'True'.
|
-- | @p --> x@. If @p@ returns 'True', execute the 'ManageHook'.
|
||||||
|
--
|
||||||
|
-- > (-->) :: Monoid m => Query Bool -> Query m -> Query m -- a simpler type
|
||||||
|
(-->) :: (Monad m, Monoid a) => m Bool -> m a -> m a
|
||||||
|
p --> f = p >>= \b -> if b then f else return mempty
|
||||||
|
|
||||||
|
-- | @q =? x@. if the result of @q@ equals @x@, return 'True'.
|
||||||
(=?) :: Eq a => Query a -> a -> Query Bool
|
(=?) :: Eq a => Query a -> a -> Query Bool
|
||||||
q =? x = fmap (== x) q
|
q =? x = fmap (== x) q
|
||||||
|
|
||||||
infixr 3 <&&>, <||>
|
infixr 3 <&&>, <||>
|
||||||
|
|
||||||
-- | 'p <&&> q'. '&&' lifted to a Monad.
|
-- | '&&' lifted to a 'Monad'.
|
||||||
(<&&>) :: Monad m => m Bool -> m Bool -> m Bool
|
(<&&>) :: Monad m => m Bool -> m Bool -> m Bool
|
||||||
(<&&>) = liftM2 (&&)
|
(<&&>) = liftM2 (&&)
|
||||||
|
|
||||||
-- | 'p <||> q'. '||' lifted to a Monad.
|
-- | '||' lifted to a 'Monad'.
|
||||||
(<||>) :: Monad m => m Bool -> m Bool -> m Bool
|
(<||>) :: Monad m => m Bool -> m Bool -> m Bool
|
||||||
(<||>) = liftM2 (||)
|
(<||>) = liftM2 (||)
|
||||||
|
|
||||||
-- | Queries that return the window title, resource, or class.
|
-- | Return the window title.
|
||||||
title, resource, className :: Query String
|
title :: Query String
|
||||||
title = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap (fromMaybe "") $ io $ fetchName d w)
|
title = ask >>= \w -> liftX $ do
|
||||||
resource = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resName $ io $ getClassHint d w)
|
d <- asks display
|
||||||
className = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resClass $ io $ getClassHint d w)
|
let
|
||||||
|
getProp =
|
||||||
|
(internAtom d "_NET_WM_NAME" False >>= getTextProperty d w)
|
||||||
|
`catch` \(SomeException _) -> getTextProperty d w wM_NAME
|
||||||
|
extract prop = do l <- wcTextPropertyToTextList d prop
|
||||||
|
return $ if null l then "" else head l
|
||||||
|
io $ bracket getProp (xFree . tp_value) extract `catch` \(SomeException _) -> return ""
|
||||||
|
|
||||||
|
-- | Return the application name.
|
||||||
|
appName :: Query String
|
||||||
|
appName = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resName $ io $ getClassHint d w)
|
||||||
|
|
||||||
|
-- | Backwards compatible alias for 'appName'.
|
||||||
|
resource :: Query String
|
||||||
|
resource = appName
|
||||||
|
|
||||||
|
-- | Return the resource class.
|
||||||
|
className :: Query String
|
||||||
|
className = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resClass $ io $ getClassHint d w)
|
||||||
|
|
||||||
|
-- | A query that can return an arbitrary X property of type 'String',
|
||||||
|
-- identified by name.
|
||||||
|
stringProperty :: String -> Query String
|
||||||
|
stringProperty p = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap (fromMaybe "") $ getStringProperty d w p)
|
||||||
|
|
||||||
|
getStringProperty :: Display -> Window -> String -> X (Maybe String)
|
||||||
|
getStringProperty d w p = do
|
||||||
|
a <- getAtom p
|
||||||
|
md <- io $ getWindowProperty8 d a w
|
||||||
|
return $ fmap (map (toEnum . fromIntegral)) md
|
||||||
|
|
||||||
-- | Modify the 'WindowSet' with a pure function.
|
-- | Modify the 'WindowSet' with a pure function.
|
||||||
doF :: (WindowSet -> WindowSet) -> ManageHook
|
doF :: (s -> s) -> Query (Endo s)
|
||||||
doF = return . Endo
|
doF = return . Endo
|
||||||
|
|
||||||
-- | Move the window to the floating layer.
|
-- | Move the window to the floating layer.
|
||||||
@@ -76,3 +113,7 @@ doFloat = ask >>= \w -> doF . W.float w . snd =<< liftX (floatLocation w)
|
|||||||
-- | Map the window and remove it from the 'WindowSet'.
|
-- | Map the window and remove it from the 'WindowSet'.
|
||||||
doIgnore :: ManageHook
|
doIgnore :: ManageHook
|
||||||
doIgnore = ask >>= \w -> liftX (reveal w) >> doF (W.delete w)
|
doIgnore = ask >>= \w -> liftX (reveal w) >> doF (W.delete w)
|
||||||
|
|
||||||
|
-- | Move the window to a given workspace
|
||||||
|
doShift :: WorkspaceId -> ManageHook
|
||||||
|
doShift i = doF . W.shiftWin i =<< ask
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||||
{-# OPTIONS_GHC -fglasgow-exts #-} -- For deriving Data/Typeable
|
|
||||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
|
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
|
||||||
|
|
||||||
-- --------------------------------------------------------------------------
|
-- --------------------------------------------------------------------------
|
||||||
@@ -23,6 +22,7 @@ import XMonad.Layout (Full(..))
|
|||||||
import qualified XMonad.StackSet as W
|
import qualified XMonad.StackSet as W
|
||||||
|
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
|
import Data.Monoid (Endo(..))
|
||||||
import Data.List (nub, (\\), find)
|
import Data.List (nub, (\\), find)
|
||||||
import Data.Bits ((.|.), (.&.), complement)
|
import Data.Bits ((.|.), (.&.), complement)
|
||||||
import Data.Ratio
|
import Data.Ratio
|
||||||
@@ -30,10 +30,11 @@ import qualified Data.Map as M
|
|||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
|
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Control.Monad.State
|
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
|
import Control.Monad.State
|
||||||
|
import qualified Control.Exception.Extensible as C
|
||||||
|
|
||||||
import System.IO
|
import System.Posix.Process (executeFile)
|
||||||
import Graphics.X11.Xlib
|
import Graphics.X11.Xlib
|
||||||
import Graphics.X11.Xinerama (getScreenInfo)
|
import Graphics.X11.Xinerama (getScreenInfo)
|
||||||
import Graphics.X11.Xlib.Extras
|
import Graphics.X11.Xlib.Extras
|
||||||
@@ -54,7 +55,7 @@ manage w = whenX (not <$> isClient w) $ withDisplay $ \d -> do
|
|||||||
let isFixedSize = sh_min_size sh /= Nothing && sh_min_size sh == sh_max_size sh
|
let isFixedSize = sh_min_size sh /= Nothing && sh_min_size sh == sh_max_size sh
|
||||||
isTransient <- isJust <$> io (getTransientForHint d w)
|
isTransient <- isJust <$> io (getTransientForHint d w)
|
||||||
|
|
||||||
(sc, rr) <- floatLocation w
|
rr <- snd `fmap` floatLocation w
|
||||||
-- ensure that float windows don't go over the edge of the screen
|
-- ensure that float windows don't go over the edge of the screen
|
||||||
let adjust (W.RationalRect x y wid h) | x + wid > 1 || y + h > 1 || x < 0 || y < 0
|
let adjust (W.RationalRect x y wid h) | x + wid > 1 || y + h > 1 || x < 0 || y < 0
|
||||||
= W.RationalRect (0.5 - wid/2) (0.5 - h/2) wid h
|
= W.RationalRect (0.5 - wid/2) (0.5 - h/2) wid h
|
||||||
@@ -62,10 +63,10 @@ manage w = whenX (not <$> isClient w) $ withDisplay $ \d -> do
|
|||||||
|
|
||||||
f ws | isFixedSize || isTransient = W.float w (adjust rr) . W.insertUp w . W.view i $ ws
|
f ws | isFixedSize || isTransient = W.float w (adjust rr) . W.insertUp w . W.view i $ ws
|
||||||
| otherwise = W.insertUp w ws
|
| otherwise = W.insertUp w ws
|
||||||
where i = fromMaybe (W.tag . W.workspace . W.current $ ws) $ W.lookupWorkspace sc ws
|
where i = W.tag $ W.workspace $ W.current ws
|
||||||
|
|
||||||
mh <- asks (manageHook . config)
|
mh <- asks (manageHook . config)
|
||||||
g <- runManageHook mh w `catchX` return id
|
g <- appEndo <$> userCodeDef (Endo id) (runQuery mh w)
|
||||||
windows (g . f)
|
windows (g . f)
|
||||||
|
|
||||||
-- | unmanage. A window no longer exists, remove it from the window
|
-- | unmanage. A window no longer exists, remove it from the window
|
||||||
@@ -74,23 +75,14 @@ manage w = whenX (not <$> isClient w) $ withDisplay $ \d -> do
|
|||||||
unmanage :: Window -> X ()
|
unmanage :: Window -> X ()
|
||||||
unmanage = windows . W.delete
|
unmanage = windows . W.delete
|
||||||
|
|
||||||
-- | Modify the size of the status gap at the top of the current screen
|
-- | Kill the specified window. If we do kill it, we'll get a
|
||||||
-- Taking a function giving the current screen, and current geometry.
|
|
||||||
modifyGap :: (Int -> (Int,Int,Int,Int) -> (Int,Int,Int,Int)) -> X ()
|
|
||||||
modifyGap f = do
|
|
||||||
windows $ \ws@(W.StackSet { W.current = c@(W.Screen { W.screenDetail = sd }) }) ->
|
|
||||||
let n = fromIntegral . W.screen $ c
|
|
||||||
g = f n . statusGap $ sd
|
|
||||||
in ws { W.current = c { W.screenDetail = sd { statusGap = g } } }
|
|
||||||
|
|
||||||
-- | Kill the currently focused client. If we do kill it, we'll get a
|
|
||||||
-- delete notify back from X.
|
-- delete notify back from X.
|
||||||
--
|
--
|
||||||
-- There are two ways to delete a window. Either just kill it, or if it
|
-- 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)
|
||||||
--
|
--
|
||||||
kill :: X ()
|
killWindow :: Window -> X ()
|
||||||
kill = withDisplay $ \d -> withFocused $ \w -> do
|
killWindow w = withDisplay $ \d -> do
|
||||||
wmdelt <- atom_WM_DELETE_WINDOW ; wmprot <- atom_WM_PROTOCOLS
|
wmdelt <- atom_WM_DELETE_WINDOW ; wmprot <- atom_WM_PROTOCOLS
|
||||||
|
|
||||||
protocols <- io $ getWMProtocols d w
|
protocols <- io $ getWMProtocols d w
|
||||||
@@ -101,6 +93,10 @@ kill = withDisplay $ \d -> withFocused $ \w -> do
|
|||||||
sendEvent d w False noEventMask ev
|
sendEvent d w False noEventMask ev
|
||||||
else killClient d w >> return ()
|
else killClient d w >> return ()
|
||||||
|
|
||||||
|
-- | Kill the currently focused client.
|
||||||
|
kill :: X ()
|
||||||
|
kill = withFocused killWindow
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
-- Managing windows
|
-- Managing windows
|
||||||
|
|
||||||
@@ -109,70 +105,75 @@ windows :: (WindowSet -> WindowSet) -> X ()
|
|||||||
windows f = do
|
windows f = do
|
||||||
XState { windowset = old } <- get
|
XState { windowset = old } <- get
|
||||||
let oldvisible = concatMap (W.integrate' . W.stack . W.workspace) $ W.current old : W.visible old
|
let oldvisible = concatMap (W.integrate' . W.stack . W.workspace) $ W.current old : W.visible old
|
||||||
|
newwindows = W.allWindows ws \\ W.allWindows old
|
||||||
ws = f old
|
ws = f old
|
||||||
XConf { display = d , normalBorder = nbc, focusedBorder = fbc } <- ask
|
XConf { display = d , normalBorder = nbc, focusedBorder = fbc } <- ask
|
||||||
|
|
||||||
mapM_ setInitialProperties (W.allWindows ws \\ W.allWindows old)
|
mapM_ setInitialProperties newwindows
|
||||||
|
|
||||||
whenJust (W.peek old) $ \otherw -> io $ setWindowBorder d otherw nbc
|
whenJust (W.peek old) $ \otherw -> io $ setWindowBorder d otherw nbc
|
||||||
modify (\s -> s { windowset = ws })
|
modify (\s -> s { windowset = ws })
|
||||||
|
|
||||||
-- notify non visibility
|
-- notify non visibility
|
||||||
let tags_oldvisible = map (W.tag . W.workspace) $ W.current old : W.visible old
|
let tags_oldvisible = map (W.tag . W.workspace) $ W.current old : W.visible old
|
||||||
gottenhidden = filter (`elem` tags_oldvisible) $ map W.tag $ W.hidden ws
|
gottenhidden = filter (flip elem tags_oldvisible . W.tag) $ W.hidden ws
|
||||||
sendMessageToWorkspaces Hide gottenhidden
|
mapM_ (sendMessageWithNoRefresh Hide) gottenhidden
|
||||||
|
|
||||||
-- for each workspace, layout the currently visible workspaces
|
-- for each workspace, layout the currently visible workspaces
|
||||||
let allscreens = W.screens ws
|
let allscreens = W.screens ws
|
||||||
summed_visible = scanl (++) [] $ map (W.integrate' . W.stack . W.workspace) allscreens
|
summed_visible = scanl (++) [] $ map (W.integrate' . W.stack . W.workspace) allscreens
|
||||||
visible <- fmap concat $ forM (zip allscreens summed_visible) $ \ (w, vis) -> do
|
rects <- fmap concat $ forM (zip allscreens summed_visible) $ \ (w, vis) -> do
|
||||||
let n = W.tag (W.workspace w)
|
let wsp = W.workspace w
|
||||||
this = W.view n ws
|
this = W.view n ws
|
||||||
l = W.layout (W.workspace w)
|
n = W.tag wsp
|
||||||
flt = filter (flip M.member (W.floating ws)) (W.index this)
|
|
||||||
tiled = (W.stack . W.workspace . W.current $ this)
|
tiled = (W.stack . W.workspace . W.current $ this)
|
||||||
>>= W.filter (`M.notMember` W.floating ws)
|
>>= W.filter (`M.notMember` W.floating ws)
|
||||||
>>= W.filter (`notElem` vis)
|
>>= W.filter (`notElem` vis)
|
||||||
(SD (Rectangle sx sy sw sh)
|
viewrect = screenRect $ W.screenDetail w
|
||||||
(gt,gb,gl,gr)) = W.screenDetail w
|
|
||||||
viewrect = Rectangle (sx + fromIntegral gl) (sy + fromIntegral gt)
|
|
||||||
(sw - fromIntegral (gl + gr)) (sh - fromIntegral (gt + gb))
|
|
||||||
|
|
||||||
-- just the tiled windows:
|
-- just the tiled windows:
|
||||||
-- now tile the windows on this workspace, modified by the gap
|
-- now tile the windows on this workspace, modified by the gap
|
||||||
(rs, ml') <- runLayout l viewrect tiled `catchX` runLayout (Layout Full) viewrect tiled
|
(rs, ml') <- runLayout wsp { W.stack = tiled } viewrect `catchX`
|
||||||
mapM_ (uncurry tileWindow) rs
|
runLayout wsp { W.stack = tiled, W.layout = Layout Full } viewrect
|
||||||
whenJust ml' $ \l' -> runOnWorkspaces (\ww -> if W.tag ww == n
|
updateLayout n ml'
|
||||||
then return $ ww { W.layout = l'}
|
|
||||||
else return ww)
|
|
||||||
|
|
||||||
-- now the floating windows:
|
let m = W.floating ws
|
||||||
-- move/resize the floating windows, if there are any
|
flt = [(fw, scaleRationalRect viewrect r)
|
||||||
forM_ flt $ \fw -> whenJust (M.lookup fw (W.floating ws)) $
|
| fw <- filter (flip M.member m) (W.index this)
|
||||||
\(W.RationalRect rx ry rw rh) -> do
|
, Just r <- [M.lookup fw m]]
|
||||||
tileWindow fw $ Rectangle
|
vs = flt ++ rs
|
||||||
(sx + floor (toRational sw*rx)) (sy + floor (toRational sh*ry))
|
|
||||||
(floor (toRational sw*rw)) (floor (toRational sh*rh))
|
|
||||||
|
|
||||||
let vs = flt ++ map fst rs
|
io $ restackWindows d (map fst vs)
|
||||||
io $ restackWindows d vs
|
|
||||||
-- return the visible windows for this workspace:
|
-- return the visible windows for this workspace:
|
||||||
return vs
|
return vs
|
||||||
|
|
||||||
|
let visible = map fst rects
|
||||||
|
|
||||||
|
mapM_ (uncurry tileWindow) rects
|
||||||
|
|
||||||
whenJust (W.peek ws) $ \w -> io $ setWindowBorder d w fbc
|
whenJust (W.peek ws) $ \w -> io $ setWindowBorder d w fbc
|
||||||
|
|
||||||
|
mapM_ reveal visible
|
||||||
setTopFocus
|
setTopFocus
|
||||||
asks (logHook . config) >>= userCode
|
|
||||||
|
|
||||||
-- hide every window that was potentially visible before, but is not
|
-- hide every window that was potentially visible before, but is not
|
||||||
-- given a position by a layout now.
|
-- given a position by a layout now.
|
||||||
mapM_ hide (nub oldvisible \\ visible)
|
mapM_ hide (nub (oldvisible ++ newwindows) \\ visible)
|
||||||
|
|
||||||
-- all windows that are no longer in the windowset are marked as
|
-- all windows that are no longer in the windowset are marked as
|
||||||
-- withdrawn, it is important to do this after the above, otherwise 'hide'
|
-- withdrawn, it is important to do this after the above, otherwise 'hide'
|
||||||
-- will overwrite withdrawnState with iconicState
|
-- will overwrite withdrawnState with iconicState
|
||||||
mapM_ (flip setWMState withdrawnState) (W.allWindows old \\ W.allWindows ws)
|
mapM_ (flip setWMState withdrawnState) (W.allWindows old \\ W.allWindows ws)
|
||||||
|
|
||||||
clearEvents enterWindowMask
|
isMouseFocused <- asks mouseFocused
|
||||||
|
unless isMouseFocused $ clearEvents enterWindowMask
|
||||||
|
asks (logHook . config) >>= userCodeDef ()
|
||||||
|
|
||||||
|
-- | Produce the actual rectangle from a screen and a ratio on that screen.
|
||||||
|
scaleRationalRect :: Rectangle -> W.RationalRect -> Rectangle
|
||||||
|
scaleRationalRect (Rectangle sx sy sw sh) (W.RationalRect rx ry rw rh)
|
||||||
|
= Rectangle (sx + scale sw rx) (sy + scale sh ry) (scale sw rw) (scale sh rh)
|
||||||
|
where scale s r = floor (toRational s * r)
|
||||||
|
|
||||||
-- | setWMState. set the WM_STATE property
|
-- | setWMState. set the WM_STATE property
|
||||||
setWMState :: Window -> Int -> X ()
|
setWMState :: Window -> Int -> X ()
|
||||||
@@ -198,7 +199,7 @@ reveal :: Window -> X ()
|
|||||||
reveal w = withDisplay $ \d -> do
|
reveal w = withDisplay $ \d -> do
|
||||||
setWMState w normalState
|
setWMState w normalState
|
||||||
io $ mapWindow d w
|
io $ mapWindow d w
|
||||||
modify (\s -> s { mapped = S.insert w (mapped s) })
|
whenX (isClient w) $ modify (\s -> s { mapped = S.insert w (mapped s) })
|
||||||
|
|
||||||
-- | The client events that xmonad is interested in
|
-- | The client events that xmonad is interested in
|
||||||
clientMask :: EventMask
|
clientMask :: EventMask
|
||||||
@@ -208,7 +209,7 @@ clientMask = structureNotifyMask .|. enterWindowMask .|. propertyChangeMask
|
|||||||
setInitialProperties :: Window -> X ()
|
setInitialProperties :: Window -> X ()
|
||||||
setInitialProperties w = asks normalBorder >>= \nb -> withDisplay $ \d -> do
|
setInitialProperties w = asks normalBorder >>= \nb -> withDisplay $ \d -> do
|
||||||
setWMState w iconicState
|
setWMState w iconicState
|
||||||
io $ selectInput d w $ clientMask
|
io $ selectInput d w clientMask
|
||||||
bw <- asks (borderWidth . config)
|
bw <- asks (borderWidth . config)
|
||||||
io $ setWindowBorderWidth d w bw
|
io $ setWindowBorderWidth d w bw
|
||||||
-- we must initially set the color of new windows, to maintain invariants
|
-- we must initially set the color of new windows, to maintain invariants
|
||||||
@@ -216,7 +217,7 @@ setInitialProperties w = asks normalBorder >>= \nb -> withDisplay $ \d -> do
|
|||||||
io $ setWindowBorder d w nb
|
io $ setWindowBorder d w nb
|
||||||
|
|
||||||
-- | refresh. Render the currently visible workspaces, as determined by
|
-- | refresh. Render the currently visible workspaces, as determined by
|
||||||
-- the StackSet. Also, set focus to the focused window.
|
-- the 'StackSet'. Also, set focus to the focused window.
|
||||||
--
|
--
|
||||||
-- This is our 'view' operation (MVC), in that it pretty prints our model
|
-- This is our 'view' operation (MVC), in that it pretty prints our model
|
||||||
-- with X calls.
|
-- with X calls.
|
||||||
@@ -242,11 +243,10 @@ tileWindow w r = withDisplay $ \d -> do
|
|||||||
| otherwise = x - bw*2
|
| otherwise = x - bw*2
|
||||||
io $ moveResizeWindow d w (rect_x r) (rect_y r)
|
io $ moveResizeWindow d w (rect_x r) (rect_y r)
|
||||||
(least $ rect_width r) (least $ rect_height r)
|
(least $ rect_width r) (least $ rect_height r)
|
||||||
reveal w
|
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
|
|
||||||
-- | Returns True if the first rectangle is contained within, but not equal
|
-- | Returns 'True' if the first rectangle is contained within, but not equal
|
||||||
-- to the second.
|
-- to the second.
|
||||||
containedIn :: Rectangle -> Rectangle -> Bool
|
containedIn :: Rectangle -> Rectangle -> Bool
|
||||||
containedIn r1@(Rectangle x1 y1 w1 h1) r2@(Rectangle x2 y2 w2 h2)
|
containedIn r1@(Rectangle x1 y1 w1 h1) r2@(Rectangle x2 y2 w2 h2)
|
||||||
@@ -274,9 +274,7 @@ rescreen = do
|
|||||||
|
|
||||||
windows $ \ws@(W.StackSet { W.current = v, W.visible = vs, W.hidden = hs }) ->
|
windows $ \ws@(W.StackSet { W.current = v, W.visible = vs, W.hidden = hs }) ->
|
||||||
let (xs, ys) = splitAt (length xinesc) $ map W.workspace (v:vs) ++ hs
|
let (xs, ys) = splitAt (length xinesc) $ map W.workspace (v:vs) ++ hs
|
||||||
(a:as) = zipWith3 W.Screen xs [0..] $ zipWith SD xinesc gs
|
(a:as) = zipWith3 W.Screen xs [0..] $ map SD xinesc
|
||||||
sgs = map (statusGap . W.screenDetail) (v:vs)
|
|
||||||
gs = take (length xinesc) (sgs ++ repeat (0,0,0,0))
|
|
||||||
in ws { W.current = a
|
in ws { W.current = a
|
||||||
, W.visible = as
|
, W.visible = as
|
||||||
, W.hidden = ys }
|
, W.hidden = ys }
|
||||||
@@ -303,9 +301,17 @@ setTopFocus = withWindowSet $ maybe (setFocusX =<< asks theRoot) setFocusX . W.p
|
|||||||
-- This happens if X notices we've moved the mouse (and perhaps moved
|
-- This happens if X notices we've moved the mouse (and perhaps moved
|
||||||
-- the mouse to a new screen).
|
-- the mouse to a new screen).
|
||||||
focus :: Window -> X ()
|
focus :: Window -> X ()
|
||||||
focus w = withWindowSet $ \s -> do
|
focus w = local (\c -> c { mouseFocused = True }) $ withWindowSet $ \s -> do
|
||||||
if W.member w s then when (W.peek s /= Just w) $ windows (W.focusWindow w)
|
let stag = W.tag . W.workspace
|
||||||
else whenX (isRoot w) $ setFocusX w
|
curr = stag $ W.current s
|
||||||
|
mnew <- maybe (return Nothing) (fmap (fmap stag) . uncurry pointScreen)
|
||||||
|
=<< asks mousePosition
|
||||||
|
root <- asks theRoot
|
||||||
|
case () of
|
||||||
|
_ | W.member w s && W.peek s /= Just w -> windows (W.focusWindow w)
|
||||||
|
| Just new <- mnew, w == root && curr /= new
|
||||||
|
-> windows (W.view new)
|
||||||
|
| otherwise -> return ()
|
||||||
|
|
||||||
-- | Call X to set the keyboard focus details.
|
-- | Call X to set the keyboard focus details.
|
||||||
setFocusX :: Window -> X ()
|
setFocusX :: Window -> X ()
|
||||||
@@ -313,36 +319,46 @@ setFocusX w = withWindowSet $ \ws -> do
|
|||||||
dpy <- asks display
|
dpy <- asks display
|
||||||
|
|
||||||
-- clear mouse button grab and border on other windows
|
-- clear mouse button grab and border on other windows
|
||||||
forM_ (W.current ws : W.visible ws) $ \wk -> do
|
forM_ (W.current ws : W.visible ws) $ \wk ->
|
||||||
forM_ (W.index (W.view (W.tag (W.workspace wk)) ws)) $ \otherw -> do
|
forM_ (W.index (W.view (W.tag (W.workspace wk)) ws)) $ \otherw ->
|
||||||
setButtonGrab True otherw
|
setButtonGrab True otherw
|
||||||
|
|
||||||
-- If we ungrab buttons on the root window, we lose our mouse bindings.
|
-- If we ungrab buttons on the root window, we lose our mouse bindings.
|
||||||
whenX (not <$> isRoot w) $ setButtonGrab False w
|
whenX (not <$> isRoot w) $ setButtonGrab False w
|
||||||
io $ do setInputFocus dpy w revertToPointerRoot 0
|
io $ setInputFocus dpy w revertToPointerRoot 0
|
||||||
-- raiseWindow dpy w
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
-- Message handling
|
-- Message handling
|
||||||
|
|
||||||
-- | Throw a message to the current LayoutClass possibly modifying how we
|
-- | Throw a message to the current 'LayoutClass' possibly modifying how we
|
||||||
-- layout the windows, then refresh.
|
-- layout the windows, then refresh.
|
||||||
sendMessage :: Message a => a -> X ()
|
sendMessage :: Message a => a -> X ()
|
||||||
sendMessage a = do
|
sendMessage a = do
|
||||||
w <- W.workspace . W.current <$> gets windowset
|
w <- W.workspace . W.current <$> gets windowset
|
||||||
ml' <- handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing
|
ml' <- handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing
|
||||||
whenJust ml' $ \l' -> do
|
whenJust ml' $ \l' ->
|
||||||
windows $ \ws -> ws { W.current = (W.current ws)
|
windows $ \ws -> ws { W.current = (W.current ws)
|
||||||
{ W.workspace = (W.workspace $ W.current ws)
|
{ W.workspace = (W.workspace $ W.current ws)
|
||||||
{ W.layout = l' }}}
|
{ W.layout = l' }}}
|
||||||
|
|
||||||
-- | Send a message to a list of workspaces' layouts, without necessarily refreshing.
|
-- | Send a message to all layouts, without refreshing.
|
||||||
sendMessageToWorkspaces :: Message a => a -> [WorkspaceId] -> X ()
|
broadcastMessage :: Message a => a -> X ()
|
||||||
sendMessageToWorkspaces a l = runOnWorkspaces $ \w ->
|
broadcastMessage a = withWindowSet $ \ws -> do
|
||||||
if W.tag w `elem` l
|
let c = W.workspace . W.current $ ws
|
||||||
then do ml' <- handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing
|
v = map W.workspace . W.visible $ ws
|
||||||
return $ w { W.layout = maybe (W.layout w) id ml' }
|
h = W.hidden ws
|
||||||
else return w
|
mapM_ (sendMessageWithNoRefresh a) (c : v ++ h)
|
||||||
|
|
||||||
|
-- | Send a message to a layout, without refreshing.
|
||||||
|
sendMessageWithNoRefresh :: Message a => a -> W.Workspace WorkspaceId (Layout Window) Window -> X ()
|
||||||
|
sendMessageWithNoRefresh a w =
|
||||||
|
handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing >>=
|
||||||
|
updateLayout (W.tag w)
|
||||||
|
|
||||||
|
-- | Update the layout field of a workspace
|
||||||
|
updateLayout :: WorkspaceId -> Maybe (Layout Window) -> X ()
|
||||||
|
updateLayout i ml = whenJust ml $ \l ->
|
||||||
|
runOnWorkspaces $ \ww -> return $ if W.tag ww == i then ww { W.layout = l} else ww
|
||||||
|
|
||||||
-- | Set the layout of the currently viewed workspace
|
-- | Set the layout of the currently viewed workspace
|
||||||
setLayout :: Layout Window -> X ()
|
setLayout :: Layout Window -> X ()
|
||||||
@@ -354,15 +370,15 @@ setLayout l = do
|
|||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
-- Utilities
|
-- Utilities
|
||||||
|
|
||||||
-- | Return workspace visible on screen 'sc', or Nothing.
|
-- | Return workspace visible on screen 'sc', or 'Nothing'.
|
||||||
screenWorkspace :: ScreenId -> X (Maybe WorkspaceId)
|
screenWorkspace :: ScreenId -> X (Maybe WorkspaceId)
|
||||||
screenWorkspace sc = withWindowSet $ return . W.lookupWorkspace sc
|
screenWorkspace sc = withWindowSet $ return . W.lookupWorkspace sc
|
||||||
|
|
||||||
-- | Apply an X operation to the currently focused window, if there is one.
|
-- | Apply an 'X' operation to the currently focused window, if there is one.
|
||||||
withFocused :: (Window -> X ()) -> X ()
|
withFocused :: (Window -> X ()) -> X ()
|
||||||
withFocused f = withWindowSet $ \w -> whenJust (W.peek w) f
|
withFocused f = withWindowSet $ \w -> whenJust (W.peek w) f
|
||||||
|
|
||||||
-- | True if window is under management by us
|
-- | 'True' if window is under management by us
|
||||||
isClient :: Window -> X Bool
|
isClient :: Window -> X Bool
|
||||||
isClient w = withWindowSet $ return . W.member w
|
isClient w = withWindowSet $ return . W.member w
|
||||||
|
|
||||||
@@ -370,20 +386,38 @@ isClient w = withWindowSet $ return . W.member w
|
|||||||
-- (numlock and capslock)
|
-- (numlock and capslock)
|
||||||
extraModifiers :: X [KeyMask]
|
extraModifiers :: X [KeyMask]
|
||||||
extraModifiers = do
|
extraModifiers = do
|
||||||
nlm <- asks (numlockMask . config)
|
nlm <- gets numberlockMask
|
||||||
return [0, nlm, lockMask, nlm .|. lockMask ]
|
return [0, nlm, lockMask, nlm .|. lockMask ]
|
||||||
|
|
||||||
-- | Strip numlock\/capslock from a mask
|
-- | Strip numlock\/capslock from a mask
|
||||||
cleanMask :: KeyMask -> X KeyMask
|
cleanMask :: KeyMask -> X KeyMask
|
||||||
cleanMask km = do
|
cleanMask km = do
|
||||||
nlm <- asks (numlockMask . config)
|
nlm <- gets numberlockMask
|
||||||
return (complement (nlm .|. lockMask) .&. km)
|
return (complement (nlm .|. lockMask) .&. km)
|
||||||
|
|
||||||
-- | Get the Pixel value for a named color
|
-- | Get the 'Pixel' value for a named color
|
||||||
initColor :: Display -> String -> IO Pixel
|
initColor :: Display -> String -> IO (Maybe Pixel)
|
||||||
initColor dpy c = (color_pixel . fst) <$> allocNamedColor dpy colormap c
|
initColor dpy c = C.handle (\(C.SomeException _) -> return Nothing) $
|
||||||
|
(Just . color_pixel . fst) <$> allocNamedColor dpy colormap c
|
||||||
where colormap = defaultColormap dpy (defaultScreen dpy)
|
where colormap = defaultColormap dpy (defaultScreen dpy)
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | @restart name resume@. Attempt to restart xmonad by executing the program
|
||||||
|
-- @name@. If @resume@ is 'True', restart with the current window state.
|
||||||
|
-- When executing another window manager, @resume@ should be 'False'.
|
||||||
|
restart :: String -> Bool -> X ()
|
||||||
|
restart prog resume = do
|
||||||
|
broadcastMessage ReleaseResources
|
||||||
|
io . flush =<< asks display
|
||||||
|
let wsData = show . W.mapLayout show . windowset
|
||||||
|
maybeShow (t, Right (PersistentExtension ext)) = Just (t, show ext)
|
||||||
|
maybeShow (t, Left str) = Just (t, str)
|
||||||
|
maybeShow _ = Nothing
|
||||||
|
extState = return . show . catMaybes . map maybeShow . M.toList . extensibleState
|
||||||
|
args <- if resume then gets (\s -> "--resume":wsData s:extState s) else return []
|
||||||
|
catchIO (executeFile prog True args Nothing)
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
-- | Floating layer support
|
-- | Floating layer support
|
||||||
|
|
||||||
@@ -394,31 +428,39 @@ floatLocation w = withDisplay $ \d -> do
|
|||||||
ws <- gets windowset
|
ws <- gets windowset
|
||||||
wa <- io $ getWindowAttributes d w
|
wa <- io $ getWindowAttributes d w
|
||||||
bw <- fi <$> asks (borderWidth . config)
|
bw <- fi <$> asks (borderWidth . config)
|
||||||
|
sc <- fromMaybe (W.current ws) <$> pointScreen (fi $ wa_x wa) (fi $ wa_y wa)
|
||||||
|
|
||||||
-- XXX horrible
|
let sr = screenRect . W.screenDetail $ sc
|
||||||
let sc = fromMaybe (W.current ws) $ find (pointWithin (fi $ wa_x wa) (fi $ wa_y wa) . screenRect . W.screenDetail) $ W.screens ws
|
|
||||||
sr = screenRect . W.screenDetail $ sc
|
|
||||||
rr = W.RationalRect ((fi (wa_x wa) - fi (rect_x sr)) % fi (rect_width sr))
|
rr = W.RationalRect ((fi (wa_x wa) - fi (rect_x sr)) % fi (rect_width sr))
|
||||||
((fi (wa_y wa) - fi (rect_y sr)) % fi (rect_height sr))
|
((fi (wa_y wa) - fi (rect_y sr)) % fi (rect_height sr))
|
||||||
(fi (wa_width wa + bw*2) % fi (rect_width sr))
|
(fi (wa_width wa + bw*2) % fi (rect_width sr))
|
||||||
(fi (wa_height wa + bw*2) % fi (rect_height sr))
|
(fi (wa_height wa + bw*2) % fi (rect_height sr))
|
||||||
|
|
||||||
return (W.screen $ sc, rr)
|
return (W.screen sc, rr)
|
||||||
where fi x = fromIntegral x
|
where fi x = fromIntegral x
|
||||||
pointWithin :: Integer -> Integer -> Rectangle -> Bool
|
|
||||||
pointWithin x y r = x >= fi (rect_x r) &&
|
-- | Given a point, determine the screen (if any) that contains it.
|
||||||
x < fi (rect_x r) + fi (rect_width r) &&
|
pointScreen :: Position -> Position
|
||||||
y >= fi (rect_y r) &&
|
-> X (Maybe (W.Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail))
|
||||||
y < fi (rect_y r) + fi (rect_height r)
|
pointScreen x y = withWindowSet $ return . find p . W.screens
|
||||||
|
where p = pointWithin x y . screenRect . W.screenDetail
|
||||||
|
|
||||||
|
-- | @pointWithin x y r@ returns 'True' if the @(x, y)@ co-ordinate is within
|
||||||
|
-- @r@.
|
||||||
|
pointWithin :: Position -> Position -> Rectangle -> Bool
|
||||||
|
pointWithin x y r = x >= rect_x r &&
|
||||||
|
x < rect_x r + fromIntegral (rect_width r) &&
|
||||||
|
y >= rect_y r &&
|
||||||
|
y < rect_y r + fromIntegral (rect_height r)
|
||||||
|
|
||||||
-- | Make a tiled window floating, using its suggested rectangle
|
-- | Make a tiled window floating, using its suggested rectangle
|
||||||
float :: Window -> X ()
|
float :: Window -> X ()
|
||||||
float w = do
|
float w = do
|
||||||
(sc, rr) <- floatLocation w
|
(sc, rr) <- floatLocation w
|
||||||
windows $ \ws -> W.float w rr . fromMaybe ws $ do
|
windows $ \ws -> W.float w rr . fromMaybe ws $ do
|
||||||
i <- W.findTag w ws
|
i <- W.findTag w ws
|
||||||
guard $ i `elem` map (W.tag . W.workspace) (W.screens ws)
|
guard $ i `elem` map (W.tag . W.workspace) (W.screens ws)
|
||||||
f <- W.peek ws
|
f <- W.peek ws
|
||||||
sw <- W.lookupWorkspace sc ws
|
sw <- W.lookupWorkspace sc ws
|
||||||
return (W.focusWindow f . W.shiftWin sw w $ ws)
|
return (W.focusWindow f . W.shiftWin sw w $ ws)
|
||||||
|
|
||||||
@@ -464,10 +506,10 @@ mouseResizeWindow w = whenX (isClient w) $ withDisplay $ \d -> do
|
|||||||
wa <- io $ getWindowAttributes d w
|
wa <- io $ getWindowAttributes d w
|
||||||
sh <- io $ getWMNormalHints d w
|
sh <- io $ getWMNormalHints d w
|
||||||
io $ warpPointer d none w 0 0 0 0 (fromIntegral (wa_width wa)) (fromIntegral (wa_height wa))
|
io $ warpPointer d none w 0 0 0 0 (fromIntegral (wa_width wa)) (fromIntegral (wa_height wa))
|
||||||
mouseDrag (\ex ey -> do
|
mouseDrag (\ex ey ->
|
||||||
io $ resizeWindow d w `uncurry`
|
io $ resizeWindow d w `uncurry`
|
||||||
applySizeHints sh (ex - fromIntegral (wa_x wa),
|
applySizeHintsContents sh (ex - fromIntegral (wa_x wa),
|
||||||
ey - fromIntegral (wa_y wa)))
|
ey - fromIntegral (wa_y wa)))
|
||||||
(float w)
|
(float w)
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
@@ -475,10 +517,26 @@ mouseResizeWindow w = whenX (isClient w) $ withDisplay $ \d -> do
|
|||||||
|
|
||||||
type D = (Dimension, Dimension)
|
type D = (Dimension, Dimension)
|
||||||
|
|
||||||
|
-- | Given a window, build an adjuster function that will reduce the given
|
||||||
|
-- dimensions according to the window's border width and size hints.
|
||||||
|
mkAdjust :: Window -> X (D -> D)
|
||||||
|
mkAdjust w = withDisplay $ \d -> liftIO $ do
|
||||||
|
sh <- getWMNormalHints d w
|
||||||
|
bw <- fmap (fromIntegral . wa_border_width) $ getWindowAttributes d w
|
||||||
|
return $ applySizeHints bw sh
|
||||||
|
|
||||||
|
-- | Reduce the dimensions if needed to comply to the given SizeHints, taking
|
||||||
|
-- window borders into account.
|
||||||
|
applySizeHints :: Integral a => Dimension -> SizeHints -> (a, a) -> D
|
||||||
|
applySizeHints bw sh =
|
||||||
|
tmap (+ 2 * bw) . applySizeHintsContents sh . tmap (subtract $ 2 * fromIntegral bw)
|
||||||
|
where
|
||||||
|
tmap f (x, y) = (f x, f y)
|
||||||
|
|
||||||
-- | Reduce the dimensions if needed to comply to the given SizeHints.
|
-- | Reduce the dimensions if needed to comply to the given SizeHints.
|
||||||
applySizeHints :: Integral a => SizeHints -> (a,a) -> D
|
applySizeHintsContents :: Integral a => SizeHints -> (a, a) -> D
|
||||||
applySizeHints sh (w,h) = applySizeHints' sh (fromIntegral $ max 1 w,
|
applySizeHintsContents sh (w, h) =
|
||||||
fromIntegral $ max 1 h)
|
applySizeHints' sh (fromIntegral $ max 1 w, fromIntegral $ max 1 h)
|
||||||
|
|
||||||
-- | XXX comment me
|
-- | XXX comment me
|
||||||
applySizeHints' :: SizeHints -> D -> D
|
applySizeHints' :: SizeHints -> D -> D
|
||||||
|
@@ -31,18 +31,18 @@ module XMonad.StackSet (
|
|||||||
-- * Xinerama operations
|
-- * Xinerama operations
|
||||||
-- $xinerama
|
-- $xinerama
|
||||||
lookupWorkspace,
|
lookupWorkspace,
|
||||||
screens, workspaces, allWindows,
|
screens, workspaces, allWindows, currentTag,
|
||||||
-- * Operations on the current stack
|
-- * Operations on the current stack
|
||||||
-- $stackOperations
|
-- $stackOperations
|
||||||
peek, index, integrate, integrate', differentiate,
|
peek, index, integrate, integrate', differentiate,
|
||||||
focusUp, focusDown, focusMaster, focusWindow,
|
focusUp, focusDown, focusUp', focusDown', focusMaster, focusWindow,
|
||||||
tagMember, renameTag, ensureTags, member, findTag, mapWorkspace, mapLayout,
|
tagMember, renameTag, ensureTags, member, findTag, mapWorkspace, mapLayout,
|
||||||
-- * Modifying the stackset
|
-- * Modifying the stackset
|
||||||
-- $modifyStackset
|
-- $modifyStackset
|
||||||
insertUp, delete, delete', filter,
|
insertUp, delete, delete', filter,
|
||||||
-- * Setting the master window
|
-- * Setting the master window
|
||||||
-- $settingMW
|
-- $settingMW
|
||||||
swapUp, swapDown, swapMaster, modify, modify', float, sink, -- needed by users
|
swapUp, swapDown, swapMaster, shiftMaster, modify, modify', float, sink, -- needed by users
|
||||||
-- * Composite operations
|
-- * Composite operations
|
||||||
-- $composite
|
-- $composite
|
||||||
shift, shiftWin,
|
shift, shiftWin,
|
||||||
@@ -52,7 +52,7 @@ module XMonad.StackSet (
|
|||||||
) where
|
) where
|
||||||
|
|
||||||
import Prelude hiding (filter)
|
import Prelude hiding (filter)
|
||||||
import Data.Maybe (listToMaybe,fromJust,isJust)
|
import Data.Maybe (listToMaybe,isJust,fromMaybe)
|
||||||
import qualified Data.List as L (deleteBy,find,splitAt,filter,nub)
|
import qualified Data.List as L (deleteBy,find,splitAt,filter,nub)
|
||||||
import Data.List ( (\\) )
|
import Data.List ( (\\) )
|
||||||
import qualified Data.Map as M (Map,insert,delete,empty)
|
import qualified Data.Map as M (Map,insert,delete,empty)
|
||||||
@@ -111,8 +111,8 @@ import qualified Data.Map as M (Map,insert,delete,empty)
|
|||||||
-- receive keyboard events), other workspaces may be passively
|
-- receive keyboard events), other workspaces may be passively
|
||||||
-- viewable. We thus need to track which virtual workspaces are
|
-- viewable. We thus need to track which virtual workspaces are
|
||||||
-- associated (viewed) on which physical screens. To keep track of
|
-- associated (viewed) on which physical screens. To keep track of
|
||||||
-- this, StackSet keeps separate lists of visible but non-focused
|
-- this, 'StackSet' keeps separate lists of visible but non-focused
|
||||||
-- workspaces, and non-visible workspaces.
|
-- workspaces, and non-visible workspaces.
|
||||||
|
|
||||||
-- $focus
|
-- $focus
|
||||||
--
|
--
|
||||||
@@ -122,38 +122,6 @@ import qualified Data.Map as M (Map,insert,delete,empty)
|
|||||||
-- 'delete'.
|
-- 'delete'.
|
||||||
--
|
--
|
||||||
|
|
||||||
-- |
|
|
||||||
-- API changes from xmonad 0.1:
|
|
||||||
-- StackSet constructor arguments changed. StackSet workspace window screen
|
|
||||||
--
|
|
||||||
-- * new, -- was: empty
|
|
||||||
--
|
|
||||||
-- * view,
|
|
||||||
--
|
|
||||||
-- * index,
|
|
||||||
--
|
|
||||||
-- * peek, -- was: peek\/peekStack
|
|
||||||
--
|
|
||||||
-- * focusUp, focusDown, -- was: rotate
|
|
||||||
--
|
|
||||||
-- * swapUp, swapDown
|
|
||||||
--
|
|
||||||
-- * focus -- was: raiseFocus
|
|
||||||
--
|
|
||||||
-- * insertUp, -- was: insert\/push
|
|
||||||
--
|
|
||||||
-- * delete,
|
|
||||||
--
|
|
||||||
-- * swapMaster, -- was: promote\/swap
|
|
||||||
--
|
|
||||||
-- * member,
|
|
||||||
--
|
|
||||||
-- * shift,
|
|
||||||
--
|
|
||||||
-- * lookupWorkspace, -- was: workspace
|
|
||||||
--
|
|
||||||
-- * visibleWorkspaces -- gone.
|
|
||||||
--
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- A cursor into a non-empty list of workspaces.
|
-- A cursor into a non-empty list of workspaces.
|
||||||
@@ -177,7 +145,7 @@ data Screen i l a sid sd = Screen { workspace :: !(Workspace i l a)
|
|||||||
deriving (Show, Read, Eq)
|
deriving (Show, Read, Eq)
|
||||||
|
|
||||||
-- |
|
-- |
|
||||||
-- A workspace is just a tag - its index - and a stack
|
-- A workspace is just a tag, a layout, and a stack.
|
||||||
--
|
--
|
||||||
data Workspace i l a = Workspace { tag :: !i, layout :: l, stack :: Maybe (Stack a) }
|
data Workspace i l a = Workspace { tag :: !i, layout :: l, stack :: Maybe (Stack a) }
|
||||||
deriving (Show, Read, Eq)
|
deriving (Show, Read, Eq)
|
||||||
@@ -187,7 +155,7 @@ data RationalRect = RationalRect Rational Rational Rational Rational
|
|||||||
deriving (Show, Read, Eq)
|
deriving (Show, Read, Eq)
|
||||||
|
|
||||||
-- |
|
-- |
|
||||||
-- A stack is a cursor onto a (possibly empty) window list.
|
-- A stack is a cursor onto a window list.
|
||||||
-- The data structure tracks focus by construction, and
|
-- The data structure tracks focus by construction, and
|
||||||
-- the master window is by convention the top-most item.
|
-- the master window is by convention the top-most item.
|
||||||
-- Focus operations will not reorder the list that results from
|
-- Focus operations will not reorder the list that results from
|
||||||
@@ -226,7 +194,8 @@ abort x = error $ "xmonad: StackSet: " ++ x
|
|||||||
-- Xinerama: Virtual workspaces are assigned to physical screens, starting at 0.
|
-- Xinerama: Virtual workspaces are assigned to physical screens, starting at 0.
|
||||||
--
|
--
|
||||||
new :: (Integral s) => l -> [i] -> [sd] -> StackSet i l a s sd
|
new :: (Integral s) => l -> [i] -> [sd] -> StackSet i l a s sd
|
||||||
new l wids m | not (null wids) && length m <= length wids = StackSet cur visi unseen M.empty
|
new l wids m | not (null wids) && length m <= length wids && not (null m)
|
||||||
|
= StackSet cur visi unseen M.empty
|
||||||
where (seen,unseen) = L.splitAt (length m) $ map (\i -> Workspace i l Nothing) wids
|
where (seen,unseen) = L.splitAt (length m) $ map (\i -> Workspace i l Nothing) wids
|
||||||
(cur:visi) = [ Screen i s sd | (i, s, sd) <- zip3 seen [0..] m ]
|
(cur:visi) = [ Screen i s sd | (i, s, sd) <- zip3 seen [0..] m ]
|
||||||
-- now zip up visibles with their screen id
|
-- now zip up visibles with their screen id
|
||||||
@@ -234,7 +203,7 @@ new _ _ _ = abort "non-positive argument to StackSet.new"
|
|||||||
|
|
||||||
-- |
|
-- |
|
||||||
-- /O(w)/. Set focus to the workspace with index \'i\'.
|
-- /O(w)/. Set focus to the workspace with index \'i\'.
|
||||||
-- If the index is out of range, return the original StackSet.
|
-- If the index is out of range, return the original 'StackSet'.
|
||||||
--
|
--
|
||||||
-- Xinerama: If the workspace is not visible on any Xinerama screen, it
|
-- Xinerama: If the workspace is not visible on any Xinerama screen, it
|
||||||
-- becomes the current screen. If it is in the visible list, it becomes
|
-- becomes the current screen. If it is in the visible list, it becomes
|
||||||
@@ -242,8 +211,7 @@ new _ _ _ = abort "non-positive argument to StackSet.new"
|
|||||||
|
|
||||||
view :: (Eq s, Eq i) => i -> StackSet i l a s sd -> StackSet i l a s sd
|
view :: (Eq s, Eq i) => i -> StackSet i l a s sd -> StackSet i l a s sd
|
||||||
view i s
|
view i s
|
||||||
| not (i `tagMember` s)
|
| i == currentTag s = s -- current
|
||||||
|| i == tag (workspace (current s)) = s -- out of bounds or current
|
|
||||||
|
|
||||||
| Just x <- L.find ((i==).tag.workspace) (visible s)
|
| Just x <- L.find ((i==).tag.workspace) (visible s)
|
||||||
-- if it is visible, it is just raised
|
-- if it is visible, it is just raised
|
||||||
@@ -254,7 +222,7 @@ view i s
|
|||||||
= s { current = (current s) { workspace = x }
|
= s { current = (current s) { workspace = x }
|
||||||
, hidden = workspace (current s) : L.deleteBy (equating tag) x (hidden s) }
|
, hidden = workspace (current s) : L.deleteBy (equating tag) x (hidden s) }
|
||||||
|
|
||||||
| otherwise = s -- can't happen: all workspaces are either invalid, current, visible, or hidden
|
| otherwise = s -- not a member of the stackset
|
||||||
|
|
||||||
where equating f = \x y -> f x == f y
|
where equating f = \x y -> f x == f y
|
||||||
|
|
||||||
@@ -285,7 +253,7 @@ greedyView w ws
|
|||||||
-- $xinerama
|
-- $xinerama
|
||||||
|
|
||||||
-- | Find the tag of the workspace visible on Xinerama screen 'sc'.
|
-- | Find the tag of the workspace visible on Xinerama screen 'sc'.
|
||||||
-- Nothing if screen is out of bounds.
|
-- 'Nothing' if screen is out of bounds.
|
||||||
lookupWorkspace :: Eq s => s -> StackSet i l a s sd -> Maybe i
|
lookupWorkspace :: Eq s => s -> StackSet i l a s sd -> Maybe i
|
||||||
lookupWorkspace sc w = listToMaybe [ tag i | Screen i s _ <- current w : visible w, s == sc ]
|
lookupWorkspace sc w = listToMaybe [ tag i | Screen i s _ <- current w : visible w, s == sc ]
|
||||||
|
|
||||||
@@ -302,7 +270,7 @@ with :: b -> (Stack a -> b) -> StackSet i l a s sd -> b
|
|||||||
with dflt f = maybe dflt f . stack . workspace . current
|
with dflt f = maybe dflt f . stack . workspace . current
|
||||||
|
|
||||||
-- |
|
-- |
|
||||||
-- Apply a function, and a default value for Nothing, to modify the current stack.
|
-- Apply a function, and a default value for 'Nothing', to modify the current stack.
|
||||||
--
|
--
|
||||||
modify :: Maybe (Stack a) -> (Stack a -> Maybe (Stack a)) -> StackSet i l a s sd -> StackSet i l a s sd
|
modify :: Maybe (Stack a) -> (Stack a -> Maybe (Stack a)) -> StackSet i l a s sd -> StackSet i l a s sd
|
||||||
modify d f s = s { current = (current s)
|
modify d f s = s { current = (current s)
|
||||||
@@ -317,13 +285,13 @@ modify' f = modify Nothing (Just . f)
|
|||||||
|
|
||||||
-- |
|
-- |
|
||||||
-- /O(1)/. Extract the focused element of the current stack.
|
-- /O(1)/. Extract the focused element of the current stack.
|
||||||
-- Return Just that element, or Nothing for an empty stack.
|
-- Return 'Just' that element, or 'Nothing' for an empty stack.
|
||||||
--
|
--
|
||||||
peek :: StackSet i l a s sd -> Maybe a
|
peek :: StackSet i l a s sd -> Maybe a
|
||||||
peek = with Nothing (return . focus)
|
peek = with Nothing (return . focus)
|
||||||
|
|
||||||
-- |
|
-- |
|
||||||
-- /O(n)/. Flatten a Stack into a list.
|
-- /O(n)/. Flatten a 'Stack' into a list.
|
||||||
--
|
--
|
||||||
integrate :: Stack a -> [a]
|
integrate :: Stack a -> [a]
|
||||||
integrate (Stack x l r) = reverse l ++ x : r
|
integrate (Stack x l r) = reverse l ++ x : r
|
||||||
@@ -333,7 +301,7 @@ integrate (Stack x l r) = reverse l ++ x : r
|
|||||||
integrate' :: Maybe (Stack a) -> [a]
|
integrate' :: Maybe (Stack a) -> [a]
|
||||||
integrate' = maybe [] integrate
|
integrate' = maybe [] integrate
|
||||||
|
|
||||||
-- |
|
-- |
|
||||||
-- /O(n)/. Turn a list into a possibly empty stack (i.e., a zipper):
|
-- /O(n)/. Turn a list into a possibly empty stack (i.e., a zipper):
|
||||||
-- the first element of the list is current, and the rest of the list
|
-- the first element of the list is current, and the rest of the list
|
||||||
-- is down.
|
-- is down.
|
||||||
@@ -343,7 +311,7 @@ differentiate (x:xs) = Just $ Stack x [] xs
|
|||||||
|
|
||||||
-- |
|
-- |
|
||||||
-- /O(n)/. 'filter p s' returns the elements of 's' such that 'p' evaluates to
|
-- /O(n)/. 'filter p s' returns the elements of 's' such that 'p' evaluates to
|
||||||
-- True. Order is preserved, and focus moves as described for 'delete'.
|
-- 'True'. Order is preserved, and focus moves as described for 'delete'.
|
||||||
--
|
--
|
||||||
filter :: (a -> Bool) -> Stack a -> Maybe (Stack a)
|
filter :: (a -> Bool) -> Stack a -> Maybe (Stack a)
|
||||||
filter p (Stack f ls rs) = case L.filter p (f:rs) of
|
filter p (Stack f ls rs) = case L.filter p (f:rs) of
|
||||||
@@ -375,15 +343,19 @@ index = with [] integrate
|
|||||||
--
|
--
|
||||||
focusUp, focusDown, swapUp, swapDown :: StackSet i l a s sd -> StackSet i l a s sd
|
focusUp, focusDown, swapUp, swapDown :: StackSet i l a s sd -> StackSet i l a s sd
|
||||||
focusUp = modify' focusUp'
|
focusUp = modify' focusUp'
|
||||||
focusDown = modify' (reverseStack . focusUp' . reverseStack)
|
focusDown = modify' focusDown'
|
||||||
|
|
||||||
swapUp = modify' swapUp'
|
swapUp = modify' swapUp'
|
||||||
swapDown = modify' (reverseStack . swapUp' . reverseStack)
|
swapDown = modify' (reverseStack . swapUp' . reverseStack)
|
||||||
|
|
||||||
focusUp', swapUp' :: Stack a -> Stack a
|
-- | Variants of 'focusUp' and 'focusDown' that work on a
|
||||||
|
-- 'Stack' rather than an entire 'StackSet'.
|
||||||
|
focusUp', focusDown' :: Stack a -> Stack a
|
||||||
focusUp' (Stack t (l:ls) rs) = Stack l ls (t:rs)
|
focusUp' (Stack t (l:ls) rs) = Stack l ls (t:rs)
|
||||||
focusUp' (Stack t [] rs) = Stack x xs [] where (x:xs) = reverse (t:rs)
|
focusUp' (Stack t [] rs) = Stack x xs [] where (x:xs) = reverse (t:rs)
|
||||||
|
focusDown' = reverseStack . focusUp' . reverseStack
|
||||||
|
|
||||||
|
swapUp' :: Stack a -> Stack a
|
||||||
swapUp' (Stack t (l:ls) rs) = Stack t ls (l:rs)
|
swapUp' (Stack t (l:ls) rs) = Stack t ls (l:rs)
|
||||||
swapUp' (Stack t [] rs) = Stack t (reverse rs) []
|
swapUp' (Stack t [] rs) = Stack t (reverse rs) []
|
||||||
|
|
||||||
@@ -397,27 +369,31 @@ reverseStack (Stack t ls rs) = Stack t rs ls
|
|||||||
--
|
--
|
||||||
focusWindow :: (Eq s, Eq a, Eq i) => a -> StackSet i l a s sd -> StackSet i l a s sd
|
focusWindow :: (Eq s, Eq a, Eq i) => a -> StackSet i l a s sd -> StackSet i l a s sd
|
||||||
focusWindow w s | Just w == peek s = s
|
focusWindow w s | Just w == peek s = s
|
||||||
| otherwise = maybe s id $ do
|
| otherwise = fromMaybe s $ do
|
||||||
n <- findTag w s
|
n <- findTag w s
|
||||||
return $ until ((Just w ==) . peek) focusUp (view n s)
|
return $ until ((Just w ==) . peek) focusUp (view n s)
|
||||||
|
|
||||||
-- | Get a list of all screens in the StackSet.
|
-- | Get a list of all screens in the 'StackSet'.
|
||||||
screens :: StackSet i l a s sd -> [Screen i l a s sd]
|
screens :: StackSet i l a s sd -> [Screen i l a s sd]
|
||||||
screens s = current s : visible s
|
screens s = current s : visible s
|
||||||
|
|
||||||
-- | Get a list of all workspaces in the StackSet.
|
-- | Get a list of all workspaces in the 'StackSet'.
|
||||||
workspaces :: StackSet i l a s sd -> [Workspace i l a]
|
workspaces :: StackSet i l a s sd -> [Workspace i l a]
|
||||||
workspaces s = workspace (current s) : map workspace (visible s) ++ hidden s
|
workspaces s = workspace (current s) : map workspace (visible s) ++ hidden s
|
||||||
|
|
||||||
-- | Get a list of all windows in the StackSet in no particular order
|
-- | Get a list of all windows in the 'StackSet' in no particular order
|
||||||
allWindows :: Eq a => StackSet i l a s sd -> [a]
|
allWindows :: Eq a => StackSet i l a s sd -> [a]
|
||||||
allWindows = L.nub . concatMap (integrate' . stack) . workspaces
|
allWindows = L.nub . concatMap (integrate' . stack) . workspaces
|
||||||
|
|
||||||
-- | Is the given tag present in the StackSet?
|
-- | Get the tag of the currently focused workspace.
|
||||||
|
currentTag :: StackSet i l a s sd -> i
|
||||||
|
currentTag = tag . workspace . current
|
||||||
|
|
||||||
|
-- | Is the given tag present in the 'StackSet'?
|
||||||
tagMember :: Eq i => i -> StackSet i l a s sd -> Bool
|
tagMember :: Eq i => i -> StackSet i l a s sd -> Bool
|
||||||
tagMember t = elem t . map tag . workspaces
|
tagMember t = elem t . map tag . workspaces
|
||||||
|
|
||||||
-- | Rename a given tag if present in the StackSet.
|
-- | Rename a given tag if present in the 'StackSet'.
|
||||||
renameTag :: Eq i => i -> i -> StackSet i l a s sd -> StackSet i l a s sd
|
renameTag :: Eq i => i -> i -> StackSet i l a s sd -> StackSet i l a s sd
|
||||||
renameTag o n = mapWorkspace rename
|
renameTag o n = mapWorkspace rename
|
||||||
where rename w = if tag w == o then w { tag = n } else w
|
where rename w = if tag w == o then w { tag = n } else w
|
||||||
@@ -432,27 +408,27 @@ ensureTags l allt st = et allt (map tag (workspaces st) \\ allt) st
|
|||||||
et (i:is) [] s = et is [] (s { hidden = Workspace i l Nothing : hidden s })
|
et (i:is) [] s = et is [] (s { hidden = Workspace i l Nothing : hidden s })
|
||||||
et (i:is) (r:rs) s = et is rs $ renameTag r i s
|
et (i:is) (r:rs) s = et is rs $ renameTag r i s
|
||||||
|
|
||||||
-- | Map a function on all the workspaces in the StackSet.
|
-- | Map a function on all the workspaces in the 'StackSet'.
|
||||||
mapWorkspace :: (Workspace i l a -> Workspace i l a) -> StackSet i l a s sd -> StackSet i l a s sd
|
mapWorkspace :: (Workspace i l a -> Workspace i l a) -> StackSet i l a s sd -> StackSet i l a s sd
|
||||||
mapWorkspace f s = s { current = updScr (current s)
|
mapWorkspace f s = s { current = updScr (current s)
|
||||||
, visible = map updScr (visible s)
|
, visible = map updScr (visible s)
|
||||||
, hidden = map f (hidden s) }
|
, hidden = map f (hidden s) }
|
||||||
where updScr scr = scr { workspace = f (workspace scr) }
|
where updScr scr = scr { workspace = f (workspace scr) }
|
||||||
|
|
||||||
-- | Map a function on all the layouts in the StackSet.
|
-- | Map a function on all the layouts in the 'StackSet'.
|
||||||
mapLayout :: (l -> l') -> StackSet i l a s sd -> StackSet i l' a s sd
|
mapLayout :: (l -> l') -> StackSet i l a s sd -> StackSet i l' a s sd
|
||||||
mapLayout f (StackSet v vs hs m) = StackSet (fScreen v) (map fScreen vs) (map fWorkspace hs) m
|
mapLayout f (StackSet v vs hs m) = StackSet (fScreen v) (map fScreen vs) (map fWorkspace hs) m
|
||||||
where
|
where
|
||||||
fScreen (Screen ws s sd) = Screen (fWorkspace ws) s sd
|
fScreen (Screen ws s sd) = Screen (fWorkspace ws) s sd
|
||||||
fWorkspace (Workspace t l s) = Workspace t (f l) s
|
fWorkspace (Workspace t l s) = Workspace t (f l) s
|
||||||
|
|
||||||
-- | /O(n)/. Is a window in the StackSet.
|
-- | /O(n)/. Is a window in the 'StackSet'?
|
||||||
member :: Eq a => a -> StackSet i l a s sd -> Bool
|
member :: Eq a => a -> StackSet i l a s sd -> Bool
|
||||||
member a s = isJust (findTag a s)
|
member a s = isJust (findTag a s)
|
||||||
|
|
||||||
-- | /O(1) on current window, O(n) in general/.
|
-- | /O(1) on current window, O(n) in general/.
|
||||||
-- Return Just the workspace tag of the given window, or Nothing
|
-- Return 'Just' the workspace tag of the given window, or 'Nothing'
|
||||||
-- if the window is not in the StackSet.
|
-- if the window is not in the 'StackSet'.
|
||||||
findTag :: Eq a => a -> StackSet i l a s sd -> Maybe i
|
findTag :: Eq a => a -> StackSet i l a s sd -> Maybe i
|
||||||
findTag a s = listToMaybe
|
findTag a s = listToMaybe
|
||||||
[ tag w | w <- workspaces s, has a (stack w) ]
|
[ tag w | w <- workspaces s, has a (stack w) ]
|
||||||
@@ -487,21 +463,25 @@ insertUp a s = if member a s then s else insert
|
|||||||
-- /O(1) on current window, O(n) in general/. Delete window 'w' if it exists.
|
-- /O(1) on current window, O(n) in general/. Delete window 'w' if it exists.
|
||||||
-- There are 4 cases to consider:
|
-- There are 4 cases to consider:
|
||||||
--
|
--
|
||||||
-- * delete on an Nothing workspace leaves it Nothing
|
-- * delete on an 'Nothing' workspace leaves it Nothing
|
||||||
|
--
|
||||||
-- * otherwise, try to move focus to the down
|
-- * otherwise, try to move focus to the down
|
||||||
|
--
|
||||||
-- * otherwise, try to move focus to the up
|
-- * otherwise, try to move focus to the up
|
||||||
-- * otherwise, you've got an empty workspace, becomes Nothing
|
--
|
||||||
|
-- * otherwise, you've got an empty workspace, becomes 'Nothing'
|
||||||
--
|
--
|
||||||
-- Behaviour with respect to the master:
|
-- Behaviour with respect to the master:
|
||||||
--
|
--
|
||||||
-- * deleting the master window resets it to the newly focused window
|
-- * deleting the master window resets it to the newly focused window
|
||||||
|
--
|
||||||
-- * otherwise, delete doesn't affect the master.
|
-- * otherwise, delete doesn't affect the master.
|
||||||
--
|
--
|
||||||
delete :: (Ord a, Eq s) => a -> StackSet i l a s sd -> StackSet i l a s sd
|
delete :: (Ord a, Eq s) => a -> StackSet i l a s sd -> StackSet i l a s sd
|
||||||
delete w = sink w . delete' w
|
delete w = sink w . delete' w
|
||||||
|
|
||||||
-- | Only temporarily remove the window from the stack, thereby not destroying special
|
-- | Only temporarily remove the window from the stack, thereby not destroying special
|
||||||
-- information saved in the Stackset
|
-- information saved in the 'Stackset'
|
||||||
delete' :: (Eq a, Eq s) => a -> StackSet i l a s sd -> StackSet i l a s sd
|
delete' :: (Eq a, Eq s) => a -> StackSet i l a s sd -> StackSet i l a s sd
|
||||||
delete' w s = s { current = removeFromScreen (current s)
|
delete' w s = s { current = removeFromScreen (current s)
|
||||||
, visible = map removeFromScreen (visible s)
|
, visible = map removeFromScreen (visible s)
|
||||||
@@ -512,7 +492,7 @@ delete' w s = s { current = removeFromScreen (current s)
|
|||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
-- | Given a window, and its preferred rectangle, set it as floating
|
-- | Given a window, and its preferred rectangle, set it as floating
|
||||||
-- A floating window should already be managed by the StackSet.
|
-- A floating window should already be managed by the 'StackSet'.
|
||||||
float :: Ord a => a -> RationalRect -> StackSet i l a s sd -> StackSet i l a s sd
|
float :: Ord a => a -> RationalRect -> StackSet i l a s sd -> StackSet i l a s sd
|
||||||
float w r s = s { floating = M.insert w r (floating s) }
|
float w r s = s { floating = M.insert w r (floating s) }
|
||||||
|
|
||||||
@@ -533,6 +513,15 @@ swapMaster = modify' $ \c -> case c of
|
|||||||
|
|
||||||
-- natural! keep focus, move current to the top, move top to current.
|
-- natural! keep focus, move current to the top, move top to current.
|
||||||
|
|
||||||
|
-- | /O(s)/. Set the master window to the focused window.
|
||||||
|
-- The other windows are kept in order and shifted down on the stack, as if you
|
||||||
|
-- just hit mod-shift-k a bunch of times.
|
||||||
|
-- Focus stays with the item moved.
|
||||||
|
shiftMaster :: StackSet i l a s sd -> StackSet i l a s sd
|
||||||
|
shiftMaster = modify' $ \c -> case c of
|
||||||
|
Stack _ [] _ -> c -- already master.
|
||||||
|
Stack t ls rs -> Stack t [] (reverse ls ++ rs)
|
||||||
|
|
||||||
-- | /O(s)/. Set focus to the master window.
|
-- | /O(s)/. Set focus to the master window.
|
||||||
focusMaster :: StackSet i l a s sd -> StackSet i l a s sd
|
focusMaster :: StackSet i l a s sd -> StackSet i l a s sd
|
||||||
focusMaster = modify' $ \c -> case c of
|
focusMaster = modify' $ \c -> case c of
|
||||||
@@ -550,10 +539,7 @@ focusMaster = modify' $ \c -> case c of
|
|||||||
-- element on the current stack, the original stackSet is returned.
|
-- element on the current stack, the original stackSet is returned.
|
||||||
--
|
--
|
||||||
shift :: (Ord a, Eq s, Eq i) => i -> StackSet i l a s sd -> StackSet i l a s sd
|
shift :: (Ord a, Eq s, Eq i) => i -> StackSet i l a s sd -> StackSet i l a s sd
|
||||||
shift n s | n `tagMember` s && n /= curtag = maybe s go (peek s)
|
shift n s = maybe s (\w -> shiftWin n w s) (peek s)
|
||||||
| otherwise = s
|
|
||||||
where go w = view curtag . insertUp w . view n . delete' w $ s
|
|
||||||
curtag = tag (workspace (current s))
|
|
||||||
|
|
||||||
-- | /O(n)/. shiftWin. Searches for the specified window 'w' on all workspaces
|
-- | /O(n)/. shiftWin. Searches for the specified window 'w' on all workspaces
|
||||||
-- of the stackSet and moves it to stack 'n', leaving it as the focused
|
-- of the stackSet and moves it to stack 'n', leaving it as the focused
|
||||||
@@ -561,14 +547,12 @@ shift n s | n `tagMember` s && n /= curtag = maybe s go (peek s)
|
|||||||
-- focused element on that workspace.
|
-- focused element on that workspace.
|
||||||
-- The actual focused workspace doesn't change. If the window is not
|
-- The actual focused workspace doesn't change. If the window is not
|
||||||
-- found in the stackSet, the original stackSet is returned.
|
-- found in the stackSet, the original stackSet is returned.
|
||||||
-- TODO how does this duplicate 'shift's behaviour?
|
|
||||||
shiftWin :: (Ord a, Eq a, Eq s, Eq i) => i -> a -> StackSet i l a s sd -> StackSet i l a s sd
|
shiftWin :: (Ord a, Eq a, Eq s, Eq i) => i -> a -> StackSet i l a s sd -> StackSet i l a s sd
|
||||||
shiftWin n w s | from == Nothing = s -- not found
|
shiftWin n w s = case findTag w s of
|
||||||
| n `tagMember` s && (Just n) /= from = go
|
Just from | n `tagMember` s && n /= from -> go from s
|
||||||
| otherwise = s
|
_ -> s
|
||||||
where from = findTag w s
|
where go from = onWorkspace n (insertUp w) . onWorkspace from (delete' w)
|
||||||
|
|
||||||
go = on n (insertUp w) . on (fromJust from) (delete' w) $ s
|
|
||||||
curtag = tag (workspace (current s))
|
|
||||||
on i f = view curtag . f . view i
|
|
||||||
|
|
||||||
|
onWorkspace :: (Eq i, Eq s) => i -> (StackSet i l a s sd -> StackSet i l a s sd)
|
||||||
|
-> (StackSet i l a s sd -> StackSet i l a s sd)
|
||||||
|
onWorkspace n f s = view (currentTag s) . f . view n $ s
|
||||||
|
68
man/HCAR.tex
Normal file
68
man/HCAR.tex
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
% xmonad-Gx.tex
|
||||||
|
\begin{hcarentry}{xmonad}
|
||||||
|
\label{xmonad}
|
||||||
|
\report{Gwern Branwen}%05/10
|
||||||
|
\status{active development}
|
||||||
|
\makeheader
|
||||||
|
|
||||||
|
XMonad is a tiling window manager for X. Windows are arranged
|
||||||
|
automatically to tile the screen without gaps or overlap, maximizing
|
||||||
|
screen use. Window manager features are accessible from the keyboard; a
|
||||||
|
mouse is optional. XMonad is written, configured, and extensible in
|
||||||
|
Haskell. Custom layout algorithms, key bindings, and other extensions may
|
||||||
|
be written by the user in config files. Layouts are applied
|
||||||
|
dynamically, and different layouts may be used on each workspace.
|
||||||
|
Xinerama is fully supported, allowing windows to be tiled on several
|
||||||
|
physical screens.
|
||||||
|
|
||||||
|
Development since the last report has continued apace, with versions
|
||||||
|
0.8, 0.8.1, 0.9 and 0.9.1 released, with simultaneous releases of the
|
||||||
|
XMonadContrib library of customizations and extensions, which has now
|
||||||
|
grown to no less than 205 modules encompassing a dizzying array of features.
|
||||||
|
|
||||||
|
Details of changes between releases can be found in the release notes:
|
||||||
|
\begin{compactitem}
|
||||||
|
\item \url{http://haskell.org/haskellwiki/Xmonad/Notable_changes_since_0.7}
|
||||||
|
\item \url{http://haskell.org/haskellwiki/Xmonad/Notable_changes_since_0.8}
|
||||||
|
\item \url{http://haskell.org/haskellwiki/Xmonad/Notable_changes_since_0.9}
|
||||||
|
\item XMonad.Config.PlainConfig allows writing configs in a more 'normal' style, and not raw Haskell
|
||||||
|
\item Supports using local modules in xmonad.hs; for example: to use definitions from \~/.xmonad/lib/XMonad/Stack/MyAdditions.hs
|
||||||
|
\item xmonad --restart CLI option
|
||||||
|
\item xmonad --replace CLI option
|
||||||
|
\item XMonad.Prompt now has customizable keymaps
|
||||||
|
\item Actions.GridSelect - a GUI menu for selecting windows or workspaces \& substring search on window names
|
||||||
|
\item Actions.OnScreen
|
||||||
|
\item Extensions now can have state
|
||||||
|
\item Actions.SpawnOn - uses state to spawn applications on the workspace the user was originally on,
|
||||||
|
and not where the user happens to be
|
||||||
|
\item Markdown manpages and not man/troff
|
||||||
|
\item XMonad.Layout.ImageButtonDecoration \&\\ XMonad.Util.Image
|
||||||
|
\item XMonad.Layout.Groups
|
||||||
|
\item XMonad.Layout.ZoomRow
|
||||||
|
\item XMonad.Layout.Renamed
|
||||||
|
\item XMonad.Layout.Drawer
|
||||||
|
\item XMonad.Layout.FullScreen
|
||||||
|
\item XMonad.Hooks.ScreenCorners
|
||||||
|
\item XMonad.Actions.DynamicWorkspaceOrder
|
||||||
|
\item XMonad.Actions.WorkspaceNames
|
||||||
|
\item XMonad.Actions.DynamicWorkspaceGroups
|
||||||
|
\end{compactitem}
|
||||||
|
|
||||||
|
Binary packages of XMonad and XMonadContrib are available for all major Linux distributions.
|
||||||
|
|
||||||
|
\FurtherReading
|
||||||
|
\begin{compactitem}
|
||||||
|
\item Homepage:
|
||||||
|
\url{http://xmonad.org/}
|
||||||
|
|
||||||
|
\item Darcs source:
|
||||||
|
|
||||||
|
\texttt{darcs get} \url{http://code.haskell.org/xmonad}
|
||||||
|
|
||||||
|
\item IRC channel:
|
||||||
|
\verb+#xmonad @@ irc.freenode.org+
|
||||||
|
|
||||||
|
\item Mailing list:
|
||||||
|
\email{xmonad@@haskell.org}
|
||||||
|
\end{compactitem}
|
||||||
|
\end{hcarentry}
|
@@ -1,43 +0,0 @@
|
|||||||
./" man page created by David Lazar on April 24, 2007
|
|
||||||
./" uses ``tmac.an'' macro set
|
|
||||||
.TH xmonad 1 "18 April 07" xmonad\-1.0 "xmonad manual"
|
|
||||||
.SH NAME
|
|
||||||
xmonad \- a tiling window manager
|
|
||||||
.SH DESCRIPTION
|
|
||||||
.PP
|
|
||||||
\fBxmonad\fR is a minimalist tiling window manager for X, written in Haskell. Windows are managed using automatic layout algorithms, which can be dynamically reconfigured. At any time windows are arranged so as to maximise the use of screen real estate. All features of the window manager are accessible purely from the keyboard: a mouse is entirely optional. \fBxmonad\fR is configured in Haskell, and custom layout algorithms may be implemented by the user in config files. A principle of \fBxmonad\fR is predictability: the user should know in advance precisely the window arrangement that will result from any action.
|
|
||||||
.PP
|
|
||||||
By default, \fBxmonad\fR provides three layout algorithms: tall, wide and fullscreen. In tall or wide mode, windows are tiled and arranged to prevent overlap and maximise screen use. Sets of windows are grouped together on virtual screens, and each screen retains its own layout, which may be reconfigured dynamically. Multiple physical monitors are supported via Xinerama, allowing simultaneous display of a number of screens.
|
|
||||||
.PP
|
|
||||||
By utilising the expressivity of a modern functional language with a rich static type system, \fBxmonad\fR provides a complete, featureful window manager in less than 500 lines of code, with an emphasis on correctness and robustness. Internal properties of the window manager are checked using a combination of static guarantees provided by the type system, and type-based automated testing. A benefit of this is that the code is simple to understand, and easy to modify.
|
|
||||||
.SH USAGE
|
|
||||||
.PP
|
|
||||||
\fBxmonad\fR places each window into a "workspace". Each workspace can have any number of windows, which you can cycle though with mod-j and mod-k. Windows are either displayed full screen, tiled horizontally, or tiled vertically. You can toggle the layout mode with mod-space, which will cycle through the available modes.
|
|
||||||
.PP
|
|
||||||
You can switch to workspace N with mod-N. For example, to switch to workspace 5, you would press mod-5. Similarly, you can move the current window to another workspace with mod-shift-N.
|
|
||||||
.PP
|
|
||||||
When running with multiple monitors (Xinerama), each screen has exactly 1 workspace visible. mod-{w,e,r} switch the focus between screens, while shift-mod-{w,e,r} move the current window to that screen. When \fBxmonad\fR starts, workspace 1 is on screen 1, workspace 2 is on screen 2, etc. When switching workspaces to one that is already visible, the current and visible workspaces are swapped.
|
|
||||||
.PP
|
|
||||||
.SS Flags
|
|
||||||
\fBxmonad\fR has several flags which you may pass to the executable. These flags are:
|
|
||||||
.TP
|
|
||||||
\fB--recompile
|
|
||||||
Recompiles your configuration in ~/.xmonad/xmonad.hs if it is newer than your already existing ~/.xmonad/xmonad executable.
|
|
||||||
.TP
|
|
||||||
\fB--recompile-force
|
|
||||||
Unconditionally recompiles your configuration in ~/.xmonad/xmonad.hs.
|
|
||||||
.TP
|
|
||||||
\fB--version
|
|
||||||
Display version of \fBxmonad\fR.
|
|
||||||
.SS Default keyboard bindings
|
|
||||||
___KEYBINDINGS___
|
|
||||||
.SH EXAMPLES
|
|
||||||
To use \fBxmonad\fR as your window manager add:
|
|
||||||
.RS
|
|
||||||
xmonad
|
|
||||||
.RE
|
|
||||||
to your \fI~/.xinitrc\fR file
|
|
||||||
.SH CUSTOMIZATION
|
|
||||||
\fBxmonad\fR is customized in ~/.xmonad/xmonad.hs, and then restarting with mod-q.
|
|
||||||
.SH BUGS
|
|
||||||
Probably. If you find any, please report them: http://code.google.com/p/xmonad/issues/list
|
|
102
man/xmonad.1.markdown
Normal file
102
man/xmonad.1.markdown
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#Name
|
||||||
|
xmonad - a tiling window manager
|
||||||
|
|
||||||
|
#Description
|
||||||
|
|
||||||
|
_xmonad_ is a minimalist tiling window manager for X, written in Haskell.
|
||||||
|
Windows are managed using automatic layout algorithms, which can be
|
||||||
|
dynamically reconfigured. At any time windows are arranged so as to
|
||||||
|
maximize the use of screen real estate. All features of the window manager
|
||||||
|
are accessible purely from the keyboard: a mouse is entirely optional.
|
||||||
|
_xmonad_ is configured in Haskell, and custom layout algorithms may be
|
||||||
|
implemented by the user in config files. A principle of _xmonad_ is
|
||||||
|
predictability: the user should know in advance precisely the window
|
||||||
|
arrangement that will result from any action.
|
||||||
|
|
||||||
|
By default, _xmonad_ provides three layout algorithms: tall, wide and
|
||||||
|
fullscreen. In tall or wide mode, windows are tiled and arranged to prevent
|
||||||
|
overlap and maximize screen use. Sets of windows are grouped together on
|
||||||
|
virtual screens, and each screen retains its own layout, which may be
|
||||||
|
reconfigured dynamically. Multiple physical monitors are supported via
|
||||||
|
Xinerama, allowing simultaneous display of a number of screens.
|
||||||
|
|
||||||
|
By utilizing the expressivity of a modern functional language with a rich
|
||||||
|
static type system, _xmonad_ provides a complete, featureful window manager
|
||||||
|
in less than 1200 lines of code, with an emphasis on correctness and
|
||||||
|
robustness. Internal properties of the window manager are checked using a
|
||||||
|
combination of static guarantees provided by the type system, and
|
||||||
|
type-based automated testing. A benefit of this is that the code is simple
|
||||||
|
to understand, and easy to modify.
|
||||||
|
|
||||||
|
#Usage
|
||||||
|
|
||||||
|
_xmonad_ places each window into a "workspace". Each workspace can have
|
||||||
|
any number of windows, which you can cycle though with mod-j and mod-k.
|
||||||
|
Windows are either displayed full screen, tiled horizontally, or tiled
|
||||||
|
vertically. You can toggle the layout mode with mod-space, which will cycle
|
||||||
|
through the available modes.
|
||||||
|
|
||||||
|
You can switch to workspace N with mod-N. For example, to switch to
|
||||||
|
workspace 5, you would press mod-5. Similarly, you can move the current
|
||||||
|
window to another workspace with mod-shift-N.
|
||||||
|
|
||||||
|
When running with multiple monitors (Xinerama), each screen has exactly 1
|
||||||
|
workspace visible. mod-{w,e,r} switch the focus between screens, while
|
||||||
|
shift-mod-{w,e,r} move the current window to that screen. When _xmonad_
|
||||||
|
starts, workspace 1 is on screen 1, workspace 2 is on screen 2, etc. When
|
||||||
|
switching workspaces to one that is already visible, the current and
|
||||||
|
visible workspaces are swapped.
|
||||||
|
|
||||||
|
##Flags
|
||||||
|
xmonad has several flags which you may pass to the executable.
|
||||||
|
These flags are:
|
||||||
|
|
||||||
|
--recompile
|
||||||
|
: Recompiles your configuration in _~/.xmonad/xmonad.hs_
|
||||||
|
|
||||||
|
--restart
|
||||||
|
: Causes the currently running _xmonad_ process to restart
|
||||||
|
|
||||||
|
--replace
|
||||||
|
: Replace the current window manager with xmonad
|
||||||
|
|
||||||
|
--version
|
||||||
|
: Display version of _xmonad_
|
||||||
|
|
||||||
|
--verbose-version
|
||||||
|
: Display detailed version of _xmonad_
|
||||||
|
|
||||||
|
##Default keyboard bindings
|
||||||
|
|
||||||
|
___KEYBINDINGS___
|
||||||
|
|
||||||
|
#Examples
|
||||||
|
To use xmonad as your window manager add to your _~/.xinitrc_ file:
|
||||||
|
|
||||||
|
> exec xmonad
|
||||||
|
|
||||||
|
#Customization
|
||||||
|
xmonad is customized in ~/.xmonad/xmonad.hs, and then restarting
|
||||||
|
with mod-q.
|
||||||
|
|
||||||
|
You can find many extensions to the core feature set in the xmonad-
|
||||||
|
contrib package, available through your package manager or from
|
||||||
|
[xmonad.org].
|
||||||
|
|
||||||
|
##Modular Configuration
|
||||||
|
As of _xmonad-0.9_, any additional Haskell modules may be placed in
|
||||||
|
_~/.xmonad/lib/_ are available in GHC's searchpath. Hierarchical modules
|
||||||
|
are supported: for example, the file
|
||||||
|
_~/.xmonad/lib/XMonad/Stack/MyAdditions.hs_ could contain:
|
||||||
|
|
||||||
|
> module XMonad.Stack.MyAdditions (function1) where
|
||||||
|
> function1 = error "function1: Not implemented yet!"
|
||||||
|
|
||||||
|
Your xmonad.hs may then import XMonad.Stack.MyAdditions as if that
|
||||||
|
module was contained within xmonad or xmonad-contrib.
|
||||||
|
|
||||||
|
#Bugs
|
||||||
|
Probably. If you find any, please report them to the [bugtracker]
|
||||||
|
|
||||||
|
[xmonad.org]: http://xmonad.org
|
||||||
|
[bugtracker]: http://code.google.com/p/xmonad/issues/list
|
141
man/xmonad.hs
141
man/xmonad.hs
@@ -8,6 +8,7 @@
|
|||||||
--
|
--
|
||||||
|
|
||||||
import XMonad
|
import XMonad
|
||||||
|
import Data.Monoid
|
||||||
import System.Exit
|
import System.Exit
|
||||||
|
|
||||||
import qualified XMonad.StackSet as W
|
import qualified XMonad.StackSet as W
|
||||||
@@ -18,6 +19,10 @@ import qualified Data.Map as M
|
|||||||
--
|
--
|
||||||
myTerminal = "xterm"
|
myTerminal = "xterm"
|
||||||
|
|
||||||
|
-- Whether focus follows the mouse pointer.
|
||||||
|
myFocusFollowsMouse :: Bool
|
||||||
|
myFocusFollowsMouse = True
|
||||||
|
|
||||||
-- Width of the window border in pixels.
|
-- Width of the window border in pixels.
|
||||||
--
|
--
|
||||||
myBorderWidth = 1
|
myBorderWidth = 1
|
||||||
@@ -29,21 +34,6 @@ myBorderWidth = 1
|
|||||||
--
|
--
|
||||||
myModMask = mod1Mask
|
myModMask = mod1Mask
|
||||||
|
|
||||||
-- The mask for the numlock key. Numlock status is "masked" from the
|
|
||||||
-- current modifier status, so the keybindings will work with numlock on or
|
|
||||||
-- off. You may need to change this on some systems.
|
|
||||||
--
|
|
||||||
-- You can find the numlock modifier by running "xmodmap" and looking for a
|
|
||||||
-- modifier with Num_Lock bound to it:
|
|
||||||
--
|
|
||||||
-- > $ xmodmap | grep Num
|
|
||||||
-- > mod2 Num_Lock (0x4d)
|
|
||||||
--
|
|
||||||
-- Set numlockMask = 0 if you don't have a numlock key, or want to treat
|
|
||||||
-- numlock status separately.
|
|
||||||
--
|
|
||||||
myNumlockMask = mod2Mask
|
|
||||||
|
|
||||||
-- The default number of workspaces (virtual screens) and their names.
|
-- The default number of workspaces (virtual screens) and their names.
|
||||||
-- By default we use numeric strings, but any string may be used as a
|
-- By default we use numeric strings, but any string may be used as a
|
||||||
-- workspace name. The number of workspaces is determined by the length
|
-- workspace name. The number of workspaces is determined by the length
|
||||||
@@ -60,92 +50,79 @@ myWorkspaces = ["1","2","3","4","5","6","7","8","9"]
|
|||||||
myNormalBorderColor = "#dddddd"
|
myNormalBorderColor = "#dddddd"
|
||||||
myFocusedBorderColor = "#ff0000"
|
myFocusedBorderColor = "#ff0000"
|
||||||
|
|
||||||
-- Default offset of drawable screen boundaries from each physical
|
|
||||||
-- screen. Anything non-zero here will leave a gap of that many pixels
|
|
||||||
-- on the given edge, on the that screen. A useful gap at top of screen
|
|
||||||
-- for a menu bar (e.g. 15)
|
|
||||||
--
|
|
||||||
-- An example, to set a top gap on monitor 1, and a gap on the bottom of
|
|
||||||
-- monitor 2, you'd use a list of geometries like so:
|
|
||||||
--
|
|
||||||
-- > defaultGaps = [(18,0,0,0),(0,18,0,0)] -- 2 gaps on 2 monitors
|
|
||||||
--
|
|
||||||
-- Fields are: top, bottom, left, right.
|
|
||||||
--
|
|
||||||
myDefaultGaps = [(0,0,0,0)]
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
-- Key bindings. Add, modify or remove key bindings here.
|
-- Key bindings. Add, modify or remove key bindings here.
|
||||||
--
|
--
|
||||||
myKeys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
myKeys conf@(XConfig {XMonad.modMask = modm}) = M.fromList $
|
||||||
|
|
||||||
-- launch a terminal
|
-- launch a terminal
|
||||||
[ ((modMask .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf)
|
[ ((modm .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf)
|
||||||
|
|
||||||
-- launch dmenu
|
-- launch dmenu
|
||||||
, ((modMask, xK_p ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"")
|
, ((modm, xK_p ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"")
|
||||||
|
|
||||||
-- launch gmrun
|
-- launch gmrun
|
||||||
, ((modMask .|. shiftMask, xK_p ), spawn "gmrun")
|
, ((modm .|. shiftMask, xK_p ), spawn "gmrun")
|
||||||
|
|
||||||
-- close focused window
|
-- close focused window
|
||||||
, ((modMask .|. shiftMask, xK_c ), kill)
|
, ((modm .|. shiftMask, xK_c ), kill)
|
||||||
|
|
||||||
-- Rotate through the available layout algorithms
|
-- Rotate through the available layout algorithms
|
||||||
, ((modMask, xK_space ), sendMessage NextLayout)
|
, ((modm, xK_space ), sendMessage NextLayout)
|
||||||
|
|
||||||
-- Reset the layouts on the current workspace to default
|
-- Reset the layouts on the current workspace to default
|
||||||
, ((modMask .|. shiftMask, xK_space ), setLayout $ XMonad.layoutHook conf)
|
, ((modm .|. shiftMask, xK_space ), setLayout $ XMonad.layoutHook conf)
|
||||||
|
|
||||||
-- Resize viewed windows to the correct size
|
-- Resize viewed windows to the correct size
|
||||||
, ((modMask, xK_n ), refresh)
|
, ((modm, xK_n ), refresh)
|
||||||
|
|
||||||
-- Move focus to the next window
|
-- Move focus to the next window
|
||||||
, ((modMask, xK_Tab ), windows W.focusDown)
|
, ((modm, xK_Tab ), windows W.focusDown)
|
||||||
|
|
||||||
-- Move focus to the next window
|
-- Move focus to the next window
|
||||||
, ((modMask, xK_j ), windows W.focusDown)
|
, ((modm, xK_j ), windows W.focusDown)
|
||||||
|
|
||||||
-- Move focus to the previous window
|
-- Move focus to the previous window
|
||||||
, ((modMask, xK_k ), windows W.focusUp )
|
, ((modm, xK_k ), windows W.focusUp )
|
||||||
|
|
||||||
-- Move focus to the master window
|
-- Move focus to the master window
|
||||||
, ((modMask, xK_m ), windows W.focusMaster )
|
, ((modm, xK_m ), windows W.focusMaster )
|
||||||
|
|
||||||
-- Swap the focused window and the master window
|
-- Swap the focused window and the master window
|
||||||
, ((modMask, xK_Return), windows W.swapMaster)
|
, ((modm, xK_Return), windows W.swapMaster)
|
||||||
|
|
||||||
-- Swap the focused window with the next window
|
-- Swap the focused window with the next window
|
||||||
, ((modMask .|. shiftMask, xK_j ), windows W.swapDown )
|
, ((modm .|. shiftMask, xK_j ), windows W.swapDown )
|
||||||
|
|
||||||
-- Swap the focused window with the previous window
|
-- Swap the focused window with the previous window
|
||||||
, ((modMask .|. shiftMask, xK_k ), windows W.swapUp )
|
, ((modm .|. shiftMask, xK_k ), windows W.swapUp )
|
||||||
|
|
||||||
-- Shrink the master area
|
-- Shrink the master area
|
||||||
, ((modMask, xK_h ), sendMessage Shrink)
|
, ((modm, xK_h ), sendMessage Shrink)
|
||||||
|
|
||||||
-- Expand the master area
|
-- Expand the master area
|
||||||
, ((modMask, xK_l ), sendMessage Expand)
|
, ((modm, xK_l ), sendMessage Expand)
|
||||||
|
|
||||||
-- Push window back into tiling
|
-- Push window back into tiling
|
||||||
, ((modMask, xK_t ), withFocused $ windows . W.sink)
|
, ((modm, xK_t ), withFocused $ windows . W.sink)
|
||||||
|
|
||||||
-- Increment the number of windows in the master area
|
-- Increment the number of windows in the master area
|
||||||
, ((modMask , xK_comma ), sendMessage (IncMasterN 1))
|
, ((modm , xK_comma ), sendMessage (IncMasterN 1))
|
||||||
|
|
||||||
-- Deincrement the number of windows in the master area
|
-- Deincrement the number of windows in the master area
|
||||||
, ((modMask , xK_period), sendMessage (IncMasterN (-1)))
|
, ((modm , xK_period), sendMessage (IncMasterN (-1)))
|
||||||
|
|
||||||
-- toggle the status bar gap
|
-- Toggle the status bar gap
|
||||||
, ((modMask , xK_b ),
|
-- Use this binding with avoidStruts from Hooks.ManageDocks.
|
||||||
modifyGap (\i n -> let x = (XMonad.defaultGaps conf ++ repeat (0,0,0,0)) !! i
|
-- See also the statusBar function from Hooks.DynamicLog.
|
||||||
in if n == x then (0,0,0,0) else x))
|
--
|
||||||
|
-- , ((modm , xK_b ), sendMessage ToggleStruts)
|
||||||
|
|
||||||
-- Quit xmonad
|
-- Quit xmonad
|
||||||
, ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess))
|
, ((modm .|. shiftMask, xK_q ), io (exitWith ExitSuccess))
|
||||||
|
|
||||||
-- Restart xmonad
|
-- Restart xmonad
|
||||||
, ((modMask , xK_q ), restart "xmonad" True)
|
, ((modm , xK_q ), spawn "xmonad --recompile; xmonad --restart")
|
||||||
]
|
]
|
||||||
++
|
++
|
||||||
|
|
||||||
@@ -153,7 +130,7 @@ myKeys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
|||||||
-- mod-[1..9], Switch to workspace N
|
-- mod-[1..9], Switch to workspace N
|
||||||
-- mod-shift-[1..9], Move client to workspace N
|
-- mod-shift-[1..9], Move client to workspace N
|
||||||
--
|
--
|
||||||
[((m .|. modMask, k), windows $ f i)
|
[((m .|. modm, k), windows $ f i)
|
||||||
| (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9]
|
| (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9]
|
||||||
, (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
|
, (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
|
||||||
++
|
++
|
||||||
@@ -162,7 +139,7 @@ myKeys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
|||||||
-- mod-{w,e,r}, Switch to physical/Xinerama screens 1, 2, or 3
|
-- 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
|
-- mod-shift-{w,e,r}, Move client to screen 1, 2, or 3
|
||||||
--
|
--
|
||||||
[((m .|. modMask, key), screenWorkspace sc >>= flip whenJust (windows . f))
|
[((m .|. modm, key), screenWorkspace sc >>= flip whenJust (windows . f))
|
||||||
| (key, sc) <- zip [xK_w, xK_e, xK_r] [0..]
|
| (key, sc) <- zip [xK_w, xK_e, xK_r] [0..]
|
||||||
, (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
|
, (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
|
||||||
|
|
||||||
@@ -170,16 +147,18 @@ myKeys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
|||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
-- Mouse bindings: default actions bound to mouse events
|
-- Mouse bindings: default actions bound to mouse events
|
||||||
--
|
--
|
||||||
myMouseBindings (XConfig {XMonad.modMask = modMask}) = M.fromList $
|
myMouseBindings (XConfig {XMonad.modMask = modm}) = M.fromList $
|
||||||
|
|
||||||
-- mod-button1, Set the window to floating mode and move by dragging
|
-- mod-button1, Set the window to floating mode and move by dragging
|
||||||
[ ((modMask, button1), (\w -> focus w >> mouseMoveWindow w))
|
[ ((modm, button1), (\w -> focus w >> mouseMoveWindow w
|
||||||
|
>> windows W.shiftMaster))
|
||||||
|
|
||||||
-- mod-button2, Raise the window to the top of the stack
|
-- mod-button2, Raise the window to the top of the stack
|
||||||
, ((modMask, button2), (\w -> focus w >> windows W.swapMaster))
|
, ((modm, button2), (\w -> focus w >> windows W.shiftMaster))
|
||||||
|
|
||||||
-- mod-button3, Set the window to floating mode and resize by dragging
|
-- mod-button3, Set the window to floating mode and resize by dragging
|
||||||
, ((modMask, button3), (\w -> focus w >> mouseResizeWindow w))
|
, ((modm, button3), (\w -> focus w >> mouseResizeWindow w
|
||||||
|
>> windows W.shiftMaster))
|
||||||
|
|
||||||
-- you may also bind events to the mouse scroll wheel (button4 and button5)
|
-- you may also bind events to the mouse scroll wheel (button4 and button5)
|
||||||
]
|
]
|
||||||
@@ -230,23 +209,35 @@ myManageHook = composeAll
|
|||||||
, resource =? "desktop_window" --> doIgnore
|
, resource =? "desktop_window" --> doIgnore
|
||||||
, resource =? "kdesktop" --> doIgnore ]
|
, resource =? "kdesktop" --> doIgnore ]
|
||||||
|
|
||||||
-- Whether focus follows the mouse pointer.
|
------------------------------------------------------------------------
|
||||||
myFocusFollowsMouse :: Bool
|
-- Event handling
|
||||||
myFocusFollowsMouse = True
|
|
||||||
|
|
||||||
|
-- * EwmhDesktops users should change this to ewmhDesktopsEventHook
|
||||||
|
--
|
||||||
|
-- Defines a custom handler function for X Events. The function should
|
||||||
|
-- return (All True) if the default handler is to be run afterwards. To
|
||||||
|
-- combine event hooks use mappend or mconcat from Data.Monoid.
|
||||||
|
--
|
||||||
|
myEventHook = mempty
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
-- Status bars and logging
|
-- Status bars and logging
|
||||||
|
|
||||||
-- Perform an arbitrary action on each internal state change or X event.
|
-- Perform an arbitrary action on each internal state change or X event.
|
||||||
-- See the 'DynamicLog' extension for examples.
|
-- See the 'XMonad.Hooks.DynamicLog' extension for examples.
|
||||||
--
|
|
||||||
-- To emulate dwm's status bar
|
|
||||||
--
|
|
||||||
-- > logHook = dynamicLogDzen
|
|
||||||
--
|
--
|
||||||
myLogHook = return ()
|
myLogHook = return ()
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
-- Startup hook
|
||||||
|
|
||||||
|
-- Perform an arbitrary action each time xmonad starts or is restarted
|
||||||
|
-- with mod-q. Used by, e.g., XMonad.Layout.PerWorkspace to initialize
|
||||||
|
-- per-workspace layout choices.
|
||||||
|
--
|
||||||
|
-- By default, do nothing.
|
||||||
|
myStartupHook = return ()
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
-- Now run xmonad with all the defaults we set up.
|
-- Now run xmonad with all the defaults we set up.
|
||||||
|
|
||||||
@@ -255,9 +246,9 @@ myLogHook = return ()
|
|||||||
main = xmonad defaults
|
main = xmonad defaults
|
||||||
|
|
||||||
-- A structure containing your configuration settings, overriding
|
-- A structure containing your configuration settings, overriding
|
||||||
-- fields in the default config. Any you don't override, will
|
-- fields in the default config. Any you don't override, will
|
||||||
-- use the defaults defined in xmonad/XMonad/Config.hs
|
-- use the defaults defined in xmonad/XMonad/Config.hs
|
||||||
--
|
--
|
||||||
-- No need to modify this.
|
-- No need to modify this.
|
||||||
--
|
--
|
||||||
defaults = defaultConfig {
|
defaults = defaultConfig {
|
||||||
@@ -266,11 +257,9 @@ defaults = defaultConfig {
|
|||||||
focusFollowsMouse = myFocusFollowsMouse,
|
focusFollowsMouse = myFocusFollowsMouse,
|
||||||
borderWidth = myBorderWidth,
|
borderWidth = myBorderWidth,
|
||||||
modMask = myModMask,
|
modMask = myModMask,
|
||||||
numlockMask = myNumlockMask,
|
|
||||||
workspaces = myWorkspaces,
|
workspaces = myWorkspaces,
|
||||||
normalBorderColor = myNormalBorderColor,
|
normalBorderColor = myNormalBorderColor,
|
||||||
focusedBorderColor = myFocusedBorderColor,
|
focusedBorderColor = myFocusedBorderColor,
|
||||||
defaultGaps = myDefaultGaps,
|
|
||||||
|
|
||||||
-- key bindings
|
-- key bindings
|
||||||
keys = myKeys,
|
keys = myKeys,
|
||||||
@@ -279,5 +268,7 @@ defaults = defaultConfig {
|
|||||||
-- hooks, layouts
|
-- hooks, layouts
|
||||||
layoutHook = myLayout,
|
layoutHook = myLayout,
|
||||||
manageHook = myManageHook,
|
manageHook = myManageHook,
|
||||||
logHook = myLogHook
|
handleEventHook = myEventHook,
|
||||||
|
logHook = myLogHook,
|
||||||
|
startupHook = myStartupHook
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,9 @@
|
|||||||
module Properties where
|
module Properties where
|
||||||
|
|
||||||
import XMonad.StackSet hiding (filter)
|
import XMonad.StackSet hiding (filter)
|
||||||
|
import XMonad.Layout
|
||||||
|
import XMonad.Core hiding (workspaces,trace)
|
||||||
|
import XMonad.Operations ( applyResizeIncHint, applyMaxSizeHint )
|
||||||
import qualified XMonad.StackSet as S (filter)
|
import qualified XMonad.StackSet as S (filter)
|
||||||
|
|
||||||
import Debug.Trace
|
import Debug.Trace
|
||||||
@@ -11,7 +14,7 @@ import Data.Ratio
|
|||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import System.Environment
|
import System.Environment
|
||||||
import Control.Exception (assert)
|
import Control.Exception (assert)
|
||||||
import qualified Control.Exception as C
|
import qualified Control.Exception.Extensible as C
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Test.QuickCheck hiding (promote)
|
import Test.QuickCheck hiding (promote)
|
||||||
import System.IO.Unsafe
|
import System.IO.Unsafe
|
||||||
@@ -136,10 +139,10 @@ prop_empty_I (n :: Positive Int) l = forAll (choose (1,fromIntegral n)) $ \m -
|
|||||||
invariant $ new l [0..fromIntegral n-1] ms
|
invariant $ new l [0..fromIntegral n-1] ms
|
||||||
|
|
||||||
prop_view_I (n :: NonNegative Int) (x :: T) =
|
prop_view_I (n :: NonNegative Int) (x :: T) =
|
||||||
n `tagMember` x ==> invariant $ view (fromIntegral n) x
|
invariant $ view (fromIntegral n) x
|
||||||
|
|
||||||
prop_greedyView_I (n :: NonNegative Int) (x :: T) =
|
prop_greedyView_I (n :: NonNegative Int) (x :: T) =
|
||||||
n `tagMember` x ==> invariant $ view (fromIntegral n) x
|
invariant $ greedyView (fromIntegral n) x
|
||||||
|
|
||||||
prop_focusUp_I (n :: NonNegative Int) (x :: T) =
|
prop_focusUp_I (n :: NonNegative Int) (x :: T) =
|
||||||
invariant $ foldr (const focusUp) x [1..n]
|
invariant $ foldr (const focusUp) x [1..n]
|
||||||
@@ -236,6 +239,13 @@ prop_greedyView_current (x :: T) (n :: NonNegative Int) = i `tagMember` x ==>
|
|||||||
where
|
where
|
||||||
i = fromIntegral n
|
i = fromIntegral n
|
||||||
|
|
||||||
|
-- greedyView leaves things unchanged for invalid workspaces
|
||||||
|
prop_greedyView_current_id (x :: T) (n :: NonNegative Int) = not (i `tagMember` x) ==>
|
||||||
|
tag (workspace $ current (greedyView i x)) == j
|
||||||
|
where
|
||||||
|
i = fromIntegral n
|
||||||
|
j = tag (workspace (current x))
|
||||||
|
|
||||||
-- greedyView *only* sets the current workspace, and touches Xinerama.
|
-- greedyView *only* sets the current workspace, and touches Xinerama.
|
||||||
-- no workspace contents will be changed.
|
-- no workspace contents will be changed.
|
||||||
prop_greedyView_local (x :: T) (n :: NonNegative Int) = i `tagMember` x ==>
|
prop_greedyView_local (x :: T) (n :: NonNegative Int) = i `tagMember` x ==>
|
||||||
@@ -348,6 +358,10 @@ prop_focusWindow_local (n :: NonNegative Int) (x::T ) =
|
|||||||
i = fromIntegral n `mod` length s
|
i = fromIntegral n `mod` length s
|
||||||
in hidden_spaces (focusWindow (s !! i) x) == hidden_spaces x
|
in hidden_spaces (focusWindow (s !! i) x) == hidden_spaces x
|
||||||
|
|
||||||
|
-- On an invalid window, the stackset is unmodified
|
||||||
|
prop_focusWindow_identity (n :: Char) (x::T ) =
|
||||||
|
not (n `member` x) ==> focusWindow n x == x
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
-- member/findTag
|
-- member/findTag
|
||||||
|
|
||||||
@@ -364,6 +378,9 @@ prop_findIndex (x :: T) =
|
|||||||
|
|
||||||
prop_allWindowsMember w (x :: T) = (w `elem` allWindows x) ==> member w x
|
prop_allWindowsMember w (x :: T) = (w `elem` allWindows x) ==> member w x
|
||||||
|
|
||||||
|
prop_currentTag (x :: T) =
|
||||||
|
currentTag x == tag (workspace (current x))
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
-- 'insert'
|
-- 'insert'
|
||||||
|
|
||||||
@@ -511,6 +528,18 @@ prop_shift_reversible i (x :: T) =
|
|||||||
y = swapMaster x
|
y = swapMaster x
|
||||||
n = tag (workspace $ current y)
|
n = tag (workspace $ current y)
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
-- shiftMaster
|
||||||
|
|
||||||
|
-- focus/local/idempotent same as swapMaster:
|
||||||
|
prop_shift_master_focus (x :: T) = peek x == (peek $ shiftMaster x)
|
||||||
|
prop_shift_master_local (x :: T) = hidden_spaces x == hidden_spaces (shiftMaster x)
|
||||||
|
prop_shift_master_idempotent (x :: T) = shiftMaster (shiftMaster x) == shiftMaster x
|
||||||
|
-- ordering is constant modulo the focused window:
|
||||||
|
prop_shift_master_ordering (x :: T) = case peek x of
|
||||||
|
Nothing -> True
|
||||||
|
Just m -> L.delete m (index x) == L.delete m (index $ shiftMaster x)
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
-- shiftWin
|
-- shiftWin
|
||||||
|
|
||||||
@@ -540,13 +569,19 @@ prop_float_reversible n (x :: T) =
|
|||||||
where
|
where
|
||||||
geom = RationalRect 100 100 100 100
|
geom = RationalRect 100 100 100 100
|
||||||
|
|
||||||
-- check rectanges were set
|
prop_float_geometry n (x :: T) =
|
||||||
{-
|
n `member` x ==> let s = float n geom x
|
||||||
prop_float_sets_geometry n (x :: T) =
|
in M.lookup n (floating s) == Just geom
|
||||||
n `member` x ==> let y = float n geom x in M.lookup y (floating x) == Just geom
|
|
||||||
where
|
where
|
||||||
geom = RationalRect 100 100 100 100
|
geom = RationalRect 100 100 100 100
|
||||||
-}
|
|
||||||
|
prop_float_delete n (x :: T) =
|
||||||
|
n `member` x ==> let s = float n geom x
|
||||||
|
t = delete n s
|
||||||
|
in not (n `member` t)
|
||||||
|
where
|
||||||
|
geom = RationalRect 100 100 100 100
|
||||||
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -578,13 +613,13 @@ prop_lookup_visible (x :: T) =
|
|||||||
|
|
||||||
-- and help out hpc
|
-- and help out hpc
|
||||||
prop_abort x = unsafePerformIO $ C.catch (abort "fail")
|
prop_abort x = unsafePerformIO $ C.catch (abort "fail")
|
||||||
(\e -> return $ show e == "xmonad: StackSet: fail" )
|
(\(C.SomeException e) -> return $ show e == "xmonad: StackSet: fail" )
|
||||||
where
|
where
|
||||||
_ = x :: Int
|
_ = x :: Int
|
||||||
|
|
||||||
-- new should fail with an abort
|
-- new should fail with an abort
|
||||||
prop_new_abort x = unsafePerformIO $ C.catch f
|
prop_new_abort x = unsafePerformIO $ C.catch f
|
||||||
(\e -> return $ show e == "xmonad: StackSet: non-positive argument to StackSet.new" )
|
(\(C.SomeException e) -> return $ show e == "xmonad: StackSet: non-positive argument to StackSet.new" )
|
||||||
where
|
where
|
||||||
f = new undefined{-layout-} [] [] `seq` return False
|
f = new undefined{-layout-} [] [] `seq` return False
|
||||||
|
|
||||||
@@ -605,9 +640,26 @@ prop_rename1 (x::T) o n = o `tagMember` x && not (n `tagMember` x) ==>
|
|||||||
let y = renameTag o n x
|
let y = renameTag o n x
|
||||||
in n `tagMember` y
|
in n `tagMember` y
|
||||||
|
|
||||||
|
-- |
|
||||||
|
-- Ensure that a given set of workspace tags is present by renaming
|
||||||
|
-- existing workspaces and\/or creating new hidden workspaces as
|
||||||
|
-- necessary.
|
||||||
|
--
|
||||||
prop_ensure (x :: T) l xs = let y = ensureTags l xs x
|
prop_ensure (x :: T) l xs = let y = ensureTags l xs x
|
||||||
in and [ n `tagMember` y | n <- xs ]
|
in and [ n `tagMember` y | n <- xs ]
|
||||||
|
|
||||||
|
-- adding a tag should create a new hidden workspace
|
||||||
|
prop_ensure_append (x :: T) l n =
|
||||||
|
not (n `tagMember` x)
|
||||||
|
==>
|
||||||
|
(hidden y /= hidden x -- doesn't append, renames
|
||||||
|
&&
|
||||||
|
and [ isNothing (stack z) && layout z == l | z <- hidden y, tag z == n ]
|
||||||
|
)
|
||||||
|
where
|
||||||
|
y = ensureTags l (n:ts) x
|
||||||
|
ts = [ tag z | z <- workspaces x ]
|
||||||
|
|
||||||
prop_mapWorkspaceId (x::T) = x == mapWorkspace id x
|
prop_mapWorkspaceId (x::T) = x == mapWorkspace id x
|
||||||
|
|
||||||
prop_mapWorkspaceInverse (x::T) = x == mapWorkspace predTag (mapWorkspace succTag x)
|
prop_mapWorkspaceInverse (x::T) = x == mapWorkspace predTag (mapWorkspace succTag x)
|
||||||
@@ -619,17 +671,145 @@ prop_mapLayoutId (x::T) = x == mapLayout id x
|
|||||||
prop_mapLayoutInverse (x::T) = x == mapLayout pred (mapLayout succ x)
|
prop_mapLayoutInverse (x::T) = x == mapLayout pred (mapLayout succ x)
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
-- some properties for layouts:
|
-- The Tall layout
|
||||||
|
|
||||||
-- 1 window should always be tiled fullscreen
|
-- 1 window should always be tiled fullscreen
|
||||||
{-
|
|
||||||
prop_tile_fullscreen rect = tile pct rect 1 1 == [rect]
|
prop_tile_fullscreen rect = tile pct rect 1 1 == [rect]
|
||||||
|
where pct = 1/2
|
||||||
|
|
||||||
-- multiple windows
|
-- multiple windows
|
||||||
prop_tile_non_overlap rect windows nmaster = noOverlaps (tile pct rect nmaster windows)
|
prop_tile_non_overlap rect windows nmaster = noOverlaps (tile pct rect nmaster windows)
|
||||||
where _ = rect :: Rectangle
|
where _ = rect :: Rectangle
|
||||||
|
pct = 3 % 100
|
||||||
|
|
||||||
pct = 3 % 100
|
-- splitting horizontally yields sensible results
|
||||||
|
prop_split_hoziontal (NonNegative n) x =
|
||||||
|
{-
|
||||||
|
trace (show (rect_x x
|
||||||
|
,rect_width x
|
||||||
|
,rect_x x + fromIntegral (rect_width x)
|
||||||
|
,map rect_x xs))
|
||||||
|
$
|
||||||
|
-}
|
||||||
|
|
||||||
|
sum (map rect_width xs) == rect_width x
|
||||||
|
&&
|
||||||
|
all (== rect_height x) (map rect_height xs)
|
||||||
|
&&
|
||||||
|
(map rect_x xs) == (sort $ map rect_x xs)
|
||||||
|
|
||||||
|
where
|
||||||
|
xs = splitHorizontally n x
|
||||||
|
|
||||||
|
-- splitting horizontally yields sensible results
|
||||||
|
prop_splitVertically (r :: Rational) x =
|
||||||
|
|
||||||
|
rect_x x == rect_x a && rect_x x == rect_x b
|
||||||
|
&&
|
||||||
|
rect_width x == rect_width a && rect_width x == rect_width b
|
||||||
|
|
||||||
|
{-
|
||||||
|
trace (show (rect_x x
|
||||||
|
,rect_width x
|
||||||
|
,rect_x x + fromIntegral (rect_width x)
|
||||||
|
,map rect_x xs))
|
||||||
|
$
|
||||||
|
-}
|
||||||
|
|
||||||
|
where
|
||||||
|
(a,b) = splitVerticallyBy r x
|
||||||
|
|
||||||
|
|
||||||
|
-- pureLayout works.
|
||||||
|
prop_purelayout_tall n r1 r2 rect (t :: T) =
|
||||||
|
isJust (peek t) ==>
|
||||||
|
length ts == length (index t)
|
||||||
|
&&
|
||||||
|
noOverlaps (map snd ts)
|
||||||
|
&&
|
||||||
|
description layoot == "Tall"
|
||||||
|
where layoot = Tall n r1 r2
|
||||||
|
st = fromJust . stack . workspace . current $ t
|
||||||
|
ts = pureLayout layoot rect st
|
||||||
|
|
||||||
|
-- Test message handling of Tall
|
||||||
|
|
||||||
|
-- what happens when we send a Shrink message to Tall
|
||||||
|
prop_shrink_tall (NonNegative n) (NonZero (NonNegative delta)) (NonNegative frac) =
|
||||||
|
n == n' && delta == delta' -- these state components are unchanged
|
||||||
|
&& frac' <= frac && (if frac' < frac then frac' == 0 || frac' == frac - delta
|
||||||
|
else frac == 0 )
|
||||||
|
-- remaining fraction should shrink
|
||||||
|
where
|
||||||
|
l1 = Tall n delta frac
|
||||||
|
Just l2@(Tall n' delta' frac') = l1 `pureMessage` (SomeMessage Shrink)
|
||||||
|
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
|
||||||
|
|
||||||
|
|
||||||
|
-- what happens when we send a Shrink message to Tall
|
||||||
|
prop_expand_tall (NonNegative n)
|
||||||
|
(NonZero (NonNegative delta))
|
||||||
|
(NonNegative n1)
|
||||||
|
(NonZero (NonNegative d1)) =
|
||||||
|
|
||||||
|
n == n'
|
||||||
|
&& delta == delta' -- these state components are unchanged
|
||||||
|
&& frac' >= frac
|
||||||
|
&& (if frac' > frac
|
||||||
|
then frac' == 1 || frac' == frac + delta
|
||||||
|
else frac == 1 )
|
||||||
|
|
||||||
|
-- remaining fraction should shrink
|
||||||
|
where
|
||||||
|
frac = min 1 (n1 % d1)
|
||||||
|
l1 = Tall n delta frac
|
||||||
|
Just l2@(Tall n' delta' frac') = l1 `pureMessage` (SomeMessage Expand)
|
||||||
|
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
|
||||||
|
|
||||||
|
-- what happens when we send an IncMaster message to Tall
|
||||||
|
prop_incmaster_tall (NonNegative n) (NonZero (NonNegative delta)) (NonNegative frac)
|
||||||
|
(NonNegative k) =
|
||||||
|
delta == delta' && frac == frac' && n' == n + k
|
||||||
|
where
|
||||||
|
l1 = Tall n delta frac
|
||||||
|
Just l2@(Tall n' delta' frac') = l1 `pureMessage` (SomeMessage (IncMasterN k))
|
||||||
|
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- toMessage LT = SomeMessage Shrink
|
||||||
|
-- toMessage EQ = SomeMessage Expand
|
||||||
|
-- toMessage GT = SomeMessage (IncMasterN 1)
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
-- Full layout
|
||||||
|
|
||||||
|
-- pureLayout works for Full
|
||||||
|
prop_purelayout_full rect (t :: T) =
|
||||||
|
isJust (peek t) ==>
|
||||||
|
length ts == 1 -- only one window to view
|
||||||
|
&&
|
||||||
|
snd (head ts) == rect -- and sets fullscreen
|
||||||
|
&&
|
||||||
|
fst (head ts) == fromJust (peek t) -- and the focused window is shown
|
||||||
|
|
||||||
|
where layoot = Full
|
||||||
|
st = fromJust . stack . workspace . current $ t
|
||||||
|
ts = pureLayout layoot rect st
|
||||||
|
|
||||||
|
-- what happens when we send an IncMaster message to Full --- Nothing
|
||||||
|
prop_sendmsg_full (NonNegative k) =
|
||||||
|
isNothing (Full `pureMessage` (SomeMessage (IncMasterN k)))
|
||||||
|
|
||||||
|
prop_desc_full = description Full == show Full
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
prop_desc_mirror n r1 r2 = description (Mirror $! t) == "Mirror Tall"
|
||||||
|
where t = Tall n r1 r2
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
noOverlaps [] = True
|
noOverlaps [] = True
|
||||||
noOverlaps [_] = True
|
noOverlaps [_] = True
|
||||||
@@ -645,7 +825,28 @@ noOverlaps xs = and [ verts a `notOverlap` verts b
|
|||||||
= (top1 < bottom2 || top2 < bottom1)
|
= (top1 < bottom2 || top2 < bottom1)
|
||||||
|| (right1 < left2 || right2 < left1)
|
|| (right1 < left2 || right2 < left1)
|
||||||
|
|
||||||
-}
|
------------------------------------------------------------------------
|
||||||
|
-- Aspect ratios
|
||||||
|
|
||||||
|
prop_resize_inc (NonZero (NonNegative inc_w),NonZero (NonNegative inc_h)) b@(w,h) =
|
||||||
|
w' `mod` inc_w == 0 && h' `mod` inc_h == 0
|
||||||
|
where (w',h') = applyResizeIncHint a b
|
||||||
|
a = (inc_w,inc_h)
|
||||||
|
|
||||||
|
prop_resize_inc_extra ((NonNegative inc_w)) b@(w,h) =
|
||||||
|
(w,h) == (w',h')
|
||||||
|
where (w',h') = applyResizeIncHint a b
|
||||||
|
a = (-inc_w,0::Dimension)-- inc_h)
|
||||||
|
|
||||||
|
prop_resize_max (NonZero (NonNegative inc_w),NonZero (NonNegative inc_h)) b@(w,h) =
|
||||||
|
w' <= inc_w && h' <= inc_h
|
||||||
|
where (w',h') = applyMaxSizeHint a b
|
||||||
|
a = (inc_w,inc_h)
|
||||||
|
|
||||||
|
prop_resize_max_extra ((NonNegative inc_w)) b@(w,h) =
|
||||||
|
(w,h) == (w',h')
|
||||||
|
where (w',h') = applyMaxSizeHint a b
|
||||||
|
a = (-inc_w,0::Dimension)-- inc_h)
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -653,7 +854,7 @@ main :: IO ()
|
|||||||
main = do
|
main = do
|
||||||
args <- fmap (drop 1) getArgs
|
args <- fmap (drop 1) getArgs
|
||||||
let n = if null args then 100 else read (head args)
|
let n = if null args then 100 else read (head args)
|
||||||
(results, passed) <- liftM unzip $ mapM (\(s,a) -> printf "%-25s: " s >> a n) tests
|
(results, passed) <- liftM unzip $ mapM (\(s,a) -> printf "%-40s: " s >> a n) tests
|
||||||
printf "Passed %d tests!\n" (sum passed)
|
printf "Passed %d tests!\n" (sum passed)
|
||||||
when (not . and $ results) $ fail "Not all tests passed!"
|
when (not . and $ results) $ fail "Not all tests passed!"
|
||||||
where
|
where
|
||||||
@@ -675,6 +876,7 @@ main = do
|
|||||||
|
|
||||||
,("greedyView : invariant" , mytest prop_greedyView_I)
|
,("greedyView : invariant" , mytest prop_greedyView_I)
|
||||||
,("greedyView sets current" , mytest prop_greedyView_current)
|
,("greedyView sets current" , mytest prop_greedyView_current)
|
||||||
|
,("greedyView is safe " , mytest prop_greedyView_current_id)
|
||||||
,("greedyView idempotent" , mytest prop_greedyView_idem)
|
,("greedyView idempotent" , mytest prop_greedyView_idem)
|
||||||
,("greedyView reversible" , mytest prop_greedyView_reversible)
|
,("greedyView reversible" , mytest prop_greedyView_reversible)
|
||||||
,("greedyView is local" , mytest prop_greedyView_local)
|
,("greedyView is local" , mytest prop_greedyView_local)
|
||||||
@@ -704,9 +906,11 @@ main = do
|
|||||||
|
|
||||||
,("focusWindow is local", mytest prop_focusWindow_local)
|
,("focusWindow is local", mytest prop_focusWindow_local)
|
||||||
,("focusWindow works" , mytest prop_focusWindow_works)
|
,("focusWindow works" , mytest prop_focusWindow_works)
|
||||||
|
,("focusWindow identity", mytest prop_focusWindow_identity)
|
||||||
|
|
||||||
,("findTag" , mytest prop_findIndex)
|
,("findTag" , mytest prop_findIndex)
|
||||||
,("allWindows/member" , mytest prop_allWindowsMember)
|
,("allWindows/member" , mytest prop_allWindowsMember)
|
||||||
|
,("currentTag" , mytest prop_currentTag)
|
||||||
|
|
||||||
,("insert: invariant" , mytest prop_insertUp_I)
|
,("insert: invariant" , mytest prop_insertUp_I)
|
||||||
,("insert/new" , mytest prop_insert_empty)
|
,("insert/new" , mytest prop_insert_empty)
|
||||||
@@ -741,6 +945,11 @@ main = do
|
|||||||
,("swapUp is local" , mytest prop_swap_left_local)
|
,("swapUp is local" , mytest prop_swap_left_local)
|
||||||
,("swapDown is local" , mytest prop_swap_right_local)
|
,("swapDown is local" , mytest prop_swap_right_local)
|
||||||
|
|
||||||
|
,("shiftMaster id on focus", mytest prop_shift_master_focus)
|
||||||
|
,("shiftMaster is local", mytest prop_shift_master_local)
|
||||||
|
,("shiftMaster is idempotent", mytest prop_shift_master_idempotent)
|
||||||
|
,("shiftMaster preserves ordering", mytest prop_shift_master_ordering)
|
||||||
|
|
||||||
,("shift: invariant" , mytest prop_shift_I)
|
,("shift: invariant" , mytest prop_shift_I)
|
||||||
,("shift is reversible" , mytest prop_shift_reversible)
|
,("shift is reversible" , mytest prop_shift_reversible)
|
||||||
,("shiftWin: invariant" , mytest prop_shift_win_I)
|
,("shiftWin: invariant" , mytest prop_shift_win_I)
|
||||||
@@ -748,13 +957,17 @@ main = do
|
|||||||
,("shiftWin fix current" , mytest prop_shift_win_fix_current)
|
,("shiftWin fix current" , mytest prop_shift_win_fix_current)
|
||||||
|
|
||||||
,("floating is reversible" , mytest prop_float_reversible)
|
,("floating is reversible" , mytest prop_float_reversible)
|
||||||
|
,("floating sets geometry" , mytest prop_float_geometry)
|
||||||
|
,("floats can be deleted", mytest prop_float_delete)
|
||||||
,("screens includes current", mytest prop_screens)
|
,("screens includes current", mytest prop_screens)
|
||||||
|
|
||||||
,("differentiate works", mytest prop_differentiate)
|
,("differentiate works", mytest prop_differentiate)
|
||||||
,("lookupTagOnScreen", mytest prop_lookup_current)
|
,("lookupTagOnScreen", mytest prop_lookup_current)
|
||||||
,("lookupTagOnVisbleScreen", mytest prop_lookup_visible)
|
,("lookupTagOnVisbleScreen", mytest prop_lookup_visible)
|
||||||
,("screens works", mytest prop_screens_works)
|
,("screens works", mytest prop_screens_works)
|
||||||
,("renaming works", mytest prop_rename1)
|
,("renaming works", mytest prop_rename1)
|
||||||
,("ensure works", mytest prop_ensure)
|
,("ensure works", mytest prop_ensure)
|
||||||
|
,("ensure hidden semantics", mytest prop_ensure_append)
|
||||||
|
|
||||||
,("mapWorkspace id", mytest prop_mapWorkspaceId)
|
,("mapWorkspace id", mytest prop_mapWorkspaceId)
|
||||||
,("mapWorkspace inverse", mytest prop_mapWorkspaceInverse)
|
,("mapWorkspace inverse", mytest prop_mapWorkspaceInverse)
|
||||||
@@ -766,12 +979,31 @@ main = do
|
|||||||
,("new fails with abort", mytest prop_new_abort)
|
,("new fails with abort", mytest prop_new_abort)
|
||||||
,("shiftWin identity", mytest prop_shift_win_indentity)
|
,("shiftWin identity", mytest prop_shift_win_indentity)
|
||||||
|
|
||||||
-- renaming
|
-- tall layout
|
||||||
|
|
||||||
{-
|
|
||||||
,("tile 1 window fullsize", mytest prop_tile_fullscreen)
|
,("tile 1 window fullsize", mytest prop_tile_fullscreen)
|
||||||
,("tiles never overlap", mytest prop_tile_non_overlap)
|
,("tiles never overlap", mytest prop_tile_non_overlap)
|
||||||
-}
|
,("split hozizontally", mytest prop_split_hoziontal)
|
||||||
|
,("split verticalBy", mytest prop_splitVertically)
|
||||||
|
|
||||||
|
,("pure layout tall", mytest prop_purelayout_tall)
|
||||||
|
,("send shrink tall", mytest prop_shrink_tall)
|
||||||
|
,("send expand tall", mytest prop_expand_tall)
|
||||||
|
,("send incmaster tall", mytest prop_incmaster_tall)
|
||||||
|
|
||||||
|
-- full layout
|
||||||
|
|
||||||
|
,("pure layout full", mytest prop_purelayout_full)
|
||||||
|
,("send message full", mytest prop_sendmsg_full)
|
||||||
|
,("describe full", mytest prop_desc_full)
|
||||||
|
|
||||||
|
,("describe mirror", mytest prop_desc_mirror)
|
||||||
|
|
||||||
|
-- resize hints
|
||||||
|
,("window hints: inc", mytest prop_resize_inc)
|
||||||
|
,("window hints: inc all", mytest prop_resize_inc_extra)
|
||||||
|
,("window hints: max", mytest prop_resize_max)
|
||||||
|
,("window hints: max all ", mytest prop_resize_max_extra)
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
10
tests/coverage.hs
Normal file
10
tests/coverage.hs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env runhaskell
|
||||||
|
|
||||||
|
import System.Cmd
|
||||||
|
|
||||||
|
-- generate appropriate .hpc files
|
||||||
|
main = do
|
||||||
|
system $ "rm -rf *.tix"
|
||||||
|
system $ "dist/build/xmonad/xmonad --run-tests"
|
||||||
|
system $ "hpc markup xmonad --exclude=Main --exclude=Properties --exclude=XMonad --exclude=Paths_xmonad"
|
||||||
|
system $ "hpc report xmonad --exclude=Main --exclude=Properties --exclude=XMonad --exclude=Paths_xmonad"
|
@@ -5,9 +5,9 @@ main = do foo <- getContents
|
|||||||
let actual_loc = filter (not.null) $ filter isntcomment $
|
let actual_loc = filter (not.null) $ filter isntcomment $
|
||||||
map (dropWhile (==' ')) $ lines foo
|
map (dropWhile (==' ')) $ lines foo
|
||||||
loc = length actual_loc
|
loc = length actual_loc
|
||||||
putStrLn $ show loc
|
print loc
|
||||||
-- uncomment the following to check for mistakes in isntcomment
|
-- uncomment the following to check for mistakes in isntcomment
|
||||||
-- putStr $ unlines $ actual_loc
|
-- print actual_loc
|
||||||
|
|
||||||
isntcomment ('-':'-':_) = False
|
isntcomment ('-':'-':_) = False
|
||||||
isntcomment ('{':'-':_) = False -- pragmas
|
isntcomment ('{':'-':_) = False -- pragmas
|
||||||
|
@@ -1,7 +1,14 @@
|
|||||||
|
-- Unlike the rest of xmonad, this file is copyright under the terms of the
|
||||||
|
-- GPL.
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Generates man/xmonad.1 from man/xmonad.1.in by filling the list of
|
-- Generates man/xmonad.1 from man/xmonad.1.in by filling the list of
|
||||||
-- keybindings with values scraped from Config.hs
|
-- keybindings with values scraped from Config.hs
|
||||||
--
|
--
|
||||||
|
-- Uses cabal to grab the xmonad version from xmonad.cabal
|
||||||
|
--
|
||||||
|
-- Uses pandoc to convert the "xmonad.1.markdown" to "xmonad.1"
|
||||||
|
--
|
||||||
-- Format for the docstrings in Config.hs takes the following form:
|
-- Format for the docstrings in Config.hs takes the following form:
|
||||||
--
|
--
|
||||||
-- -- mod-x %! Frob the whatsit
|
-- -- mod-x %! Frob the whatsit
|
||||||
@@ -14,12 +21,23 @@
|
|||||||
-- [ ((modMask .|. shiftMask, xK_Return), spawn "xterm") -- %! Launch an xterm
|
-- [ ((modMask .|. shiftMask, xK_Return), spawn "xterm") -- %! Launch an xterm
|
||||||
--
|
--
|
||||||
-- Here, mod-shift-return will be used as the keybinding name.
|
-- Here, mod-shift-return will be used as the keybinding name.
|
||||||
--
|
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
|
import Control.Applicative
|
||||||
import Text.Regex.Posix
|
import Text.Regex.Posix
|
||||||
import Data.Char
|
import Data.Char
|
||||||
import Data.List
|
import Data.List
|
||||||
|
|
||||||
|
import Distribution.PackageDescription.Parse
|
||||||
|
import Distribution.Verbosity
|
||||||
|
import Distribution.Package
|
||||||
|
import Distribution.PackageDescription
|
||||||
|
import Text.PrettyPrint.HughesPJ
|
||||||
|
import Distribution.Text
|
||||||
|
|
||||||
|
import Text.Pandoc -- works with 1.6
|
||||||
|
|
||||||
|
releaseDate = "25 October 09"
|
||||||
|
|
||||||
trim :: String -> String
|
trim :: String -> String
|
||||||
trim = reverse . dropWhile isSpace . reverse . dropWhile isSpace
|
trim = reverse . dropWhile isSpace . reverse . dropWhile isSpace
|
||||||
|
|
||||||
@@ -35,13 +53,47 @@ allBindings :: String -> [(String, String)]
|
|||||||
allBindings xs = map (binding . map trim) (xs =~ "(.*)--(.*)%!(.*)")
|
allBindings xs = map (binding . map trim) (xs =~ "(.*)--(.*)%!(.*)")
|
||||||
|
|
||||||
-- FIXME: What escaping should we be doing on these strings?
|
-- FIXME: What escaping should we be doing on these strings?
|
||||||
troff :: (String, String) -> String
|
markdownDefn :: (String, String) -> String
|
||||||
troff (key, desc) = ".IP\n \\fB" ++ key ++ "\\fR\n" ++ desc ++ "\n"
|
markdownDefn (key, desc) = key ++ "\n: " ++ desc
|
||||||
|
|
||||||
replace :: Eq a => a -> a -> [a] -> [a]
|
replace :: Eq a => a -> a -> [a] -> [a]
|
||||||
replace x y = map (\a -> if a == x then y else a)
|
replace x y = map (\a -> if a == x then y else a)
|
||||||
|
|
||||||
|
-- rawSystem "pandoc" ["--read=markdown","--write=man","man/xmonad.1.markdown"]
|
||||||
|
|
||||||
main = do
|
main = do
|
||||||
troffBindings <- (concatMap troff . allBindings) `liftM` readFile "./XMonad/Config.hs"
|
releaseName <- (show . disp . package . packageDescription)
|
||||||
let sed = unlines . replace "___KEYBINDINGS___" troffBindings . lines
|
`liftM`readPackageDescription normal "xmonad.cabal"
|
||||||
readFile "./man/xmonad.1.in" >>= return . sed >>= writeFile "./man/xmonad.1"
|
keybindings <- (intercalate "\n\n" . map markdownDefn . allBindings)
|
||||||
|
`liftM` readFile "./XMonad/Config.hs"
|
||||||
|
|
||||||
|
let manHeader = unwords [".TH xmonad 1","\""++releaseDate++"\"",releaseName,"\"xmonad manual\""]
|
||||||
|
writeOpts = defaultWriterOptions -- { writerLiterateHaskell = True }
|
||||||
|
|
||||||
|
parsed <- readMarkdown defaultParserState { stateLiterateHaskell = True }
|
||||||
|
. unlines
|
||||||
|
. replace "___KEYBINDINGS___" keybindings
|
||||||
|
. lines
|
||||||
|
<$> readFile "./man/xmonad.1.markdown"
|
||||||
|
|
||||||
|
Right template <- getDefaultTemplate Nothing "man"
|
||||||
|
writeFile "./man/xmonad.1"
|
||||||
|
. (manHeader ++)
|
||||||
|
. writeMan writeOpts{ writerStandalone = True, writerTemplate = template }
|
||||||
|
$ parsed
|
||||||
|
putStrLn "Documentation created: man/xmonad.1"
|
||||||
|
|
||||||
|
Right template <- getDefaultTemplate Nothing "html"
|
||||||
|
writeFile "./man/xmonad.1.html"
|
||||||
|
. writeHtmlString writeOpts
|
||||||
|
{ writerVariables =
|
||||||
|
[("include-before"
|
||||||
|
,"<h1>"++releaseName++"</h1>"++
|
||||||
|
"<p>Section: xmonad manual (1)<br/>"++
|
||||||
|
"Updated: "++releaseDate++"</p>"++
|
||||||
|
"<hr/>")]
|
||||||
|
, writerStandalone = True
|
||||||
|
, writerTemplate = template
|
||||||
|
, writerTableOfContents = True }
|
||||||
|
$ parsed
|
||||||
|
putStrLn "Documentation created: man/xmonad.1.html"
|
||||||
|
28
xmonad.cabal
28
xmonad.cabal
@@ -1,5 +1,5 @@
|
|||||||
name: xmonad
|
name: xmonad
|
||||||
version: 0.6
|
version: 0.10
|
||||||
homepage: http://xmonad.org
|
homepage: http://xmonad.org
|
||||||
synopsis: A tiling window manager
|
synopsis: A tiling window manager
|
||||||
description:
|
description:
|
||||||
@@ -18,9 +18,12 @@ license-file: LICENSE
|
|||||||
author: Spencer Janssen
|
author: Spencer Janssen
|
||||||
maintainer: xmonad@haskell.org
|
maintainer: xmonad@haskell.org
|
||||||
extra-source-files: README TODO CONFIG STYLE tests/loc.hs tests/Properties.hs
|
extra-source-files: README TODO CONFIG STYLE tests/loc.hs tests/Properties.hs
|
||||||
man/xmonad.1.in man/xmonad.1 man/xmonad.html man/xmonad.hs
|
man/xmonad.1.markdown man/xmonad.1 man/xmonad.1.html
|
||||||
util/GenerateManpage.hs
|
util/GenerateManpage.hs
|
||||||
cabal-version: >= 1.2
|
cabal-version: >= 1.2
|
||||||
|
build-type: Simple
|
||||||
|
|
||||||
|
data-files: man/xmonad.hs
|
||||||
|
|
||||||
flag small_base
|
flag small_base
|
||||||
description: Choose the new smaller, split-up base package.
|
description: Choose the new smaller, split-up base package.
|
||||||
@@ -40,12 +43,18 @@ library
|
|||||||
XMonad.StackSet
|
XMonad.StackSet
|
||||||
|
|
||||||
if flag(small_base)
|
if flag(small_base)
|
||||||
build-depends: base >= 3, containers, directory, process
|
build-depends: base < 5 && >=3, containers, directory, process, filepath, extensible-exceptions
|
||||||
else
|
else
|
||||||
build-depends: base < 3
|
build-depends: base < 3
|
||||||
build-depends: X11>=1.4.1, mtl, unix
|
build-depends: X11>=1.5.0.0 && < 1.6, mtl, unix,
|
||||||
|
utf8-string >= 0.3 && < 0.4
|
||||||
|
|
||||||
|
if true
|
||||||
|
ghc-options: -funbox-strict-fields -Wall
|
||||||
|
|
||||||
|
if impl(ghc >= 6.12.1)
|
||||||
|
ghc-options: -fno-warn-unused-do-bind
|
||||||
|
|
||||||
ghc-options: -funbox-strict-fields -Wall -optl-Wl,-s
|
|
||||||
ghc-prof-options: -prof -auto-all
|
ghc-prof-options: -prof -auto-all
|
||||||
extensions: CPP
|
extensions: CPP
|
||||||
|
|
||||||
@@ -63,7 +72,12 @@ executable xmonad
|
|||||||
XMonad.Operations
|
XMonad.Operations
|
||||||
XMonad.StackSet
|
XMonad.StackSet
|
||||||
|
|
||||||
ghc-options: -funbox-strict-fields -Wall -optl-Wl,-s
|
if true
|
||||||
|
ghc-options: -funbox-strict-fields -Wall
|
||||||
|
|
||||||
|
if impl(ghc >= 6.12.1)
|
||||||
|
ghc-options: -fno-warn-unused-do-bind
|
||||||
|
|
||||||
ghc-prof-options: -prof -auto-all
|
ghc-prof-options: -prof -auto-all
|
||||||
extensions: CPP
|
extensions: CPP
|
||||||
|
|
||||||
@@ -73,4 +87,4 @@ executable xmonad
|
|||||||
build-depends: QuickCheck < 2
|
build-depends: QuickCheck < 2
|
||||||
ghc-options: -Werror
|
ghc-options: -Werror
|
||||||
if flag(testing) && flag(small_base)
|
if flag(testing) && flag(small_base)
|
||||||
build-depends: random
|
build-depends: filepath, process, directory, mtl, unix, X11, base, containers, random, extensible-exceptions
|
||||||
|
Reference in New Issue
Block a user