mirror of
https://github.com/xmonad/xmonad.git
synced 2025-08-02 05:01:53 -07:00
Compare commits
128 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a0ffe7e47d | ||
|
b00b94fda7 | ||
|
45a78ba802 | ||
|
4c0717e9cc | ||
|
30b4ff5e40 | ||
|
b68ebc797a | ||
|
eb4ef5b23f | ||
|
73224be21b | ||
|
7e287ec815 | ||
|
60f472faa2 | ||
|
bd72c6e1e2 | ||
|
b5402e76d3 | ||
|
39dc00b16f | ||
|
038f77de5a | ||
|
59e731ea11 | ||
|
3ce9dcbbb5 | ||
|
f25afdab9f | ||
|
577d5ae968 | ||
|
29bcd465c2 | ||
|
307b82a53d | ||
|
197b0091f8 | ||
|
69c5dae00d | ||
|
28c3482411 | ||
|
73ee008cf6 | ||
|
82a1cae123 | ||
|
d01b913594 | ||
|
d9e3ebf531 | ||
|
252c9d5eee | ||
|
5f0b1601d5 | ||
|
d60791e3f5 | ||
|
f0054fdde7 | ||
|
a9de363b14 | ||
|
7eb6ba0126 | ||
|
f03d2cdf74 | ||
|
16c0cb9a33 | ||
|
fdf3fb2c58 | ||
|
22d5e7eaa3 | ||
|
f837b830fc | ||
|
939c0558e6 | ||
|
fbd406eb03 | ||
|
ecde376224 | ||
|
edf3394821 | ||
|
20be322b08 | ||
|
f8f53fdff8 | ||
|
1da1e2e21e | ||
|
4026075bc6 | ||
|
8863761d66 | ||
|
d67dcd8c4b | ||
|
aa84841289 | ||
|
d10abdcdd0 | ||
|
3073826dfc | ||
|
daed0062c6 | ||
|
abd737cfb4 | ||
|
e719be4e69 | ||
|
ec1a20c727 | ||
|
8f039ec434 | ||
|
057fcc5162 | ||
|
8e7634f543 | ||
|
40cb12ce17 | ||
|
b803fd74a5 | ||
|
d386a230f6 | ||
|
e87456ab77 | ||
|
cdc22f0849 | ||
|
70413b2e22 | ||
|
67ffde0dfb | ||
|
d904fb1cc4 | ||
|
4120be8ba0 | ||
|
e015155131 | ||
|
4c1536cd18 | ||
|
a34a5e979a | ||
|
38faddf9de | ||
|
3ab2b28711 | ||
|
934ff6a562 | ||
|
f8b07d8956 | ||
|
67d436a4e6 | ||
|
c6fef373dc | ||
|
2d4f304c0a | ||
|
1df8ea3d0e | ||
|
490719c035 | ||
|
3cd001e8df | ||
|
b0dda7b351 | ||
|
d8495adf0d | ||
|
06f35a650e | ||
|
56f5ecb320 | ||
|
ff674a27e2 | ||
|
6c51745122 | ||
|
108c2280ef | ||
|
e70b489936 | ||
|
450c3a34fe | ||
|
32f416a3c2 | ||
|
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 |
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
.cabal-sandbox/
|
||||
cabal.sandbox.config
|
||||
.hpc/
|
||||
*.hi
|
||||
*.o
|
||||
*.p_hi
|
||||
*.prof
|
||||
*.tix
|
||||
cabal.config
|
||||
dist
|
||||
dist-*
|
||||
|
||||
# editor temp files
|
||||
|
||||
*#
|
||||
.#*
|
||||
*~
|
||||
.*.swp
|
||||
|
||||
# TAGS files
|
||||
TAGS
|
||||
tags
|
||||
|
||||
# stack artifacts
|
||||
/.stack-work/
|
82
.travis.yml
Normal file
82
.travis.yml
Normal file
@@ -0,0 +1,82 @@
|
||||
# This file has been generated -- see https://github.com/hvr/multi-ghc-travis
|
||||
language: c
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cabsnap
|
||||
- $HOME/.cabal/packages
|
||||
|
||||
before_cache:
|
||||
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
|
||||
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.tar
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- env: CABALVER=1.16 GHCVER=7.6.3
|
||||
compiler: ": #GHC 7.6.3"
|
||||
addons: {apt: {packages: [cabal-install-1.16,ghc-7.6.3], sources: [hvr-ghc]}}
|
||||
- env: CABALVER=1.18 GHCVER=7.8.4
|
||||
compiler: ": #GHC 7.8.4"
|
||||
addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4], sources: [hvr-ghc]}}
|
||||
- env: CABALVER=1.22 GHCVER=7.10.2
|
||||
compiler: ": #GHC 7.10.2"
|
||||
addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.2], sources: [hvr-ghc]}}
|
||||
|
||||
before_install:
|
||||
- unset CC
|
||||
- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
|
||||
|
||||
install:
|
||||
- cabal --version
|
||||
- echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]"
|
||||
- if [ -f $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz ];
|
||||
then
|
||||
zcat $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz >
|
||||
$HOME/.cabal/packages/hackage.haskell.org/00-index.tar;
|
||||
fi
|
||||
- travis_retry cabal update -v
|
||||
- sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
|
||||
- cabal install --only-dependencies --enable-tests --enable-benchmarks --dry -v > installplan.txt
|
||||
- sed -i -e '1,/^Resolving /d' installplan.txt; cat installplan.txt
|
||||
|
||||
# check whether current requested install-plan matches cached package-db snapshot
|
||||
- if diff -u installplan.txt $HOME/.cabsnap/installplan.txt;
|
||||
then
|
||||
echo "cabal build-cache HIT";
|
||||
rm -rfv .ghc;
|
||||
cp -a $HOME/.cabsnap/ghc $HOME/.ghc;
|
||||
cp -a $HOME/.cabsnap/lib $HOME/.cabsnap/share $HOME/.cabsnap/bin $HOME/.cabal/;
|
||||
else
|
||||
echo "cabal build-cache MISS";
|
||||
rm -rf $HOME/.cabsnap;
|
||||
mkdir -p $HOME/.ghc $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin;
|
||||
cabal install --only-dependencies --enable-tests --enable-benchmarks;
|
||||
fi
|
||||
|
||||
# snapshot package-db on cache miss
|
||||
- if [ ! -d $HOME/.cabsnap ];
|
||||
then
|
||||
echo "snapshotting package-db to build-cache";
|
||||
mkdir $HOME/.cabsnap;
|
||||
cp -a $HOME/.ghc $HOME/.cabsnap/ghc;
|
||||
cp -a $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin installplan.txt $HOME/.cabsnap/;
|
||||
fi
|
||||
|
||||
# Here starts the actual work to be performed for the package under test;
|
||||
# any command which exits with a non-zero exit code causes the build to fail.
|
||||
script:
|
||||
- if [ -f configure.ac ]; then autoreconf -i; fi
|
||||
- cabal configure --enable-tests --enable-benchmarks -v2 # -v2 provides useful information for debugging
|
||||
- cabal build # this builds all libraries and executables (including tests/benchmarks)
|
||||
- cabal test
|
||||
- cabal check
|
||||
- cabal sdist # tests that a source-distribution can be generated
|
||||
|
||||
# Check that the resulting source distribution can be built & installed.
|
||||
# If there are no other `.tar.gz` files in `dist`, this can be even simpler:
|
||||
# `cabal install --force-reinstalls dist/*-*.tar.gz`
|
||||
- SRC_TGZ=$(cabal info . | awk '{print $2;exit}').tar.gz &&
|
||||
(cd dist && cabal install --force-reinstalls "$SRC_TGZ")
|
||||
|
||||
# EOF
|
25
CHANGES.md
Normal file
25
CHANGES.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Change Log / Release Notes
|
||||
|
||||
## 0.12 (December 14, 2015)
|
||||
|
||||
* Compiles with GHC 7.10.2, 7.8.4, and 7.6.3
|
||||
|
||||
* Use of [data-default][] allows using `def` where previously you
|
||||
had to write `defaultConfig`, `defaultXPConfig`, etc.
|
||||
|
||||
* The [setlocale][] package is now used instead of a binding shipped
|
||||
with xmonad proper allowing the use of `Main.hs` instead of
|
||||
`Main.hsc`
|
||||
|
||||
* No longer encodes paths for `spawnPID`
|
||||
|
||||
* The default `manageHook` no longer floats Gimp windows
|
||||
|
||||
* Doesn't crash when there are fewer workspaces than screens
|
||||
|
||||
* `Query` is now an instance of `Applicative`
|
||||
|
||||
* Various improvements to the example configuration file
|
||||
|
||||
[data-default]: http://hackage.haskell.org/package/data-default
|
||||
[setlocale]: https://hackage.haskell.org/package/setlocale
|
2
CONFIG
2
CONFIG
@@ -21,7 +21,7 @@ $HOME/.xmonad/xmonad.hs :
|
||||
|
||||
import XMonad
|
||||
|
||||
main = xmonad $ defaultConfig
|
||||
main = xmonad $ def
|
||||
{ borderWidth = 2
|
||||
, terminal = "urxvt"
|
||||
, normalBorderColor = "#cccccc"
|
||||
|
90
Main.hs
90
Main.hs
@@ -16,93 +16,5 @@ module Main (main) where
|
||||
|
||||
import XMonad
|
||||
|
||||
import Control.Monad (unless)
|
||||
import System.IO
|
||||
import System.Info
|
||||
import System.Environment
|
||||
import System.Posix.Process (executeFile)
|
||||
import System.Exit (exitFailure)
|
||||
|
||||
import Paths_xmonad (version)
|
||||
import Data.Version (showVersion)
|
||||
|
||||
import Graphics.X11.Xinerama (compiledWithXinerama)
|
||||
|
||||
#ifdef TESTING
|
||||
import qualified Properties
|
||||
#endif
|
||||
|
||||
-- | The entry point into xmonad. Attempts to compile any custom main
|
||||
-- for xmonad, and if it doesn't find one, just launches the default.
|
||||
main :: IO ()
|
||||
main = do
|
||||
installSignalHandlers -- important to ignore SIGCHLD to avoid zombies
|
||||
args <- getArgs
|
||||
let launch = catchIO buildLaunch >> xmonad defaultConfig
|
||||
case args of
|
||||
[] -> launch
|
||||
("--resume":_) -> launch
|
||||
["--help"] -> usage
|
||||
["--recompile"] -> recompile True >>= flip unless exitFailure
|
||||
["--restart"] -> sendRestart >> return ()
|
||||
["--version"] -> putStrLn $ unwords shortVersion
|
||||
["--verbose-version"] -> putStrLn . unwords $ shortVersion ++ longVersion
|
||||
#ifdef TESTING
|
||||
("--run-tests":_) -> Properties.main
|
||||
#endif
|
||||
_ -> fail "unrecognized flags"
|
||||
where
|
||||
shortVersion = ["xmonad", showVersion version]
|
||||
longVersion = [ "compiled by", compilerName, showVersion compilerVersion
|
||||
, "for", arch ++ "-" ++ os
|
||||
, "\nXinerama:", show compiledWithXinerama ]
|
||||
|
||||
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" :
|
||||
" --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
|
||||
-- these cases:
|
||||
--
|
||||
-- * ghc missing
|
||||
--
|
||||
-- * both "~\/.xmonad\/xmonad.hs" and "~\/.xmonad\/xmonad-$arch-$os" missing
|
||||
--
|
||||
-- * xmonad.hs fails to compile
|
||||
--
|
||||
-- ** wrong ghc in path (fails to compile)
|
||||
--
|
||||
-- ** type error, syntax error, ..
|
||||
--
|
||||
-- * Missing XMonad\/XMonadContrib modules due to ghc upgrade
|
||||
--
|
||||
buildLaunch :: IO ()
|
||||
buildLaunch = do
|
||||
recompile False
|
||||
dir <- getXMonadDir
|
||||
args <- getArgs
|
||||
executeFile (dir ++ "/xmonad-"++arch++"-"++os) False args Nothing
|
||||
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
|
||||
main = xmonad def
|
||||
|
149
README
149
README
@@ -1,149 +0,0 @@
|
||||
xmonad : a tiling window manager
|
||||
|
||||
http://xmonad.org
|
||||
|
||||
xmonad is a tiling window manager for X. Windows are arranged
|
||||
automatically to tile the screen without gaps or overlap, maximising
|
||||
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.
|
||||
|
||||
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 is quite straightforward, and requires a basic Haskell toolchain.
|
||||
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
|
||||
in preference to a source build, as the dependency resolution will be
|
||||
simpler.
|
||||
|
||||
We'll now walk through the complete list of toolchain dependencies.
|
||||
|
||||
* GHC: the Glasgow Haskell Compiler
|
||||
|
||||
You first need a Haskell compiler. Your distribution's package
|
||||
system will have binaries of GHC (the Glasgow Haskell Compiler), the
|
||||
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
|
||||
them here:
|
||||
|
||||
http://haskell.org/ghc
|
||||
|
||||
For example, in Debian you would install GHC with:
|
||||
|
||||
apt-get install ghc6
|
||||
|
||||
It shouldn't be necessary to compile GHC from source -- every common
|
||||
system has a pre-build binary version.
|
||||
|
||||
* X11 libraries:
|
||||
|
||||
Since you're building an X application, you'll need the C X11
|
||||
library headers. On many platforms, these come pre-installed. For
|
||||
others, such as Debian, you can get them from your package manager:
|
||||
|
||||
apt-get install libx11-dev
|
||||
|
||||
Typically you need: libXinerama libXext libX11
|
||||
|
||||
* Cabal
|
||||
|
||||
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
|
||||
using GHC 6.6.x, you'll need to build and install Cabal from hackage
|
||||
first:
|
||||
|
||||
http://hackage.haskell.org/cgi-bin/hackage-scripts/package/Cabal
|
||||
|
||||
You can check which version you have with the command:
|
||||
|
||||
$ ghc-pkg list Cabal
|
||||
Cabal-1.2.2.0
|
||||
|
||||
* Haskell libraries: mtl, unix, X11
|
||||
|
||||
Finally, you need the Haskell libraries xmonad depends on. Since
|
||||
you've a working GHC installation now, most of these will be
|
||||
provided. To check whether you've got a package run 'ghc-pkg list
|
||||
some_package_name'. You will need the following packages:
|
||||
|
||||
mtl http://hackage.haskell.org/cgi-bin/hackage-scripts/package/mtl
|
||||
unix http://hackage.haskell.org/cgi-bin/hackage-scripts/package/unix
|
||||
X11 http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11
|
||||
|
||||
* Build xmonad:
|
||||
|
||||
Once you've got all the dependencies in place (which should be
|
||||
straightforward), build xmonad:
|
||||
|
||||
runhaskell Setup.lhs configure --user --prefix=$HOME
|
||||
runhaskell Setup.lhs build
|
||||
runhaskell Setup.lhs install --user
|
||||
|
||||
And you're done!
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Running xmonad:
|
||||
|
||||
Add:
|
||||
|
||||
$HOME/bin/xmonad
|
||||
|
||||
to the last line of your .xsession or .xinitrc file.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Configuring:
|
||||
|
||||
See the CONFIG document
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
XMonadContrib
|
||||
|
||||
There are many extensions to xmonad available in the XMonadContrib
|
||||
(xmc) library. Examples include an ion3-like tabbed layout, a
|
||||
prompt/program launcher, and various other useful modules.
|
||||
XMonadContrib is available at:
|
||||
|
||||
latest release: http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmonad-contrib
|
||||
|
||||
darcs version: darcs get http://code.haskell.org/XMonadContrib
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Other useful programs:
|
||||
|
||||
A nicer xterm replacement, that supports resizing better:
|
||||
|
||||
urxvt http://software.schmorp.de/pkg/rxvt-unicode.html
|
||||
|
||||
For custom status bars:
|
||||
|
||||
dzen http://gotmor.googlepages.com/dzen
|
||||
xmobar http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmobar
|
||||
|
||||
For a program dispatch menu:
|
||||
|
||||
dmenu http://www.suckless.org/download/
|
||||
gmrun (in your package system)
|
||||
|
||||
Authors:
|
||||
|
||||
Spencer Janssen
|
||||
Don Stewart
|
||||
Jason Creighton
|
117
README.md
Normal file
117
README.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# xmonad: A Tiling Window Manager
|
||||
|
||||
[xmonad][] is a tiling window manager for X. Windows are arranged
|
||||
automatically to tile the screen without gaps or overlap, maximising
|
||||
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.
|
||||
|
||||
## Quick Start
|
||||
|
||||
* From hackage:
|
||||
|
||||
cabal update
|
||||
cabal install xmonad xmonad-contrib
|
||||
|
||||
* Alternatively, build from source using the following repositories:
|
||||
|
||||
- <https://github.com/xmonad/xmonad>
|
||||
|
||||
- <https://github.com/xmonad/xmonad-contrib>
|
||||
|
||||
For the full story, read on.
|
||||
|
||||
## Building
|
||||
|
||||
Building is quite straightforward, and requires a basic Haskell toolchain.
|
||||
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
|
||||
in preference to a source build, as the dependency resolution will be
|
||||
simpler.
|
||||
|
||||
We'll now walk through the complete list of toolchain dependencies.
|
||||
|
||||
* GHC: the Glasgow Haskell Compiler
|
||||
|
||||
You first need a Haskell compiler. Your distribution's package
|
||||
system will have binaries of GHC (the Glasgow Haskell Compiler),
|
||||
the compiler we use, so install that first. If your operating
|
||||
system's package system doesn't provide a binary version of GHC
|
||||
and the `cabal-install` tool, you can install both using the
|
||||
[Haskell Platform][platform].
|
||||
|
||||
It shouldn't be necessary to compile GHC from source -- every common
|
||||
system has a pre-build binary version. However, if you want to
|
||||
build from source, the following links will be helpful:
|
||||
|
||||
- GHC: <http://haskell.org/ghc/>
|
||||
|
||||
- Cabal: <http://haskell.org/cabal/download.html>
|
||||
|
||||
* X11 libraries:
|
||||
|
||||
Since you're building an X application, you'll need the C X11
|
||||
library headers. On many platforms, these come pre-installed. For
|
||||
others, such as Debian, you can get them from your package manager:
|
||||
|
||||
$ apt-get install libx11-dev libxinerama-dev libxext-dev
|
||||
|
||||
## Running xmonad
|
||||
|
||||
Add:
|
||||
|
||||
exec $HOME/.cabal/bin/xmonad
|
||||
|
||||
to the last line of your `.xsession` or `.xinitrc` file.
|
||||
|
||||
## Configuring
|
||||
|
||||
See the `CONFIG` document.
|
||||
|
||||
## XMonadContrib
|
||||
|
||||
There are many extensions to xmonad available in the XMonadContrib
|
||||
(xmc) library. Examples include an ion3-like tabbed layout, a
|
||||
prompt/program launcher, and various other useful modules.
|
||||
XMonadContrib is available at:
|
||||
|
||||
* Latest release: <http://hackage.haskell.org/package/xmonad-contrib>
|
||||
|
||||
* Git version: <https://github.com/xmonad/xmonad-contrib>
|
||||
|
||||
## Other Useful Programs
|
||||
|
||||
A nicer xterm replacement, that supports resizing better:
|
||||
|
||||
* urxvt: <http://software.schmorp.de/pkg/rxvt-unicode.html>
|
||||
|
||||
For custom status bars:
|
||||
|
||||
* xmobar: <http://hackage.haskell.org/package/xmobar>
|
||||
|
||||
* taffybar: <https://github.com/travitch/taffybar>
|
||||
|
||||
* dzen: <http://gotmor.googlepages.com/dzen>
|
||||
|
||||
For a program dispatch menu:
|
||||
|
||||
* [XMonad.Prompt.Shell][xmc-prompt-shell]: (from [XMonadContrib][])
|
||||
|
||||
* dmenu: <http://www.suckless.org/download/>
|
||||
|
||||
* gmrun: (in your package system)
|
||||
|
||||
## Authors
|
||||
|
||||
* Spencer Janssen
|
||||
* Don Stewart
|
||||
* Jason Creighton
|
||||
|
||||
[xmonad]: http://xmonad.org
|
||||
[xmonadcontrib]: https://hackage.haskell.org/package/xmonad-contrib
|
||||
[xmc-prompt-shell]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Prompt-Shell.html
|
||||
[platform]: http://haskell.org/platform/
|
5
STYLE
5
STYLE
@@ -7,12 +7,13 @@
|
||||
|
||||
* Follow the coding style of the other modules.
|
||||
|
||||
* Code should be compilable with -Wall -Werror. There should be no warnings.
|
||||
* Code should be compilable with -Wall -Werror -fno-warn-unused-do-bind -fwarn-tabs.
|
||||
There should be no warnings.
|
||||
|
||||
* Partial functions should be avoided: the window manager should not
|
||||
crash, so do not call `error` or `undefined`
|
||||
|
||||
* Tabs are illegal. Use 4 spaces for indenting.
|
||||
* Use 4 spaces for indenting.
|
||||
|
||||
* Any pure function added to the core should have QuickCheck properties
|
||||
precisely defining its behavior.
|
||||
|
17
TODO
17
TODO
@@ -1,21 +1,14 @@
|
||||
- Write down invariants for the window life cycle, especially:
|
||||
- When are borders set? Prove that the current handling is sufficient.
|
||||
|
||||
- current floating layer handling is nonoptimal. FocusUp should raise,
|
||||
for example
|
||||
|
||||
- Issues still with stacking order.
|
||||
|
||||
= Release management =
|
||||
|
||||
* configuration documentation
|
||||
|
||||
* generate haddocks for core and XMC, upload to xmonad.org
|
||||
* generate, and push website haddocks with xmonad-web/gen-docs.sh
|
||||
* generate manpage, generate html manpage
|
||||
* double check README build instructions
|
||||
* test core with 6.6 and 6.8
|
||||
* bump xmonad.cabal version and X11 version
|
||||
* update cabal "tested-with:" fields
|
||||
* upload X11 and xmonad to Hackage
|
||||
* update #xmonad topic
|
||||
* check examples/text in user-facing Config.hs
|
||||
* check tour.html and intro.html are up to date, and mention all core bindings
|
||||
* confirm template config is type correct
|
||||
* update haskellwiki notable changes since x.x
|
||||
* email announce
|
||||
|
71
man/HCAR.tex
Normal file
71
man/HCAR.tex
Normal file
@@ -0,0 +1,71 @@
|
||||
% xmonad-Gx.tex
|
||||
\begin{hcarentry}{xmonad}
|
||||
\label{xmonad}
|
||||
\report{Gwern Branwen}%11/11
|
||||
\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; XMonad founder Don Stewart
|
||||
has stepped down and Adam Vogt is the new maintainer.
|
||||
After gestating for 2 years, version 0.10 has been released, with simultaneous
|
||||
releases of the XMonadContrib library of customizations (which has now grown to
|
||||
no less than 216 modules encompassing a dizzying array of features) and the
|
||||
xmonad-extras package of extensions,
|
||||
|
||||
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.8}
|
||||
\item \url{http://haskell.org/haskellwiki/Xmonad/Notable_changes_since_0.9}
|
||||
% \item \url{http://haskell.org/haskellwiki/Xmonad/Notable_changes_since_0.10}
|
||||
\item the Darcs repositories have been upgraded to the hashed format
|
||||
\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 Git source:
|
||||
|
||||
\texttt{git clone} \url{https://github.com/xmonad/xmonad.git}
|
||||
|
||||
\item IRC channel:
|
||||
\verb+#xmonad @@ irc.freenode.org+
|
||||
|
||||
\item Mailing list:
|
||||
\email{xmonad@@haskell.org}
|
||||
\end{compactitem}
|
||||
\end{hcarentry}
|
282
man/xmonad.1
Normal file
282
man/xmonad.1
Normal file
@@ -0,0 +1,282 @@
|
||||
.TH xmonad 1 "31 December 2012" xmonad-0.12 "xmonad manual".\" Automatically generated by Pandoc 1.15.1
|
||||
.\"
|
||||
.hy
|
||||
.TH "" "" "" "" ""
|
||||
.SH Name
|
||||
.PP
|
||||
xmonad \- a tiling window manager
|
||||
.SH Description
|
||||
.PP
|
||||
\f[I]xmonad\f[] 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.
|
||||
\f[I]xmonad\f[] is configured in Haskell, and custom layout algorithms
|
||||
may be implemented by the user in config files.
|
||||
A principle of \f[I]xmonad\f[] is predictability: the user should know
|
||||
in advance precisely the window arrangement that will result from any
|
||||
action.
|
||||
.PP
|
||||
By default, \f[I]xmonad\f[] 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.
|
||||
.PP
|
||||
By utilizing the expressivity of a modern functional language with a
|
||||
rich static type system, \f[I]xmonad\f[] 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.
|
||||
.SH Usage
|
||||
.PP
|
||||
\f[I]xmonad\f[] 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 \f[I]xmonad\f[] 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.
|
||||
.SS Flags
|
||||
.PP
|
||||
xmonad has several flags which you may pass to the executable.
|
||||
These flags are:
|
||||
.TP
|
||||
.B \-\-recompile
|
||||
Recompiles your configuration in \f[I]~/.xmonad/xmonad.hs\f[]
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B \-\-restart
|
||||
Causes the currently running \f[I]xmonad\f[] process to restart
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B \-\-replace
|
||||
Replace the current window manager with xmonad
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B \-\-version
|
||||
Display version of \f[I]xmonad\f[]
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B \-\-verbose\-version
|
||||
Display detailed version of \f[I]xmonad\f[]
|
||||
.RS
|
||||
.RE
|
||||
.SS Default keyboard bindings
|
||||
.TP
|
||||
.B mod\-shift\-return
|
||||
Launch terminal
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-p
|
||||
Launch dmenu
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-p
|
||||
Launch gmrun
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-c
|
||||
Close the focused window
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-space
|
||||
Rotate through the available layout algorithms
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-space
|
||||
Reset the layouts on the current workspace to default
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-n
|
||||
Resize viewed windows to the correct size
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-tab
|
||||
Move focus to the next window
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-tab
|
||||
Move focus to the previous window
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-j
|
||||
Move focus to the next window
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-k
|
||||
Move focus to the previous window
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-m
|
||||
Move focus to the master window
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-return
|
||||
Swap the focused window and the master window
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-j
|
||||
Swap the focused window with the next window
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-k
|
||||
Swap the focused window with the previous window
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-h
|
||||
Shrink the master area
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-l
|
||||
Expand the master area
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-t
|
||||
Push window back into tiling
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-comma
|
||||
Increment the number of windows in the master area
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-period
|
||||
Deincrement the number of windows in the master area
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-q
|
||||
Quit xmonad
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-q
|
||||
Restart xmonad
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-slash
|
||||
Run xmessage with a summary of the default keybindings (useful for
|
||||
beginners)
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-[1..9]
|
||||
Switch to workspace N
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-[1..9]
|
||||
Move client to workspace N
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-{w,e,r}
|
||||
Switch to physical/Xinerama screens 1, 2, or 3
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-{w,e,r}
|
||||
Move client to screen 1, 2, or 3
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-button1
|
||||
Set the window to floating mode and move by dragging
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-button2
|
||||
Raise the window to the top of the stack
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-button3
|
||||
Set the window to floating mode and resize by dragging
|
||||
.RS
|
||||
.RE
|
||||
.SH Examples
|
||||
.PP
|
||||
To use xmonad as your window manager add to your \f[I]~/.xinitrc\f[]
|
||||
file:
|
||||
.RS
|
||||
.PP
|
||||
exec xmonad
|
||||
.RE
|
||||
.SH Customization
|
||||
.PP
|
||||
xmonad is customized in ~/.xmonad/xmonad.hs, and then restarted with
|
||||
mod\-q.
|
||||
.PP
|
||||
You can find many extensions to the core feature set in the xmonad\-
|
||||
contrib package, available through your package manager or from
|
||||
xmonad.org (http://xmonad.org).
|
||||
.SS Modular Configuration
|
||||
.PP
|
||||
As of \f[I]xmonad\-0.9\f[], any additional Haskell modules may be placed
|
||||
in \f[I]~/.xmonad/lib/\f[] are available in GHC\[aq]s searchpath.
|
||||
Hierarchical modules are supported: for example, the file
|
||||
\f[I]~/.xmonad/lib/XMonad/Stack/MyAdditions.hs\f[] could contain:
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
module\ XMonad.Stack.MyAdditions\ (function1)\ where
|
||||
\ \ function1\ =\ error\ "function1:\ Not\ implemented\ yet!"
|
||||
\f[]
|
||||
.fi
|
||||
.PP
|
||||
Your xmonad.hs may then import XMonad.Stack.MyAdditions as if that
|
||||
module was contained within xmonad or xmonad\-contrib.
|
||||
.SH Bugs
|
||||
.PP
|
||||
Probably.
|
||||
If you find any, please report them to the
|
||||
bugtracker (https://github.com/xmonad/xmonad/issues)
|
165
man/xmonad.1.html
Normal file
165
man/xmonad.1.html
Normal file
@@ -0,0 +1,165 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="Content-Style-Type" content="text/css" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<title></title>
|
||||
<style type="text/css">code{white-space: pre;}</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>xmonad-0.12</h1><p>Section: xmonad manual (1)<br/>Updated: 31 December 2012</p><hr/>
|
||||
<div id="TOC">
|
||||
<ul>
|
||||
<li><a href="#name">Name</a></li>
|
||||
<li><a href="#description">Description</a></li>
|
||||
<li><a href="#usage">Usage</a><ul>
|
||||
<li><a href="#flags">Flags</a></li>
|
||||
<li><a href="#default-keyboard-bindings">Default keyboard bindings</a></li>
|
||||
</ul></li>
|
||||
<li><a href="#examples">Examples</a></li>
|
||||
<li><a href="#customization">Customization</a><ul>
|
||||
<li><a href="#modular-configuration">Modular Configuration</a></li>
|
||||
</ul></li>
|
||||
<li><a href="#bugs">Bugs</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<h1 id="name">Name</h1>
|
||||
<p>xmonad - a tiling window manager</p>
|
||||
<h1 id="description">Description</h1>
|
||||
<p><em>xmonad</em> 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. <em>xmonad</em> is configured in Haskell, and custom layout algorithms may be implemented by the user in config files. A principle of <em>xmonad</em> is predictability: the user should know in advance precisely the window arrangement that will result from any action.</p>
|
||||
<p>By default, <em>xmonad</em> 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.</p>
|
||||
<p>By utilizing the expressivity of a modern functional language with a rich static type system, <em>xmonad</em> 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.</p>
|
||||
<h1 id="usage">Usage</h1>
|
||||
<p><em>xmonad</em> 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.</p>
|
||||
<p>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.</p>
|
||||
<p>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 <em>xmonad</em> 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.</p>
|
||||
<h2 id="flags">Flags</h2>
|
||||
<p>xmonad has several flags which you may pass to the executable. These flags are:</p>
|
||||
<dl>
|
||||
<dt>--recompile</dt>
|
||||
<dd>Recompiles your configuration in <em>~/.xmonad/xmonad.hs</em>
|
||||
</dd>
|
||||
<dt>--restart</dt>
|
||||
<dd>Causes the currently running <em>xmonad</em> process to restart
|
||||
</dd>
|
||||
<dt>--replace</dt>
|
||||
<dd>Replace the current window manager with xmonad
|
||||
</dd>
|
||||
<dt>--version</dt>
|
||||
<dd>Display version of <em>xmonad</em>
|
||||
</dd>
|
||||
<dt>--verbose-version</dt>
|
||||
<dd>Display detailed version of <em>xmonad</em>
|
||||
</dd>
|
||||
</dl>
|
||||
<h2 id="default-keyboard-bindings">Default keyboard bindings</h2>
|
||||
<dl>
|
||||
<dt>mod-shift-return</dt>
|
||||
<dd>Launch terminal
|
||||
</dd>
|
||||
<dt>mod-p</dt>
|
||||
<dd>Launch dmenu
|
||||
</dd>
|
||||
<dt>mod-shift-p</dt>
|
||||
<dd>Launch gmrun
|
||||
</dd>
|
||||
<dt>mod-shift-c</dt>
|
||||
<dd>Close the focused window
|
||||
</dd>
|
||||
<dt>mod-space</dt>
|
||||
<dd>Rotate through the available layout algorithms
|
||||
</dd>
|
||||
<dt>mod-shift-space</dt>
|
||||
<dd>Reset the layouts on the current workspace to default
|
||||
</dd>
|
||||
<dt>mod-n</dt>
|
||||
<dd>Resize viewed windows to the correct size
|
||||
</dd>
|
||||
<dt>mod-tab</dt>
|
||||
<dd>Move focus to the next window
|
||||
</dd>
|
||||
<dt>mod-shift-tab</dt>
|
||||
<dd>Move focus to the previous window
|
||||
</dd>
|
||||
<dt>mod-j</dt>
|
||||
<dd>Move focus to the next window
|
||||
</dd>
|
||||
<dt>mod-k</dt>
|
||||
<dd>Move focus to the previous window
|
||||
</dd>
|
||||
<dt>mod-m</dt>
|
||||
<dd>Move focus to the master window
|
||||
</dd>
|
||||
<dt>mod-return</dt>
|
||||
<dd>Swap the focused window and the master window
|
||||
</dd>
|
||||
<dt>mod-shift-j</dt>
|
||||
<dd>Swap the focused window with the next window
|
||||
</dd>
|
||||
<dt>mod-shift-k</dt>
|
||||
<dd>Swap the focused window with the previous window
|
||||
</dd>
|
||||
<dt>mod-h</dt>
|
||||
<dd>Shrink the master area
|
||||
</dd>
|
||||
<dt>mod-l</dt>
|
||||
<dd>Expand the master area
|
||||
</dd>
|
||||
<dt>mod-t</dt>
|
||||
<dd>Push window back into tiling
|
||||
</dd>
|
||||
<dt>mod-comma</dt>
|
||||
<dd>Increment the number of windows in the master area
|
||||
</dd>
|
||||
<dt>mod-period</dt>
|
||||
<dd>Deincrement the number of windows in the master area
|
||||
</dd>
|
||||
<dt>mod-shift-q</dt>
|
||||
<dd>Quit xmonad
|
||||
</dd>
|
||||
<dt>mod-q</dt>
|
||||
<dd>Restart xmonad
|
||||
</dd>
|
||||
<dt>mod-shift-slash</dt>
|
||||
<dd>Run xmessage with a summary of the default keybindings (useful for beginners)
|
||||
</dd>
|
||||
<dt>mod-[1..9]</dt>
|
||||
<dd>Switch to workspace N
|
||||
</dd>
|
||||
<dt>mod-shift-[1..9]</dt>
|
||||
<dd>Move client to workspace N
|
||||
</dd>
|
||||
<dt>mod-{w,e,r}</dt>
|
||||
<dd>Switch to physical/Xinerama screens 1, 2, or 3
|
||||
</dd>
|
||||
<dt>mod-shift-{w,e,r}</dt>
|
||||
<dd>Move client to screen 1, 2, or 3
|
||||
</dd>
|
||||
<dt>mod-button1</dt>
|
||||
<dd>Set the window to floating mode and move by dragging
|
||||
</dd>
|
||||
<dt>mod-button2</dt>
|
||||
<dd>Raise the window to the top of the stack
|
||||
</dd>
|
||||
<dt>mod-button3</dt>
|
||||
<dd>Set the window to floating mode and resize by dragging
|
||||
</dd>
|
||||
</dl>
|
||||
<h1 id="examples">Examples</h1>
|
||||
<p>To use xmonad as your window manager add to your <em>~/.xinitrc</em> file:</p>
|
||||
<blockquote>
|
||||
<p>exec xmonad</p>
|
||||
</blockquote>
|
||||
<h1 id="customization">Customization</h1>
|
||||
<p>xmonad is customized in ~/.xmonad/xmonad.hs, and then restarted with mod-q.</p>
|
||||
<p>You can find many extensions to the core feature set in the xmonad- contrib package, available through your package manager or from <a href="http://xmonad.org">xmonad.org</a>.</p>
|
||||
<h2 id="modular-configuration">Modular Configuration</h2>
|
||||
<p>As of <em>xmonad-0.9</em>, any additional Haskell modules may be placed in <em>~/.xmonad/lib/</em> are available in GHC's searchpath. Hierarchical modules are supported: for example, the file <em>~/.xmonad/lib/XMonad/Stack/MyAdditions.hs</em> could contain:</p>
|
||||
<pre class="haskell"><code>module XMonad.Stack.MyAdditions (function1) where
|
||||
function1 = error "function1: Not implemented yet!"</code></pre>
|
||||
<p>Your xmonad.hs may then import XMonad.Stack.MyAdditions as if that module was contained within xmonad or xmonad-contrib.</p>
|
||||
<h1 id="bugs">Bugs</h1>
|
||||
<p>Probably. If you find any, please report them to the <a href="https://github.com/xmonad/xmonad/issues">bugtracker</a></p>
|
||||
</body>
|
||||
</html>
|
@@ -57,6 +57,9 @@ These flags are:
|
||||
--restart
|
||||
: Causes the currently running _xmonad_ process to restart
|
||||
|
||||
--replace
|
||||
: Replace the current window manager with xmonad
|
||||
|
||||
--version
|
||||
: Display version of _xmonad_
|
||||
|
||||
@@ -73,7 +76,7 @@ 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
|
||||
xmonad is customized in ~/.xmonad/xmonad.hs, and then restarted
|
||||
with mod-q.
|
||||
|
||||
You can find many extensions to the core feature set in the xmonad-
|
||||
@@ -86,8 +89,10 @@ _~/.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!"
|
||||
```haskell
|
||||
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.
|
||||
@@ -96,4 +101,4 @@ module was contained within xmonad or xmonad-contrib.
|
||||
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
|
||||
[bugtracker]: https://github.com/xmonad/xmonad/issues
|
||||
|
@@ -23,6 +23,10 @@ myTerminal = "xterm"
|
||||
myFocusFollowsMouse :: Bool
|
||||
myFocusFollowsMouse = True
|
||||
|
||||
-- Whether clicking on a window to focus also passes the click to the window
|
||||
myClickJustFocuses :: Bool
|
||||
myClickJustFocuses = False
|
||||
|
||||
-- Width of the window border in pixels.
|
||||
--
|
||||
myBorderWidth = 1
|
||||
@@ -59,7 +63,7 @@ myKeys conf@(XConfig {XMonad.modMask = modm}) = M.fromList $
|
||||
[ ((modm .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf)
|
||||
|
||||
-- launch dmenu
|
||||
, ((modm, xK_p ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"")
|
||||
, ((modm, xK_p ), spawn "dmenu_run")
|
||||
|
||||
-- launch gmrun
|
||||
, ((modm .|. shiftMask, xK_p ), spawn "gmrun")
|
||||
@@ -123,6 +127,9 @@ myKeys conf@(XConfig {XMonad.modMask = modm}) = M.fromList $
|
||||
|
||||
-- Restart xmonad
|
||||
, ((modm , xK_q ), spawn "xmonad --recompile; xmonad --restart")
|
||||
|
||||
-- Run xmessage with a summary of the default keybindings (useful for beginners)
|
||||
, ((modm .|. shiftMask, xK_slash ), spawn ("echo \"" ++ help ++ "\" | xmessage -file -"))
|
||||
]
|
||||
++
|
||||
|
||||
@@ -251,10 +258,11 @@ main = xmonad defaults
|
||||
--
|
||||
-- No need to modify this.
|
||||
--
|
||||
defaults = defaultConfig {
|
||||
defaults = def {
|
||||
-- simple stuff
|
||||
terminal = myTerminal,
|
||||
focusFollowsMouse = myFocusFollowsMouse,
|
||||
clickJustFocuses = myClickJustFocuses,
|
||||
borderWidth = myBorderWidth,
|
||||
modMask = myModMask,
|
||||
workspaces = myWorkspaces,
|
||||
@@ -272,3 +280,54 @@ defaults = defaultConfig {
|
||||
logHook = myLogHook,
|
||||
startupHook = myStartupHook
|
||||
}
|
||||
|
||||
-- | Finally, a copy of the default bindings in simple textual tabular format.
|
||||
help :: String
|
||||
help = unlines ["The default modifier key is 'alt'. Default keybindings:",
|
||||
"",
|
||||
"-- launching and killing programs",
|
||||
"mod-Shift-Enter Launch xterminal",
|
||||
"mod-p Launch dmenu",
|
||||
"mod-Shift-p Launch gmrun",
|
||||
"mod-Shift-c Close/kill the focused window",
|
||||
"mod-Space Rotate through the available layout algorithms",
|
||||
"mod-Shift-Space Reset the layouts on the current workSpace to default",
|
||||
"mod-n Resize/refresh viewed windows to the correct size",
|
||||
"",
|
||||
"-- move focus up or down the window stack",
|
||||
"mod-Tab Move focus to the next window",
|
||||
"mod-Shift-Tab Move focus to the previous window",
|
||||
"mod-j Move focus to the next window",
|
||||
"mod-k Move focus to the previous window",
|
||||
"mod-m Move focus to the master window",
|
||||
"",
|
||||
"-- modifying the window order",
|
||||
"mod-Return Swap the focused window and the master window",
|
||||
"mod-Shift-j Swap the focused window with the next window",
|
||||
"mod-Shift-k Swap the focused window with the previous window",
|
||||
"",
|
||||
"-- resizing the master/slave ratio",
|
||||
"mod-h Shrink the master area",
|
||||
"mod-l Expand the master area",
|
||||
"",
|
||||
"-- floating layer support",
|
||||
"mod-t Push window back into tiling; unfloat and re-tile it",
|
||||
"",
|
||||
"-- increase or decrease number of windows in the master area",
|
||||
"mod-comma (mod-,) Increment the number of windows in the master area",
|
||||
"mod-period (mod-.) Deincrement the number of windows in the master area",
|
||||
"",
|
||||
"-- quit, or restart",
|
||||
"mod-Shift-q Quit xmonad",
|
||||
"mod-q Restart xmonad",
|
||||
"mod-[1..9] Switch to workSpace N",
|
||||
"",
|
||||
"-- Workspaces & screens",
|
||||
"mod-Shift-[1..9] Move client to workspace N",
|
||||
"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",
|
||||
"",
|
||||
"-- Mouse bindings: default actions bound to mouse events",
|
||||
"mod-button1 Set the window to floating mode and move by dragging",
|
||||
"mod-button2 Raise the window to the top of the stack",
|
||||
"mod-button3 Set the window to floating mode and resize by dragging"]
|
||||
|
@@ -1,4 +1,5 @@
|
||||
{-# OPTIONS -fno-warn-missing-signatures #-}
|
||||
{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-orphans #-}
|
||||
{-# LANGUAGE TypeFamilies #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Config
|
||||
@@ -13,13 +14,13 @@
|
||||
--
|
||||
-- DO NOT MODIFY THIS FILE! It won't work. You may configure xmonad
|
||||
-- by providing your own @~\/.xmonad\/xmonad.hs@ that overrides
|
||||
-- specific fields in 'defaultConfig'. For a starting point, you can
|
||||
-- specific fields in the default config, 'def'. For a starting point, you can
|
||||
-- copy the @xmonad.hs@ found in the @man@ directory, or look at
|
||||
-- examples on the xmonad wiki.
|
||||
--
|
||||
------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Config (defaultConfig) where
|
||||
module XMonad.Config (defaultConfig, Default(..)) where
|
||||
|
||||
--
|
||||
-- Useful imports
|
||||
@@ -27,17 +28,18 @@ module XMonad.Config (defaultConfig) where
|
||||
import XMonad.Core as XMonad hiding
|
||||
(workspaces,manageHook,keys,logHook,startupHook,borderWidth,mouseBindings
|
||||
,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor,focusFollowsMouse
|
||||
,handleEventHook)
|
||||
,handleEventHook,clickJustFocuses,rootMask,clientMask)
|
||||
import qualified XMonad.Core as XMonad
|
||||
(workspaces,manageHook,keys,logHook,startupHook,borderWidth,mouseBindings
|
||||
,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor,focusFollowsMouse
|
||||
,handleEventHook)
|
||||
,handleEventHook,clickJustFocuses,rootMask,clientMask)
|
||||
|
||||
import XMonad.Layout
|
||||
import XMonad.Operations
|
||||
import XMonad.ManageHook
|
||||
import qualified XMonad.StackSet as W
|
||||
import Data.Bits ((.|.))
|
||||
import Data.Default
|
||||
import Data.Monoid
|
||||
import qualified Data.Map as M
|
||||
import System.Exit
|
||||
@@ -90,7 +92,7 @@ focusedBorderColor = "red" -- "#ff0000" don't use hex, not <24 bit safe
|
||||
manageHook :: ManageHook
|
||||
manageHook = composeAll
|
||||
[ className =? "MPlayer" --> doFloat
|
||||
, className =? "Gimp" --> doFloat ]
|
||||
, className =? "mplayer2" --> doFloat ]
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Logging
|
||||
@@ -145,6 +147,19 @@ layout = tiled ||| Mirror tiled ||| Full
|
||||
-- Percent of screen to increment by when resizing panes
|
||||
delta = 3/100
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Event Masks:
|
||||
|
||||
-- | The client events that xmonad is interested in
|
||||
clientMask :: EventMask
|
||||
clientMask = structureNotifyMask .|. enterWindowMask .|. propertyChangeMask
|
||||
|
||||
-- | The root events that xmonad is interested in
|
||||
rootMask :: EventMask
|
||||
rootMask = substructureRedirectMask .|. substructureNotifyMask
|
||||
.|. enterWindowMask .|. leaveWindowMask .|. structureNotifyMask
|
||||
.|. buttonPressMask
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Key bindings:
|
||||
|
||||
@@ -157,6 +172,11 @@ terminal = "xterm"
|
||||
focusFollowsMouse :: Bool
|
||||
focusFollowsMouse = True
|
||||
|
||||
-- | Whether a mouse click select the focus or is just passed to the window
|
||||
clickJustFocuses :: Bool
|
||||
clickJustFocuses = True
|
||||
|
||||
|
||||
-- | The xmonad key bindings. Add, modify or remove key bindings here.
|
||||
--
|
||||
-- (The comment formatting character is used when generating the manpage)
|
||||
@@ -165,7 +185,7 @@ keys :: XConfig Layout -> M.Map (KeyMask, KeySym) (X ())
|
||||
keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
||||
-- launching and killing programs
|
||||
[ ((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_c ), kill) -- %! Close the focused window
|
||||
|
||||
@@ -197,12 +217,13 @@ keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
||||
, ((modMask , xK_comma ), sendMessage (IncMasterN 1)) -- %! Increment 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
|
||||
--, ((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
|
||||
, ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess)) -- %! Quit xmonad
|
||||
, ((modMask , xK_q ), spawn "xmonad --recompile && xmonad --restart") -- %! Restart xmonad
|
||||
, ((modMask , xK_q ), spawn "if type xmonad; then xmonad --recompile && xmonad --restart; else xmessage xmonad not in \\$PATH: \"$PATH\"; fi") -- %! Restart xmonad
|
||||
|
||||
, ((modMask .|. shiftMask, xK_slash ), spawn ("echo \"" ++ help ++ "\" | xmessage -file -")) -- %! Run xmessage with a summary of the default keybindings (useful for beginners)
|
||||
-- repeat the binding for non-American layout keyboards
|
||||
, ((modMask , xK_question), spawn ("echo \"" ++ help ++ "\" | xmessage -file -"))
|
||||
]
|
||||
++
|
||||
-- mod-[1..9] %! Switch to workspace N
|
||||
@@ -218,22 +239,21 @@ keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
||||
, (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
|
||||
|
||||
-- | Mouse bindings: default actions bound to mouse events
|
||||
--
|
||||
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
|
||||
[ ((modMask, button1), (\w -> focus w >> mouseMoveWindow w
|
||||
>> windows W.shiftMaster))
|
||||
[ ((modMask, button1), \w -> focus w >> mouseMoveWindow w
|
||||
>> windows W.shiftMaster)
|
||||
-- mod-button2 %! Raise the window to the top of the stack
|
||||
, ((modMask, button2), (\w -> focus w >> windows W.shiftMaster))
|
||||
, ((modMask, button2), windows . (W.shiftMaster .) . W.focusWindow)
|
||||
-- mod-button3 %! Set the window to floating mode and resize by dragging
|
||||
, ((modMask, button3), (\w -> focus w >> mouseResizeWindow w
|
||||
>> windows W.shiftMaster))
|
||||
, ((modMask, button3), \w -> focus w >> mouseResizeWindow w
|
||||
>> windows W.shiftMaster)
|
||||
-- you may also bind events to the mouse scroll wheel (button4 and button5)
|
||||
]
|
||||
|
||||
-- | And, finally, the default set of configuration values itself
|
||||
defaultConfig = XConfig
|
||||
instance (a ~ Choose Tall (Choose (Mirror Tall) Full)) => Default (XConfig a) where
|
||||
def = XConfig
|
||||
{ XMonad.borderWidth = borderWidth
|
||||
, XMonad.workspaces = workspaces
|
||||
, XMonad.layoutHook = layout
|
||||
@@ -248,4 +268,66 @@ defaultConfig = XConfig
|
||||
, XMonad.manageHook = manageHook
|
||||
, XMonad.handleEventHook = handleEventHook
|
||||
, XMonad.focusFollowsMouse = focusFollowsMouse
|
||||
}
|
||||
, XMonad.clickJustFocuses = clickJustFocuses
|
||||
, XMonad.clientMask = clientMask
|
||||
, XMonad.rootMask = rootMask
|
||||
, XMonad.handleExtraArgs = \ xs theConf -> case xs of
|
||||
[] -> return theConf
|
||||
_ -> fail ("unrecognized flags:" ++ show xs)
|
||||
}
|
||||
|
||||
-- | The default set of configuration values itself
|
||||
{-# DEPRECATED defaultConfig "Use def (from Data.Default, and re-exported by XMonad and XMonad.Config) instead." #-}
|
||||
defaultConfig :: XConfig (Choose Tall (Choose (Mirror Tall) Full))
|
||||
defaultConfig = def
|
||||
|
||||
-- | Finally, a copy of the default bindings in simple textual tabular format.
|
||||
help :: String
|
||||
help = unlines ["The default modifier key is 'alt'. Default keybindings:",
|
||||
"",
|
||||
"-- launching and killing programs",
|
||||
"mod-Shift-Enter Launch xterminal",
|
||||
"mod-p Launch dmenu",
|
||||
"mod-Shift-p Launch gmrun",
|
||||
"mod-Shift-c Close/kill the focused window",
|
||||
"mod-Space Rotate through the available layout algorithms",
|
||||
"mod-Shift-Space Reset the layouts on the current workSpace to default",
|
||||
"mod-n Resize/refresh viewed windows to the correct size",
|
||||
"",
|
||||
"-- move focus up or down the window stack",
|
||||
"mod-Tab Move focus to the next window",
|
||||
"mod-Shift-Tab Move focus to the previous window",
|
||||
"mod-j Move focus to the next window",
|
||||
"mod-k Move focus to the previous window",
|
||||
"mod-m Move focus to the master window",
|
||||
"",
|
||||
"-- modifying the window order",
|
||||
"mod-Return Swap the focused window and the master window",
|
||||
"mod-Shift-j Swap the focused window with the next window",
|
||||
"mod-Shift-k Swap the focused window with the previous window",
|
||||
"",
|
||||
"-- resizing the master/slave ratio",
|
||||
"mod-h Shrink the master area",
|
||||
"mod-l Expand the master area",
|
||||
"",
|
||||
"-- floating layer support",
|
||||
"mod-t Push window back into tiling; unfloat and re-tile it",
|
||||
"",
|
||||
"-- increase or decrease number of windows in the master area",
|
||||
"mod-comma (mod-,) Increment the number of windows in the master area",
|
||||
"mod-period (mod-.) Deincrement the number of windows in the master area",
|
||||
"",
|
||||
"-- quit, or restart",
|
||||
"mod-Shift-q Quit xmonad",
|
||||
"mod-q Restart xmonad",
|
||||
"mod-[1..9] Switch to workSpace N",
|
||||
"",
|
||||
"-- Workspaces & screens",
|
||||
"mod-Shift-[1..9] Move client to workspace N",
|
||||
"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",
|
||||
"",
|
||||
"-- Mouse bindings: default actions bound to mouse events",
|
||||
"mod-button1 Set the window to floating mode and move by dragging",
|
||||
"mod-button2 Raise the window to the top of the stack",
|
||||
"mod-button3 Set the window to floating mode and resize by dragging"]
|
@@ -1,7 +1,5 @@
|
||||
{-# LANGUAGE ExistentialQuantification, FlexibleInstances, GeneralizedNewtypeDeriving,
|
||||
MultiParamTypeClasses, TypeSynonymInstances, CPP #-}
|
||||
-- required for deriving Typeable
|
||||
{-# OPTIONS_GHC -fglasgow-exts #-}
|
||||
MultiParamTypeClasses, TypeSynonymInstances, CPP, DeriveDataTypeable #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
@@ -27,17 +25,19 @@ module XMonad.Core (
|
||||
StateExtension(..), ExtensionClass(..),
|
||||
runX, catchX, userCode, userCodeDef, io, catchIO, installSignalHandlers, uninstallSignalHandlers,
|
||||
withDisplay, withWindowSet, isRoot, runOnWorkspaces,
|
||||
getAtom, spawn, spawnPID, getXMonadDir, recompile, trace, whenJust, whenX,
|
||||
atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, ManageHook, Query(..), runQuery
|
||||
getAtom, spawn, spawnPID, xfork, getXMonadDir, recompile, trace, whenJust, whenX,
|
||||
atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, atom_WM_TAKE_FOCUS, ManageHook, Query(..), runQuery
|
||||
) where
|
||||
|
||||
import XMonad.StackSet hiding (modify)
|
||||
|
||||
import Prelude hiding ( catch )
|
||||
import Control.Exception (catch, try, bracket, throw, finally, Exception(ExitException))
|
||||
import Prelude
|
||||
import Control.Exception.Extensible (fromException, try, bracket, throw, finally, SomeException(..))
|
||||
import qualified Control.Exception.Extensible as E
|
||||
import Control.Applicative
|
||||
import Control.Monad.State
|
||||
import Control.Monad.Reader
|
||||
import Data.Default
|
||||
import System.FilePath
|
||||
import System.IO
|
||||
import System.Info
|
||||
@@ -64,11 +64,11 @@ data XState = XState
|
||||
, mapped :: !(S.Set Window) -- ^ the Set of mapped windows
|
||||
, waitingUnmap :: !(M.Map Window Int) -- ^ the number of expected UnmapEvents
|
||||
, dragging :: !(Maybe (Position -> Position -> X (), X ()))
|
||||
, numlockMask :: !KeyMask -- ^ The numlock modifier
|
||||
, numberlockMask :: !KeyMask -- ^ The numlock modifier
|
||||
, extensibleState :: !(M.Map String (Either String StateExtension))
|
||||
-- ^ stores custom state information.
|
||||
--
|
||||
-- The module XMonad.Utils.ExtensibleState in xmonad-contrib
|
||||
-- The module "XMonad.Utils.ExtensibleState" in xmonad-contrib
|
||||
-- provides additional information and a simple interface for using this.
|
||||
}
|
||||
|
||||
@@ -87,6 +87,8 @@ data XConf = XConf
|
||||
, mousePosition :: !(Maybe (Position, Position))
|
||||
-- ^ position of the mouse according to
|
||||
-- the event currently being processed
|
||||
, currentEvent :: !(Maybe Event)
|
||||
-- ^ event currently being processed
|
||||
}
|
||||
|
||||
-- todo, better name
|
||||
@@ -109,6 +111,11 @@ data XConfig l = XConfig
|
||||
, 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
|
||||
, clickJustFocuses :: !Bool -- ^ False to make a click which changes focus to be additionally passed to the window
|
||||
, clientMask :: !EventMask -- ^ The client events that xmonad is interested in
|
||||
, rootMask :: !EventMask -- ^ The root events that xmonad is interested in
|
||||
, handleExtraArgs :: !([String] -> XConfig Layout -> IO (XConfig Layout))
|
||||
-- ^ Modify the configuration, complain about extra arguments etc. with arguments that are not handled by default
|
||||
}
|
||||
|
||||
|
||||
@@ -135,9 +142,7 @@ data ScreenDetail = SD { screenRect :: !Rectangle } deriving (Eq,Show, Read)
|
||||
-- instantiated on 'XConf' and 'XState' automatically.
|
||||
--
|
||||
newtype X a = X (ReaderT XConf (StateT XState IO) a)
|
||||
#ifndef __HADDOCK__
|
||||
deriving (Functor, Monad, MonadIO, MonadState XState, MonadReader XConf, Typeable)
|
||||
#endif
|
||||
|
||||
instance Applicative X where
|
||||
pure = return
|
||||
@@ -147,11 +152,12 @@ instance (Monoid a) => Monoid (X a) where
|
||||
mempty = return mempty
|
||||
mappend = liftM2 mappend
|
||||
|
||||
instance Default a => Default (X a) where
|
||||
def = return def
|
||||
|
||||
type ManageHook = Query (Endo WindowSet)
|
||||
newtype Query a = Query (ReaderT Window X a)
|
||||
#ifndef __HADDOCK__
|
||||
deriving (Functor, Monad, MonadReader Window, MonadIO)
|
||||
#endif
|
||||
deriving (Functor, Applicative, Monad, MonadReader Window, MonadIO)
|
||||
|
||||
runQuery :: Query a -> Window -> X a
|
||||
runQuery (Query m) w = runReaderT m w
|
||||
@@ -160,6 +166,9 @@ instance Monoid a => Monoid (Query a) where
|
||||
mempty = return mempty
|
||||
mappend = liftM2 mappend
|
||||
|
||||
instance Default a => Default (Query a) where
|
||||
def = return def
|
||||
|
||||
-- | Run the 'X' monad, given a chunk of 'X' monad code, and an initial state
|
||||
-- Return the result, and final state
|
||||
runX :: XConf -> XState -> X a -> IO (a, XState)
|
||||
@@ -171,9 +180,9 @@ catchX :: X a -> X a -> X a
|
||||
catchX job errcase = do
|
||||
st <- get
|
||||
c <- ask
|
||||
(a, s') <- io $ runX c st job `catch` \e -> case e of
|
||||
ExitException {} -> throw e
|
||||
_ -> do hPrint stderr e; runX c st errcase
|
||||
(a, s') <- io $ runX c st job `E.catch` \e -> case fromException e of
|
||||
Just x -> throw e `const` (x `asTypeOf` ExitSuccess)
|
||||
_ -> do hPrint stderr e; runX c st errcase
|
||||
put s'
|
||||
return a
|
||||
|
||||
@@ -185,7 +194,7 @@ 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
|
||||
userCodeDef defValue a = fromMaybe defValue `liftM` userCode a
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- Convenient wrappers to state
|
||||
@@ -207,10 +216,11 @@ getAtom :: String -> X Atom
|
||||
getAtom str = withDisplay $ \dpy -> io $ internAtom dpy str False
|
||||
|
||||
-- | Common non-predefined atoms
|
||||
atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, atom_WM_STATE :: X Atom
|
||||
atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, atom_WM_STATE, atom_WM_TAKE_FOCUS :: X Atom
|
||||
atom_WM_PROTOCOLS = getAtom "WM_PROTOCOLS"
|
||||
atom_WM_DELETE_WINDOW = getAtom "WM_DELETE_WINDOW"
|
||||
atom_WM_STATE = getAtom "WM_STATE"
|
||||
atom_WM_TAKE_FOCUS = getAtom "WM_TAKE_FOCUS"
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- LayoutClass handling. See particular instances in Operations.hs
|
||||
@@ -386,19 +396,25 @@ io = liftIO
|
||||
-- | 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.
|
||||
catchIO :: MonadIO m => IO () -> m ()
|
||||
catchIO f = io (f `catch` \e -> hPrint stderr e >> hFlush stderr)
|
||||
catchIO f = io (f `E.catch` \(SomeException e) -> hPrint stderr e >> hFlush stderr)
|
||||
|
||||
-- | spawn. Launch an external application. Specifically, it double-forks and
|
||||
-- runs the 'String' you pass as a command to /bin/sh.
|
||||
-- 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 x = spawnPID x >> return ()
|
||||
|
||||
-- | Like 'spawn', but returns the 'ProcessID' of the launched application
|
||||
spawnPID :: MonadIO m => String -> m ProcessID
|
||||
spawnPID x = io . forkProcess . finally nullStdin $ do
|
||||
spawnPID x = xfork $ executeFile "/bin/sh" False ["-c", x] Nothing
|
||||
|
||||
-- | A replacement for 'forkProcess' which resets default signal handlers.
|
||||
xfork :: MonadIO m => IO () -> m ProcessID
|
||||
xfork x = io . forkProcess . finally nullStdin $ do
|
||||
uninstallSignalHandlers
|
||||
createSession
|
||||
executeFile "/bin/sh" False ["-c", x] Nothing
|
||||
x
|
||||
where
|
||||
nullStdin = do
|
||||
fd <- openFd "/dev/null" ReadOnly Nothing defaultFileFlags
|
||||
@@ -426,9 +442,11 @@ getXMonadDir = io $ getAppUserDataDirectory "xmonad"
|
||||
--
|
||||
-- * the xmonad executable does not exist
|
||||
--
|
||||
-- * the xmonad executable is older than xmonad.hs
|
||||
-- * 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.
|
||||
-- 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
|
||||
-- GHC indicates failure with a non-zero exit code, an xmessage displaying
|
||||
@@ -452,8 +470,8 @@ recompile force = io $ do
|
||||
then do
|
||||
-- temporarily disable SIGCHLD ignoring:
|
||||
uninstallSignalHandlers
|
||||
status <- bracket (openFile err WriteMode) hClose $ \h -> do
|
||||
waitForProcess =<< runProcess "ghc" ["--make", "xmonad.hs", "-i", "-ilib", "-fforce-recomp", "-v0", "-o",binn] (Just dir)
|
||||
status <- bracket (openFile err WriteMode) hClose $ \h ->
|
||||
waitForProcess =<< runProcess "ghc" ["--make", "xmonad.hs", "-i", "-ilib", "-fforce-recomp", "-main-is", "main", "-v0", "-o",binn] (Just dir)
|
||||
Nothing Nothing Nothing (Just h)
|
||||
|
||||
-- re-enable SIGCHLD:
|
||||
@@ -464,7 +482,8 @@ recompile force = io $ do
|
||||
ghcErr <- readFile err
|
||||
let msg = unlines $
|
||||
["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)
|
||||
++ ["","Please check the file for errors."]
|
||||
-- nb, the ordering of printing, then forking, is crucial due to
|
||||
-- lazy evaluation
|
||||
hPutStrLn stderr msg
|
||||
@@ -472,11 +491,11 @@ recompile force = io $ do
|
||||
return ()
|
||||
return (status == ExitSuccess)
|
||||
else return True
|
||||
where getModTime f = catch (Just <$> getModificationTime f) (const $ return Nothing)
|
||||
isSource = flip elem [".hs",".lhs",".hsc"]
|
||||
where getModTime f = E.catch (Just <$> getModificationTime f) (\(SomeException _) -> return Nothing)
|
||||
isSource = flip elem [".hs",".lhs",".hsc"] . takeExtension
|
||||
allFiles t = do
|
||||
let prep = map (t</>) . Prelude.filter (`notElem` [".",".."])
|
||||
cs <- prep <$> catch (getDirectoryContents t) (\_ -> return [])
|
||||
cs <- prep <$> E.catch (getDirectoryContents t) (\(SomeException _) -> return [])
|
||||
ds <- filterM doesDirectoryExist cs
|
||||
concat . ((cs \\ ds):) <$> mapM allFiles ds
|
||||
|
||||
@@ -499,7 +518,8 @@ installSignalHandlers :: MonadIO m => m ()
|
||||
installSignalHandlers = io $ do
|
||||
installHandler openEndedPipe Ignore Nothing
|
||||
installHandler sigCHLD Ignore Nothing
|
||||
try $ fix $ \more -> do
|
||||
(try :: IO a -> IO (Either SomeException a))
|
||||
$ fix $ \more -> do
|
||||
x <- getAnyProcessStatus False False
|
||||
when (isJust x) more
|
||||
return ()
|
@@ -1,5 +1,4 @@
|
||||
{-# OPTIONS_GHC -fglasgow-exts #-} -- For deriving Data/Typeable
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances, DeriveDataTypeable #-}
|
||||
|
||||
-- --------------------------------------------------------------------------
|
||||
-- |
|
||||
@@ -53,7 +52,8 @@ instance LayoutClass Full a
|
||||
-- '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)
|
||||
, tallRatio :: !Rational -- ^ Default proportion of screen occupied by master pane (default: 1/2)
|
||||
}
|
||||
deriving (Show, Read)
|
||||
-- TODO should be capped [0..1] ..
|
||||
|
||||
@@ -125,7 +125,7 @@ instance LayoutClass l a => LayoutClass (Mirror l) a where
|
||||
|
||||
-- | Mirror a rectangle.
|
||||
mirrorRect :: Rectangle -> Rectangle
|
||||
mirrorRect (Rectangle rx ry rw rh) = (Rectangle ry rx rh rw)
|
||||
mirrorRect (Rectangle rx ry rw rh) = Rectangle ry rx rh rw
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- LayoutClass selection manager
|
||||
@@ -173,7 +173,7 @@ choose (Choose d l r) d' ml mr = f lr
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
@@ -194,7 +194,7 @@ instance (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a where
|
||||
|
||||
R -> choose c R Nothing =<< handle r NextNoWrap
|
||||
|
||||
handleMessage c@(Choose _ l _) m | Just FirstLayout <- fromMessage m = do
|
||||
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 =
|
@@ -1,4 +1,4 @@
|
||||
{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, ForeignFunctionInterface #-}
|
||||
{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts #-}
|
||||
----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Main
|
||||
@@ -15,9 +15,11 @@
|
||||
|
||||
module XMonad.Main (xmonad) where
|
||||
|
||||
import System.Locale.SetLocale
|
||||
import Control.Arrow (second)
|
||||
import Data.Bits
|
||||
import Data.List ((\\))
|
||||
import Data.Function
|
||||
import qualified Data.Map as M
|
||||
import qualified Data.Set as S
|
||||
import Control.Monad.Reader
|
||||
@@ -25,11 +27,6 @@ import Control.Monad.State
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Monoid (getAll)
|
||||
|
||||
import Foreign.C
|
||||
import Foreign.Ptr
|
||||
|
||||
import System.Environment (getArgs)
|
||||
|
||||
import Graphics.X11.Xlib hiding (refreshKeyboardMapping)
|
||||
import Graphics.X11.Xlib.Extras
|
||||
|
||||
@@ -41,23 +38,123 @@ import XMonad.Operations
|
||||
|
||||
import System.IO
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Locale support
|
||||
import System.Info
|
||||
import System.Environment
|
||||
import System.Posix.Process (executeFile)
|
||||
import System.Exit (exitFailure)
|
||||
import System.FilePath
|
||||
|
||||
#include <locale.h>
|
||||
import Paths_xmonad (version)
|
||||
import Data.Version (showVersion)
|
||||
|
||||
foreign import ccall unsafe "locale.h setlocale"
|
||||
c_setlocale :: CInt -> Ptr CChar -> IO (Ptr CChar)
|
||||
import Graphics.X11.Xinerama (compiledWithXinerama)
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
|
||||
-- |
|
||||
-- | The entry point into xmonad. Attempts to compile any custom main
|
||||
-- for xmonad, and if it doesn't find one, just launches the default.
|
||||
xmonad :: (LayoutClass l Window, Read (l Window)) => XConfig l -> IO ()
|
||||
xmonad conf = do
|
||||
installSignalHandlers -- important to ignore SIGCHLD to avoid zombies
|
||||
|
||||
let launch serializedWinset serializedExtState args = do
|
||||
catchIO buildLaunch
|
||||
conf' @ XConfig { layoutHook = Layout l }
|
||||
<- handleExtraArgs conf args conf{ layoutHook = Layout (layoutHook conf) }
|
||||
withArgs [] $
|
||||
xmonadNoargs (conf' { layoutHook = l })
|
||||
serializedWinset
|
||||
serializedExtState
|
||||
|
||||
args <- getArgs
|
||||
case args of
|
||||
("--resume": ws : xs : args') -> launch (Just ws) (Just xs) args'
|
||||
["--help"] -> usage
|
||||
["--recompile"] -> recompile True >>= flip unless exitFailure
|
||||
["--restart"] -> sendRestart
|
||||
["--version"] -> putStrLn $ unwords shortVersion
|
||||
["--verbose-version"] -> putStrLn . unwords $ shortVersion ++ longVersion
|
||||
"--replace" : args' -> do
|
||||
sendReplace
|
||||
launch Nothing Nothing args'
|
||||
_ -> launch Nothing Nothing args
|
||||
where
|
||||
shortVersion = ["xmonad", showVersion version]
|
||||
longVersion = [ "compiled by", compilerName, showVersion compilerVersion
|
||||
, "for", arch ++ "-" ++ os
|
||||
, "\nXinerama:", show compiledWithXinerama ]
|
||||
|
||||
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" :
|
||||
[]
|
||||
|
||||
-- | 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
|
||||
-- these cases:
|
||||
--
|
||||
-- * ghc missing
|
||||
--
|
||||
-- * both "~\/.xmonad\/xmonad.hs" and "~\/.xmonad\/xmonad-$arch-$os" missing
|
||||
--
|
||||
-- * xmonad.hs fails to compile
|
||||
--
|
||||
-- ** wrong ghc in path (fails to compile)
|
||||
--
|
||||
-- ** type error, syntax error, ..
|
||||
--
|
||||
-- * Missing XMonad\/XMonadContrib modules due to ghc upgrade
|
||||
--
|
||||
buildLaunch :: IO ()
|
||||
buildLaunch = do
|
||||
recompile False
|
||||
dir <- getXMonadDir
|
||||
args <- getArgs
|
||||
whoami <- getProgName
|
||||
let compiledConfig = "xmonad-"++arch++"-"++os
|
||||
unless (whoami == compiledConfig) $
|
||||
executeFile (dir </> compiledConfig) False args Nothing
|
||||
|
||||
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
|
||||
|
||||
-- | a wrapper for 'replace'
|
||||
sendReplace :: IO ()
|
||||
sendReplace = do
|
||||
dpy <- openDisplay ""
|
||||
let dflt = defaultScreen dpy
|
||||
rootw <- rootWindow dpy dflt
|
||||
replace dpy dflt rootw
|
||||
|
||||
|
||||
-- |
|
||||
-- The main entry point
|
||||
--
|
||||
xmonad :: (LayoutClass l Window, Read (l Window)) => XConfig l -> IO ()
|
||||
xmonad initxmc = do
|
||||
xmonadNoargs :: (LayoutClass l Window, Read (l Window)) => XConfig l
|
||||
-> Maybe String -- ^ serialized windowset
|
||||
-> Maybe String -- ^ serialized extensible state
|
||||
-> IO ()
|
||||
xmonadNoargs initxmc serializedWinset serializedExtstate = do
|
||||
-- setup locale information from environment
|
||||
withCString "" $ c_setlocale (#const LC_ALL)
|
||||
setLocale LC_ALL (Just "")
|
||||
-- ignore SIGPIPE and SIGCHLD
|
||||
installSignalHandlers
|
||||
-- First, wrap the layout in an existential, to keep things pretty:
|
||||
@@ -70,9 +167,8 @@ xmonad initxmc = do
|
||||
-- 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
|
||||
selectInput dpy rootw $ rootMask initxmc
|
||||
|
||||
sync dpy False -- sync to ensure all outstanding errors are delivered
|
||||
|
||||
-- turn off the default handler in favor of one that ignores all errors
|
||||
@@ -81,30 +177,30 @@ xmonad initxmc = do
|
||||
|
||||
xinesc <- getCleanedScreenInfo dpy
|
||||
nbc <- do v <- initColor dpy $ normalBorderColor xmc
|
||||
~(Just nbc_) <- initColor dpy $ normalBorderColor Default.defaultConfig
|
||||
~(Just nbc_) <- initColor dpy $ normalBorderColor Default.def
|
||||
return (fromMaybe nbc_ v)
|
||||
|
||||
fbc <- do v <- initColor dpy $ focusedBorderColor xmc
|
||||
~(Just fbc_) <- initColor dpy $ focusedBorderColor Default.defaultConfig
|
||||
~(Just fbc_) <- initColor dpy $ focusedBorderColor Default.def
|
||||
return (fromMaybe fbc_ v)
|
||||
|
||||
hSetBuffering stdout NoBuffering
|
||||
args <- getArgs
|
||||
|
||||
let layout = layoutHook xmc
|
||||
lreads = readsLayout layout
|
||||
initialWinset = new layout (workspaces xmc) $ map SD xinesc
|
||||
initialWinset = let padToLen n xs = take (max n (length xs)) $ xs ++ repeat ""
|
||||
in new layout (padToLen (length xinesc) (workspaces xmc)) $ map SD xinesc
|
||||
maybeRead reads' s = case reads' s of
|
||||
[(x, "")] -> Just x
|
||||
_ -> Nothing
|
||||
|
||||
winset = fromMaybe initialWinset $ do
|
||||
("--resume" : s : _) <- return args
|
||||
s <- serializedWinset
|
||||
ws <- maybeRead reads s
|
||||
return . W.ensureTags layout (workspaces xmc)
|
||||
$ W.mapLayout (fromMaybe layout . maybeRead lreads) ws
|
||||
extState = fromMaybe M.empty $ do
|
||||
("--resume" : _ : dyns : _) <- return args
|
||||
dyns <- serializedExtstate
|
||||
vals <- maybeRead reads dyns
|
||||
return . M.fromList . map (second Left) $ vals
|
||||
|
||||
@@ -117,11 +213,12 @@ xmonad initxmc = do
|
||||
, keyActions = keys xmc xmc
|
||||
, buttonActions = mouseBindings xmc xmc
|
||||
, mouseFocused = False
|
||||
, mousePosition = Nothing }
|
||||
, mousePosition = Nothing
|
||||
, currentEvent = Nothing }
|
||||
|
||||
st = XState
|
||||
{ windowset = initialWinset
|
||||
, numlockMask = 0
|
||||
, numberlockMask = 0
|
||||
, mapped = S.empty
|
||||
, waitingUnmap = M.empty
|
||||
, dragging = Nothing
|
||||
@@ -151,17 +248,15 @@ xmonad initxmc = do
|
||||
userCode $ startupHook initxmc
|
||||
|
||||
-- main loop, for all you HOF/recursion fans out there.
|
||||
forever_ $ prehandle =<< io (nextEvent dpy e >> getEvent e)
|
||||
forever $ prehandle =<< io (nextEvent dpy e >> getEvent e)
|
||||
|
||||
return ()
|
||||
where
|
||||
forever_ a = a >> forever_ a
|
||||
|
||||
-- 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)
|
||||
in local (\c -> c { mousePosition = mouse, currentEvent = Just e }) (handleWithHook e)
|
||||
evs = [ keyPress, keyRelease, enterNotify, leaveNotify
|
||||
, buttonPress, buttonRelease]
|
||||
|
||||
@@ -245,12 +340,16 @@ handle e@(ButtonEvent {ev_window = w,ev_event_type = t,ev_button = b })
|
||||
| t == buttonPress = do
|
||||
-- If it's the root window, then it's something we
|
||||
-- grabbed in grabButtons. Otherwise, it's click-to-focus.
|
||||
dpy <- asks display
|
||||
isr <- isRoot w
|
||||
m <- cleanMask $ ev_state e
|
||||
mact <- asks (M.lookup (m, b) . buttonActions)
|
||||
case mact of
|
||||
(Just act) | isr -> act $ ev_subwindow e
|
||||
_ -> focus w
|
||||
Just act | isr -> act $ ev_subwindow e
|
||||
_ -> do
|
||||
focus w
|
||||
ctf <- asks (clickJustFocuses . config)
|
||||
unless ctf $ io (allowEvents dpy replayPointer currentTime)
|
||||
broadcastMessage e -- Always send button events.
|
||||
|
||||
-- entered a normal window: focus it if focusFollowsMouse is set to
|
||||
@@ -295,8 +394,9 @@ handle e@(ConfigureRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
|
||||
handle (ConfigureEvent {ev_window = w}) = whenX (isRoot w) rescreen
|
||||
|
||||
-- property notify
|
||||
handle PropertyEvent { ev_event_type = t, ev_atom = a }
|
||||
| t == propertyNotify && a == wM_NAME = userCodeDef () =<< asks (logHook . config)
|
||||
handle event@(PropertyEvent { ev_event_type = t, ev_atom = a })
|
||||
| t == propertyNotify && a == wM_NAME = asks (logHook . config) >>= userCodeDef () >>
|
||||
broadcastMessage event
|
||||
|
||||
handle e@ClientMessageEvent { ev_message_type = mt } = do
|
||||
a <- getAtom "XMONAD_RESTART"
|
||||
@@ -338,20 +438,25 @@ setNumlockMask = do
|
||||
then return (setBit 0 (fromIntegral m))
|
||||
else return (0 :: KeyMask)
|
||||
| (m, kcs) <- ms, kc <- kcs, kc /= 0]
|
||||
modify (\s -> s { numlockMask = foldr (.|.) 0 xs })
|
||||
modify (\s -> s { numberlockMask = foldr (.|.) 0 xs })
|
||||
|
||||
-- | Grab the keys back
|
||||
grabKeys :: X ()
|
||||
grabKeys = do
|
||||
XConf { display = dpy, theRoot = rootw } <- ask
|
||||
let grab kc m = io $ grabKey dpy kc m rootw True grabModeAsync grabModeAsync
|
||||
(minCode, maxCode) = displayKeycodes dpy
|
||||
allCodes = [fromIntegral minCode .. fromIntegral maxCode]
|
||||
io $ ungrabKey dpy anyKey anyModifier rootw
|
||||
ks <- asks keyActions
|
||||
forM_ (M.keys ks) $ \(mask,sym) -> do
|
||||
kc <- io $ keysymToKeycode dpy sym
|
||||
-- "If the specified KeySym is not defined for any KeyCode,
|
||||
-- XKeysymToKeycode() returns zero."
|
||||
when (kc /= 0) $ mapM_ (grab kc . (mask .|.)) =<< extraModifiers
|
||||
-- build a map from keysyms to lists of keysyms (doing what
|
||||
-- XGetKeyboardMapping would do if the X11 package bound it)
|
||||
syms <- forM allCodes $ \code -> io (keycodeToKeysym dpy code 0)
|
||||
let keysymMap = M.fromListWith (++) (zip syms [[code] | code <- allCodes])
|
||||
keysymToKeycodes sym = M.findWithDefault [] sym keysymMap
|
||||
forM_ (M.keys ks) $ \(mask,sym) ->
|
||||
forM_ (keysymToKeycodes sym) $ \kc ->
|
||||
mapM_ (grab kc . (mask .|.)) =<< extraModifiers
|
||||
|
||||
-- | XXX comment me
|
||||
grabButtons :: X ()
|
||||
@@ -363,3 +468,36 @@ grabButtons = do
|
||||
ems <- extraModifiers
|
||||
ba <- asks buttonActions
|
||||
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
|
@@ -18,11 +18,11 @@
|
||||
|
||||
module XMonad.ManageHook where
|
||||
|
||||
import Prelude hiding (catch)
|
||||
import XMonad.Core
|
||||
import Graphics.X11.Xlib.Extras
|
||||
import Graphics.X11.Xlib (Display, Window, internAtom, wM_NAME)
|
||||
import Control.Exception (bracket, catch)
|
||||
import Control.Exception.Extensible (bracket, SomeException(..))
|
||||
import qualified Control.Exception.Extensible as E
|
||||
import Control.Monad.Reader
|
||||
import Data.Maybe
|
||||
import Data.Monoid
|
||||
@@ -34,22 +34,24 @@ liftX :: X a -> Query a
|
||||
liftX = Query . lift
|
||||
|
||||
-- | The identity hook that returns the WindowSet unchanged.
|
||||
idHook :: ManageHook
|
||||
idHook = doF id
|
||||
idHook :: Monoid m => m
|
||||
idHook = mempty
|
||||
|
||||
-- | Infix 'mappend'. Compose two 'ManageHook' from right to left.
|
||||
(<+>) :: Monoid m => m -> m -> m
|
||||
(<+>) = mappend
|
||||
|
||||
-- | Compose the list of 'ManageHook's.
|
||||
composeAll :: [ManageHook] -> ManageHook
|
||||
composeAll :: Monoid m => [m] -> m
|
||||
composeAll = mconcat
|
||||
|
||||
infix 0 -->
|
||||
|
||||
-- | @p --> x@. If @p@ returns 'True', execute the 'ManageHook'.
|
||||
(-->) :: Query Bool -> ManageHook -> ManageHook
|
||||
p --> f = p >>= \b -> if b then f else mempty
|
||||
--
|
||||
-- > (-->) :: 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
|
||||
@@ -72,10 +74,10 @@ title = ask >>= \w -> liftX $ do
|
||||
let
|
||||
getProp =
|
||||
(internAtom d "_NET_WM_NAME" False >>= getTextProperty d w)
|
||||
`catch` \_ -> getTextProperty d w wM_NAME
|
||||
`E.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` \_ -> return ""
|
||||
io $ bracket getProp (xFree . tp_value) extract `E.catch` \(SomeException _) -> return ""
|
||||
|
||||
-- | Return the application name.
|
||||
appName :: Query String
|
||||
@@ -101,7 +103,7 @@ getStringProperty d w p = do
|
||||
return $ fmap (map (toEnum . fromIntegral)) md
|
||||
|
||||
-- | Modify the 'WindowSet' with a pure function.
|
||||
doF :: (WindowSet -> WindowSet) -> ManageHook
|
||||
doF :: (s -> s) -> Query (Endo s)
|
||||
doF = return . Endo
|
||||
|
||||
-- | Move the window to the floating layer.
|
@@ -1,5 +1,4 @@
|
||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||
{-# OPTIONS_GHC -fglasgow-exts #-} -- For deriving Data/Typeable
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
|
||||
|
||||
-- --------------------------------------------------------------------------
|
||||
@@ -25,7 +24,7 @@ import qualified XMonad.StackSet as W
|
||||
import Data.Maybe
|
||||
import Data.Monoid (Endo(..))
|
||||
import Data.List (nub, (\\), find)
|
||||
import Data.Bits ((.|.), (.&.), complement)
|
||||
import Data.Bits ((.|.), (.&.), complement, testBit)
|
||||
import Data.Ratio
|
||||
import qualified Data.Map as M
|
||||
import qualified Data.Set as S
|
||||
@@ -33,9 +32,8 @@ import qualified Data.Set as S
|
||||
import Control.Applicative
|
||||
import Control.Monad.Reader
|
||||
import Control.Monad.State
|
||||
import qualified Control.Exception as C
|
||||
import qualified Control.Exception.Extensible as C
|
||||
|
||||
import System.IO
|
||||
import System.Posix.Process (executeFile)
|
||||
import Graphics.X11.Xlib
|
||||
import Graphics.X11.Xinerama (getScreenInfo)
|
||||
@@ -186,9 +184,10 @@ setWMState w v = withDisplay $ \dpy -> do
|
||||
-- | hide. Hide a window by unmapping it, and setting Iconified.
|
||||
hide :: Window -> X ()
|
||||
hide w = whenX (gets (S.member w . mapped)) $ withDisplay $ \d -> do
|
||||
io $ do selectInput d w (clientMask .&. complement structureNotifyMask)
|
||||
cMask <- asks $ clientMask . config
|
||||
io $ do selectInput d w (cMask .&. complement structureNotifyMask)
|
||||
unmapWindow d w
|
||||
selectInput d w clientMask
|
||||
selectInput d w cMask
|
||||
setWMState w iconicState
|
||||
-- this part is key: we increment the waitingUnmap counter to distinguish
|
||||
-- between client and xmonad initiated unmaps.
|
||||
@@ -201,17 +200,13 @@ reveal :: Window -> X ()
|
||||
reveal w = withDisplay $ \d -> do
|
||||
setWMState w normalState
|
||||
io $ mapWindow d w
|
||||
modify (\s -> s { mapped = S.insert w (mapped s) })
|
||||
|
||||
-- | The client events that xmonad is interested in
|
||||
clientMask :: EventMask
|
||||
clientMask = structureNotifyMask .|. enterWindowMask .|. propertyChangeMask
|
||||
whenX (isClient w) $ modify (\s -> s { mapped = S.insert w (mapped s) })
|
||||
|
||||
-- | Set some properties when we initially gain control of a window
|
||||
setInitialProperties :: Window -> X ()
|
||||
setInitialProperties w = asks normalBorder >>= \nb -> withDisplay $ \d -> do
|
||||
setWMState w iconicState
|
||||
io $ selectInput d w $ clientMask
|
||||
asks (clientMask . config) >>= io . selectInput d w
|
||||
bw <- asks (borderWidth . config)
|
||||
io $ setWindowBorderWidth d w bw
|
||||
-- we must initially set the color of new windows, to maintain invariants
|
||||
@@ -285,11 +280,14 @@ rescreen = do
|
||||
|
||||
-- | setButtonGrab. Tell whether or not to intercept clicks on a given window
|
||||
setButtonGrab :: Bool -> Window -> X ()
|
||||
setButtonGrab grab w = withDisplay $ \d -> io $
|
||||
if grab
|
||||
setButtonGrab grab w = do
|
||||
pointerMode <- asks $ \c -> if clickJustFocuses (config c)
|
||||
then grabModeAsync
|
||||
else grabModeSync
|
||||
withDisplay $ \d -> io $ if grab
|
||||
then forM_ [button1, button2, button3] $ \b ->
|
||||
grabButton d b anyModifier w False buttonPressMask
|
||||
grabModeAsync grabModeSync none none
|
||||
pointerMode grabModeSync none none
|
||||
else ungrabButton d anyButton anyModifier w
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
@@ -321,14 +319,33 @@ setFocusX w = withWindowSet $ \ws -> do
|
||||
dpy <- asks display
|
||||
|
||||
-- clear mouse button grab and border on other windows
|
||||
forM_ (W.current ws : W.visible ws) $ \wk -> do
|
||||
forM_ (W.index (W.view (W.tag (W.workspace wk)) ws)) $ \otherw -> do
|
||||
forM_ (W.current ws : W.visible ws) $ \wk ->
|
||||
forM_ (W.index (W.view (W.tag (W.workspace wk)) ws)) $ \otherw ->
|
||||
setButtonGrab True otherw
|
||||
|
||||
-- If we ungrab buttons on the root window, we lose our mouse bindings.
|
||||
whenX (not <$> isRoot w) $ setButtonGrab False w
|
||||
io $ do setInputFocus dpy w revertToPointerRoot 0
|
||||
-- raiseWindow dpy w
|
||||
|
||||
hints <- io $ getWMHints dpy w
|
||||
protocols <- io $ getWMProtocols dpy w
|
||||
wmprot <- atom_WM_PROTOCOLS
|
||||
wmtf <- atom_WM_TAKE_FOCUS
|
||||
currevt <- asks currentEvent
|
||||
let inputHintSet = wmh_flags hints `testBit` inputHintBit
|
||||
|
||||
when ((inputHintSet && wmh_input hints) || (not inputHintSet)) $
|
||||
io $ do setInputFocus dpy w revertToPointerRoot 0
|
||||
when (wmtf `elem` protocols) $
|
||||
io $ allocaXEvent $ \ev -> do
|
||||
setEventType ev clientMessage
|
||||
setClientMessageEvent ev w wmprot 32 wmtf $ maybe currentTime event_time currevt
|
||||
sendEvent dpy w False noEventMask ev
|
||||
where event_time ev =
|
||||
if (ev_event_type ev) `elem` timedEvents then
|
||||
ev_time ev
|
||||
else
|
||||
currentTime
|
||||
timedEvents = [ keyPress, keyRelease, buttonPress, buttonRelease, enterNotify, leaveNotify, selectionRequest ]
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Message handling
|
||||
@@ -339,7 +356,7 @@ sendMessage :: Message a => a -> X ()
|
||||
sendMessage a = do
|
||||
w <- W.workspace . W.current <$> gets windowset
|
||||
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)
|
||||
{ W.workspace = (W.workspace $ W.current ws)
|
||||
{ W.layout = l' }}}
|
||||
@@ -389,18 +406,18 @@ isClient w = withWindowSet $ return . W.member w
|
||||
-- (numlock and capslock)
|
||||
extraModifiers :: X [KeyMask]
|
||||
extraModifiers = do
|
||||
nlm <- gets numlockMask
|
||||
nlm <- gets numberlockMask
|
||||
return [0, nlm, lockMask, nlm .|. lockMask ]
|
||||
|
||||
-- | Strip numlock\/capslock from a mask
|
||||
cleanMask :: KeyMask -> X KeyMask
|
||||
cleanMask km = do
|
||||
nlm <- gets numlockMask
|
||||
nlm <- gets numberlockMask
|
||||
return (complement (nlm .|. lockMask) .&. km)
|
||||
|
||||
-- | Get the 'Pixel' value for a named color
|
||||
initColor :: Display -> String -> IO (Maybe Pixel)
|
||||
initColor dpy c = C.handle (\_ -> return Nothing) $
|
||||
initColor dpy c = C.handle (\(C.SomeException _) -> return Nothing) $
|
||||
(Just . color_pixel . fst) <$> allocNamedColor dpy colormap c
|
||||
where colormap = defaultColormap dpy (defaultScreen dpy)
|
||||
|
||||
@@ -430,7 +447,7 @@ floatLocation :: Window -> X (ScreenId, W.RationalRect)
|
||||
floatLocation w = withDisplay $ \d -> do
|
||||
ws <- gets windowset
|
||||
wa <- io $ getWindowAttributes d w
|
||||
bw <- fi <$> asks (borderWidth . config)
|
||||
let bw = (fromIntegral . wa_border_width) wa
|
||||
sc <- fromMaybe (W.current ws) <$> pointScreen (fi $ wa_x wa) (fi $ wa_y wa)
|
||||
|
||||
let sr = screenRect . W.screenDetail $ sc
|
||||
@@ -439,7 +456,7 @@ floatLocation w = withDisplay $ \d -> do
|
||||
(fi (wa_width wa + bw*2) % fi (rect_width 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
|
||||
|
||||
-- | Given a point, determine the screen (if any) that contains it.
|
||||
@@ -509,7 +526,7 @@ mouseResizeWindow w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
wa <- io $ getWindowAttributes d w
|
||||
sh <- io $ getWMNormalHints d w
|
||||
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`
|
||||
applySizeHintsContents sh (ex - fromIntegral (wa_x wa),
|
||||
ey - fromIntegral (wa_y wa)))
|
@@ -52,7 +52,7 @@ module XMonad.StackSet (
|
||||
) where
|
||||
|
||||
import Prelude hiding (filter)
|
||||
import Data.Maybe (listToMaybe,isJust)
|
||||
import Data.Maybe (listToMaybe,isJust,fromMaybe)
|
||||
import qualified Data.List as L (deleteBy,find,splitAt,filter,nub)
|
||||
import Data.List ( (\\) )
|
||||
import qualified Data.Map as M (Map,insert,delete,empty)
|
||||
@@ -155,7 +155,7 @@ data RationalRect = RationalRect Rational Rational Rational Rational
|
||||
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 master window is by convention the top-most item.
|
||||
-- Focus operations will not reorder the list that results from
|
||||
@@ -369,7 +369,7 @@ 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 w s | Just w == peek s = s
|
||||
| otherwise = maybe s id $ do
|
||||
| otherwise = fromMaybe s $ do
|
||||
n <- findTag w s
|
||||
return $ until ((Just w ==) . peek) focusUp (view n s)
|
||||
|
140
tests/Instances.hs
Normal file
140
tests/Instances.hs
Normal file
@@ -0,0 +1,140 @@
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
module Instances where
|
||||
|
||||
import Test.QuickCheck
|
||||
|
||||
import Utils
|
||||
|
||||
import XMonad.StackSet
|
||||
import Control.Monad
|
||||
import Data.List (nub, genericLength)
|
||||
|
||||
import Debug.Trace
|
||||
|
||||
import Graphics.X11 (Rectangle(Rectangle))
|
||||
import Control.Applicative
|
||||
|
||||
--
|
||||
-- The all important Arbitrary instance for StackSet.
|
||||
--
|
||||
instance (Integral i, Integral s, Eq a, Arbitrary a, Arbitrary l, Arbitrary sd)
|
||||
=> Arbitrary (StackSet i l a s sd) where
|
||||
arbitrary = do
|
||||
-- TODO: Fix this to be a reasonable higher number, Possibly use PositiveSized
|
||||
numWs <- choose (1, 20) -- number of workspaces, there must be at least 1.
|
||||
numScreens <- choose (1, numWs) -- number of physical screens, there must be at least 1
|
||||
lay <- arbitrary -- pick any layout
|
||||
|
||||
wsIdxInFocus <- choose (1, numWs) -- pick index of WS to be in focus
|
||||
|
||||
-- The same screen id's will be present in the list, with high possibility.
|
||||
screens <- replicateM numScreens arbitrary
|
||||
|
||||
-- Generate a list of "windows" for each workspace.
|
||||
wsWindows <- vector numWs :: Gen [[a]]
|
||||
|
||||
-- Pick a random window "number" in each workspace, to give focus.
|
||||
focus <- sequence [ if null windows
|
||||
then return Nothing
|
||||
else liftM Just $ choose (0, length windows - 1)
|
||||
| windows <- wsWindows ]
|
||||
|
||||
let tags = [1 .. fromIntegral numWs]
|
||||
focusWsWindows = zip focus wsWindows
|
||||
wss = zip tags focusWsWindows -- tmp representation of a workspace (tag, windows)
|
||||
initSs = new lay tags screens
|
||||
return $
|
||||
view (fromIntegral wsIdxInFocus) $
|
||||
foldr (\(tag, (focus, windows)) ss -> -- Fold through all generated (tags,windows).
|
||||
-- set workspace active by tag and fold through all
|
||||
-- windows while inserting them. Apply the given number
|
||||
-- of `focusUp` on the resulting StackSet.
|
||||
applyN focus focusUp $ foldr insertUp (view tag ss) windows
|
||||
) initSs wss
|
||||
|
||||
|
||||
--
|
||||
-- Just generate StackSets with Char elements.
|
||||
--
|
||||
type Tag = Int
|
||||
type Window = Char
|
||||
type T = StackSet Tag Int Window Int Int
|
||||
|
||||
|
||||
|
||||
newtype EmptyStackSet = EmptyStackSet T
|
||||
deriving Show
|
||||
|
||||
instance Arbitrary EmptyStackSet where
|
||||
arbitrary = do
|
||||
(NonEmptyNubList ns) <- arbitrary
|
||||
(NonEmptyNubList sds) <- arbitrary
|
||||
l <- arbitrary
|
||||
-- there cannot be more screens than workspaces:
|
||||
return . EmptyStackSet . new l ns $ take (min (length ns) (length sds)) sds
|
||||
|
||||
|
||||
|
||||
newtype NonEmptyWindowsStackSet = NonEmptyWindowsStackSet T
|
||||
deriving Show
|
||||
|
||||
instance Arbitrary NonEmptyWindowsStackSet where
|
||||
arbitrary =
|
||||
NonEmptyWindowsStackSet `fmap` (arbitrary `suchThat` (not . null . allWindows))
|
||||
|
||||
instance Arbitrary Rectangle where
|
||||
arbitrary = Rectangle <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
|
||||
|
||||
|
||||
newtype SizedPositive = SizedPositive Int
|
||||
deriving (Eq, Ord, Show, Read)
|
||||
|
||||
instance Arbitrary SizedPositive where
|
||||
arbitrary = sized $ \s -> do x <- choose (1, max 1 s)
|
||||
return $ SizedPositive x
|
||||
|
||||
|
||||
|
||||
newtype NonEmptyNubList a = NonEmptyNubList [a]
|
||||
deriving ( Eq, Ord, Show, Read )
|
||||
|
||||
instance (Eq a, Arbitrary a) => Arbitrary (NonEmptyNubList a) where
|
||||
arbitrary = NonEmptyNubList `fmap` ((liftM nub arbitrary) `suchThat` (not . null))
|
||||
|
||||
|
||||
|
||||
-- | Pull out an arbitrary tag from the StackSet. This removes the need for the
|
||||
-- precondition "n `tagMember x` in many properties and thus reduces the number
|
||||
-- of discarded tests.
|
||||
--
|
||||
-- n <- arbitraryTag x
|
||||
--
|
||||
-- We can do the reverse with a simple `suchThat`:
|
||||
--
|
||||
-- n <- arbitrary `suchThat` \n' -> not $ n' `tagMember` x
|
||||
arbitraryTag :: T -> Gen Tag
|
||||
arbitraryTag x = do
|
||||
let ts = tags x
|
||||
-- There must be at least 1 workspace, thus at least 1 tag.
|
||||
idx <- choose (0, (length ts) - 1)
|
||||
return $ ts!!idx
|
||||
|
||||
-- | Pull out an arbitrary window from a StackSet that is guaranteed to have a
|
||||
-- non empty set of windows. This eliminates the precondition "i `member` x" in
|
||||
-- a few properties.
|
||||
--
|
||||
--
|
||||
-- foo (nex :: NonEmptyWindowsStackSet) = do
|
||||
-- let NonEmptyWindowsStackSet x = nex
|
||||
-- w <- arbitraryWindow nex
|
||||
-- return $ .......
|
||||
--
|
||||
-- We can do the reverse with a simple `suchThat`:
|
||||
--
|
||||
-- n <- arbitrary `suchThat` \n' -> not $ n `member` x
|
||||
arbitraryWindow :: NonEmptyWindowsStackSet -> Gen Window
|
||||
arbitraryWindow (NonEmptyWindowsStackSet x) = do
|
||||
let ws = allWindows x
|
||||
-- We know that there are at least 1 window in a NonEmptyWindowsStackSet.
|
||||
idx <- choose(0, (length ws) - 1)
|
||||
return $ ws!!idx
|
1300
tests/Properties.hs
1300
tests/Properties.hs
File diff suppressed because it is too large
Load Diff
70
tests/Properties/Delete.hs
Normal file
70
tests/Properties/Delete.hs
Normal file
@@ -0,0 +1,70 @@
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
module Properties.Delete where
|
||||
|
||||
import Test.QuickCheck
|
||||
import Instances
|
||||
import Utils
|
||||
|
||||
import XMonad.StackSet hiding (filter)
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- 'delete'
|
||||
|
||||
-- deleting the current item removes it.
|
||||
prop_delete x =
|
||||
case peek x of
|
||||
Nothing -> True
|
||||
Just i -> not (member i (delete i x))
|
||||
where _ = x :: T
|
||||
|
||||
-- delete is reversible with 'insert'.
|
||||
-- It is the identiy, except for the 'master', which is reset on insert and delete.
|
||||
--
|
||||
prop_delete_insert (x :: T) =
|
||||
case peek x of
|
||||
Nothing -> True
|
||||
Just n -> insertUp n (delete n y) == y
|
||||
where
|
||||
y = swapMaster x
|
||||
|
||||
-- delete should be local
|
||||
prop_delete_local (x :: T) =
|
||||
case peek x of
|
||||
Nothing -> True
|
||||
Just i -> hidden_spaces x == hidden_spaces (delete i x)
|
||||
|
||||
-- delete should not affect focus unless the focused element is what is being deleted
|
||||
prop_delete_focus = do
|
||||
-- There should be at least two windows. One in focus, and some to try and
|
||||
-- delete (doesn't have to be windows on the current workspace). We generate
|
||||
-- our own, since we can't rely on NonEmptyWindowsStackSet returning one in
|
||||
-- the argument with at least two windows.
|
||||
x <- arbitrary `suchThat` \x' -> length (allWindows x') >= 2
|
||||
w <- arbitraryWindow (NonEmptyWindowsStackSet x)
|
||||
-- Make sure we pick a window that is NOT the currently focused
|
||||
`suchThat` \w' -> Just w' /= peek x
|
||||
return $ peek (delete w x) == peek x
|
||||
|
||||
-- focus movement in the presence of delete:
|
||||
-- when the last window in the stack set is focused, focus moves `up'.
|
||||
-- usual case is that it moves 'down'.
|
||||
prop_delete_focus_end = do
|
||||
-- Generate a StackSet with at least two windows on the current workspace.
|
||||
x <- arbitrary `suchThat` \(x' :: T) -> length (index x') >= 2
|
||||
let w = last (index x)
|
||||
y = focusWindow w x -- focus last window in stack
|
||||
return $ peek (delete w y) == peek (focusUp y)
|
||||
|
||||
|
||||
-- focus movement in the presence of delete:
|
||||
-- when not in the last item in the stack, focus moves down
|
||||
prop_delete_focus_not_end = do
|
||||
x <- arbitrary
|
||||
-- There must be at least two windows and the current focused is not the
|
||||
-- last one in the stack.
|
||||
`suchThat` \(x' :: T) ->
|
||||
let currWins = index x'
|
||||
in length (currWins) >= 2 && peek x' /= Just (last currWins)
|
||||
-- This is safe, as we know there are >= 2 windows
|
||||
let Just n = peek x
|
||||
return $ peek (delete n x) == peek (focusDown x)
|
26
tests/Properties/Failure.hs
Normal file
26
tests/Properties/Failure.hs
Normal file
@@ -0,0 +1,26 @@
|
||||
module Properties.Failure where
|
||||
|
||||
import XMonad.StackSet hiding (filter)
|
||||
|
||||
import qualified Control.Exception.Extensible as C
|
||||
import System.IO.Unsafe
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- testing for failure
|
||||
|
||||
-- and help out hpc
|
||||
prop_abort x = unsafePerformIO $ C.catch (abort "fail")
|
||||
(\(C.SomeException e) -> return $ show e == "xmonad: StackSet: fail" )
|
||||
where
|
||||
_ = x :: Int
|
||||
|
||||
-- new should fail with an abort
|
||||
prop_new_abort x = unsafePerformIO $ C.catch f
|
||||
(\(C.SomeException e) -> return $ show e == "xmonad: StackSet: non-positive argument to StackSet.new" )
|
||||
where
|
||||
f = new undefined{-layout-} [] [] `seq` return False
|
||||
|
||||
_ = x :: Int
|
||||
|
||||
-- TODO: Fix this?
|
||||
-- prop_view_should_fail = view {- with some bogus data -}
|
36
tests/Properties/Floating.hs
Normal file
36
tests/Properties/Floating.hs
Normal file
@@ -0,0 +1,36 @@
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
module Properties.Floating where
|
||||
|
||||
import Test.QuickCheck
|
||||
import Instances
|
||||
|
||||
import XMonad.StackSet hiding (filter)
|
||||
|
||||
import qualified Data.Map as M
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- properties for the floating layer:
|
||||
|
||||
prop_float_reversible (nex :: NonEmptyWindowsStackSet) = do
|
||||
let NonEmptyWindowsStackSet x = nex
|
||||
w <- arbitraryWindow nex
|
||||
return $ sink w (float w geom x) == x
|
||||
where
|
||||
geom = RationalRect 100 100 100 100
|
||||
|
||||
prop_float_geometry (nex :: NonEmptyWindowsStackSet) = do
|
||||
let NonEmptyWindowsStackSet x = nex
|
||||
w <- arbitraryWindow nex
|
||||
let s = float w geom x
|
||||
return $ M.lookup w (floating s) == Just geom
|
||||
where
|
||||
geom = RationalRect 100 100 100 100
|
||||
|
||||
prop_float_delete (nex :: NonEmptyWindowsStackSet) = do
|
||||
let NonEmptyWindowsStackSet x = nex
|
||||
w <- arbitraryWindow nex
|
||||
let s = float w geom x
|
||||
t = delete w s
|
||||
return $ not (w `member` t)
|
||||
where
|
||||
geom = RationalRect 100 100 100 100
|
74
tests/Properties/Focus.hs
Normal file
74
tests/Properties/Focus.hs
Normal file
@@ -0,0 +1,74 @@
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
module Properties.Focus where
|
||||
|
||||
import Test.QuickCheck
|
||||
import Instances
|
||||
import Utils
|
||||
|
||||
import XMonad.StackSet hiding (filter)
|
||||
|
||||
import Data.Maybe (fromJust)
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- rotating focus
|
||||
--
|
||||
|
||||
-- master/focus
|
||||
--
|
||||
-- The tiling order, and master window, of a stack is unaffected by focus changes.
|
||||
--
|
||||
prop_focus_left_master (SizedPositive n) (x::T) =
|
||||
index (applyN (Just n) focusUp x) == index x
|
||||
prop_focus_right_master (SizedPositive n) (x::T) =
|
||||
index (applyN (Just n) focusDown x) == index x
|
||||
prop_focus_master_master (SizedPositive n) (x::T) =
|
||||
index (applyN (Just n) focusMaster x) == index x
|
||||
|
||||
prop_focusWindow_master (NonNegative n) (x :: T) =
|
||||
case peek x of
|
||||
Nothing -> True
|
||||
Just _ -> let s = index x
|
||||
i = n `mod` length s
|
||||
in index (focusWindow (s !! i) x) == index x
|
||||
|
||||
-- shifting focus is trivially reversible
|
||||
prop_focus_left (x :: T) = (focusUp (focusDown x)) == x
|
||||
prop_focus_right (x :: T) = (focusDown (focusUp x)) == x
|
||||
|
||||
-- focus master is idempotent
|
||||
prop_focusMaster_idem (x :: T) = focusMaster x == focusMaster (focusMaster x)
|
||||
|
||||
-- focusWindow actually leaves the window focused...
|
||||
prop_focusWindow_works (NonNegative (n :: Int)) (x :: T) =
|
||||
case peek x of
|
||||
Nothing -> True
|
||||
Just _ -> let s = index x
|
||||
i = fromIntegral n `mod` length s
|
||||
in (focus . fromJust . stack . workspace . current) (focusWindow (s !! i) x) == (s !! i)
|
||||
|
||||
-- rotation through the height of a stack gets us back to the start
|
||||
prop_focus_all_l (x :: T) = (foldr (const focusUp) x [1..n]) == x
|
||||
where n = length (index x)
|
||||
prop_focus_all_r (x :: T) = (foldr (const focusDown) x [1..n]) == x
|
||||
where n = length (index x)
|
||||
|
||||
-- prop_rotate_all (x :: T) = f (f x) == f x
|
||||
-- f x' = foldr (\_ y -> rotate GT y) x' [1..n]
|
||||
|
||||
-- focus is local to the current workspace
|
||||
prop_focus_down_local (x :: T) = hidden_spaces (focusDown x) == hidden_spaces x
|
||||
prop_focus_up_local (x :: T) = hidden_spaces (focusUp x) == hidden_spaces x
|
||||
|
||||
prop_focus_master_local (x :: T) = hidden_spaces (focusMaster x) == hidden_spaces x
|
||||
|
||||
prop_focusWindow_local (NonNegative (n :: Int)) (x::T ) =
|
||||
case peek x of
|
||||
Nothing -> True
|
||||
Just _ -> let s = index x
|
||||
i = fromIntegral n `mod` length s
|
||||
in hidden_spaces (focusWindow (s !! i) x) == hidden_spaces x
|
||||
|
||||
-- On an invalid window, the stackset is unmodified
|
||||
prop_focusWindow_identity (x::T ) = do
|
||||
n <- arbitrary `suchThat` \n' -> not $ n' `member` x
|
||||
return $ focusWindow n x == x
|
44
tests/Properties/GreedyView.hs
Normal file
44
tests/Properties/GreedyView.hs
Normal file
@@ -0,0 +1,44 @@
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
module Properties.GreedyView where
|
||||
|
||||
import Test.QuickCheck
|
||||
import Instances
|
||||
import Utils
|
||||
|
||||
import XMonad.StackSet hiding (filter)
|
||||
|
||||
import Data.List (sortBy)
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- greedyViewing workspaces
|
||||
|
||||
-- greedyView sets the current workspace to 'n'
|
||||
prop_greedyView_current (x :: T) = do
|
||||
n <- arbitraryTag x
|
||||
return $ currentTag (greedyView n x) == n
|
||||
|
||||
-- greedyView leaves things unchanged for invalid workspaces
|
||||
prop_greedyView_current_id (x :: T) = do
|
||||
n <- arbitrary `suchThat` \n' -> not $ n' `tagMember` x
|
||||
return $ currentTag (greedyView n x) == currentTag x
|
||||
|
||||
-- greedyView *only* sets the current workspace, and touches Xinerama.
|
||||
-- no workspace contents will be changed.
|
||||
prop_greedyView_local (x :: T) = do
|
||||
n <- arbitraryTag x
|
||||
return $ workspaces x == workspaces (greedyView n x)
|
||||
where
|
||||
workspaces a = sortBy (\s t -> tag s `compare` tag t) $
|
||||
workspace (current a)
|
||||
: map workspace (visible a) ++ hidden a
|
||||
|
||||
-- greedyView is idempotent
|
||||
prop_greedyView_idem (x :: T) = do
|
||||
n <- arbitraryTag x
|
||||
return $ greedyView n (greedyView n x) == (greedyView n x)
|
||||
|
||||
-- greedyView is reversible, though shuffles the order of hidden/visible
|
||||
prop_greedyView_reversible (x :: T) = do
|
||||
n <- arbitraryTag x
|
||||
return $ normal (greedyView n' (greedyView n x)) == normal x
|
||||
where n' = currentTag x
|
52
tests/Properties/Insert.hs
Normal file
52
tests/Properties/Insert.hs
Normal file
@@ -0,0 +1,52 @@
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
module Properties.Insert where
|
||||
|
||||
import Test.QuickCheck
|
||||
import Instances
|
||||
import Utils
|
||||
|
||||
import XMonad.StackSet hiding (filter)
|
||||
|
||||
import Data.List (nub)
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- 'insert'
|
||||
|
||||
-- inserting a item into an empty stackset means that item is now a member
|
||||
prop_insert_empty i (EmptyStackSet x)= member i (insertUp i x)
|
||||
|
||||
-- insert should be idempotent
|
||||
prop_insert_idem i (x :: T) = insertUp i x == insertUp i (insertUp i x)
|
||||
|
||||
-- insert when an item is a member should leave the stackset unchanged
|
||||
prop_insert_duplicate (nex :: NonEmptyWindowsStackSet) = do
|
||||
let NonEmptyWindowsStackSet x = nex
|
||||
w <- arbitraryWindow nex
|
||||
return $ insertUp w x == x
|
||||
|
||||
-- push shouldn't change anything but the current workspace
|
||||
prop_insert_local (x :: T) = do
|
||||
i <- arbitrary `suchThat` \i' -> not $ i' `member` x
|
||||
return $ hidden_spaces x == hidden_spaces (insertUp i x)
|
||||
|
||||
-- Inserting a (unique) list of items into an empty stackset should
|
||||
-- result in the last inserted element having focus.
|
||||
prop_insert_peek (EmptyStackSet x) (NonEmptyNubList is) =
|
||||
peek (foldr insertUp x is) == Just (head is)
|
||||
|
||||
-- insert >> delete is the identity, when i `notElem` .
|
||||
-- Except for the 'master', which is reset on insert and delete.
|
||||
--
|
||||
prop_insert_delete x = do
|
||||
n <- arbitrary `suchThat` \n -> not $ n `member` x
|
||||
return $ delete n (insertUp n y) == (y :: T)
|
||||
where
|
||||
y = swapMaster x -- sets the master window to the current focus.
|
||||
-- otherwise, we don't have a rule for where master goes.
|
||||
|
||||
-- inserting n elements increases current stack size by n
|
||||
prop_size_insert is (EmptyStackSet x) =
|
||||
size (foldr insertUp x ws ) == (length ws)
|
||||
where
|
||||
ws = nub is
|
||||
size = length . index
|
34
tests/Properties/Layout/Full.hs
Normal file
34
tests/Properties/Layout/Full.hs
Normal file
@@ -0,0 +1,34 @@
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
module Properties.Layout.Full where
|
||||
|
||||
import Test.QuickCheck
|
||||
import Instances
|
||||
|
||||
import XMonad.StackSet hiding (filter)
|
||||
import XMonad.Core
|
||||
import XMonad.Layout
|
||||
|
||||
import Data.Maybe
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Full layout
|
||||
|
||||
-- pureLayout works for Full
|
||||
prop_purelayout_full rect = do
|
||||
x <- (arbitrary :: Gen T) `suchThat` (isJust . peek)
|
||||
let layout = Full
|
||||
st = fromJust . stack . workspace . current $ x
|
||||
ts = pureLayout layout rect st
|
||||
return $
|
||||
length ts == 1 -- only one window to view
|
||||
&&
|
||||
snd (head ts) == rect -- and sets fullscreen
|
||||
&&
|
||||
fst (head ts) == fromJust (peek x) -- and the focused window is shown
|
||||
|
||||
|
||||
-- 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
|
116
tests/Properties/Layout/Tall.hs
Normal file
116
tests/Properties/Layout/Tall.hs
Normal file
@@ -0,0 +1,116 @@
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
module Properties.Layout.Tall where
|
||||
|
||||
import Test.QuickCheck
|
||||
import Instances
|
||||
import Utils
|
||||
|
||||
import XMonad.StackSet hiding (filter)
|
||||
import XMonad.Core
|
||||
import XMonad.Layout
|
||||
|
||||
import Graphics.X11.Xlib.Types (Rectangle(..))
|
||||
|
||||
import Data.Maybe
|
||||
import Data.List (sort)
|
||||
import Data.Ratio
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- The Tall layout
|
||||
|
||||
-- 1 window should always be tiled fullscreen
|
||||
prop_tile_fullscreen rect = tile pct rect 1 1 == [rect]
|
||||
where pct = 1/2
|
||||
|
||||
-- multiple windows
|
||||
prop_tile_non_overlap rect windows nmaster = noOverlaps (tile pct rect nmaster windows)
|
||||
where _ = rect :: Rectangle
|
||||
pct = 3 % 100
|
||||
|
||||
-- splitting horizontally yields sensible results
|
||||
prop_split_horizontal (NonNegative n) x =
|
||||
(noOverflows (+) (rect_x x) (rect_width x)) ==>
|
||||
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 vertically yields sensible results
|
||||
prop_split_vertical (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
|
||||
where
|
||||
(a,b) = splitVerticallyBy r x
|
||||
|
||||
|
||||
-- pureLayout works.
|
||||
prop_purelayout_tall n r1 r2 rect = do
|
||||
x <- (arbitrary :: Gen T) `suchThat` (isJust . peek)
|
||||
let layout = Tall n r1 r2
|
||||
st = fromJust . stack . workspace . current $ x
|
||||
ts = pureLayout layout rect st
|
||||
return $
|
||||
length ts == length (index x)
|
||||
&&
|
||||
noOverlaps (map snd ts)
|
||||
&&
|
||||
description layout == "Tall"
|
||||
|
||||
|
||||
-- Test message handling of Tall
|
||||
|
||||
-- what happens when we send a Shrink message to Tall
|
||||
prop_shrink_tall (NonNegative n) (Positive 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)
|
||||
(Positive delta)
|
||||
(NonNegative n1)
|
||||
(Positive 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) (Positive 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)
|
||||
|
||||
|
||||
prop_desc_mirror n r1 r2 = description (Mirror $! t) == "Mirror Tall"
|
||||
where t = Tall n r1 r2
|
73
tests/Properties/Screen.hs
Normal file
73
tests/Properties/Screen.hs
Normal file
@@ -0,0 +1,73 @@
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
module Properties.Screen where
|
||||
|
||||
import Utils
|
||||
import Test.QuickCheck
|
||||
import Instances
|
||||
|
||||
import Control.Applicative
|
||||
import XMonad.StackSet hiding (filter)
|
||||
import XMonad.Operations
|
||||
import Graphics.X11.Xlib.Types (Dimension)
|
||||
|
||||
import Graphics.X11 (Rectangle(Rectangle))
|
||||
import XMonad.Layout
|
||||
|
||||
prop_screens (x :: T) = n `elem` screens x
|
||||
where
|
||||
n = current x
|
||||
|
||||
-- screens makes sense
|
||||
prop_screens_works (x :: T) = screens x == current x : visible x
|
||||
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Hints
|
||||
|
||||
prop_resize_inc (Positive inc_w,Positive 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 (Positive inc_w,Positive 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)
|
||||
|
||||
|
||||
prop_aspect_hint_shrink hint (w,h) = case applyAspectHint hint (w,h) of
|
||||
(w',h') -> w' <= w && h' <= h
|
||||
|
||||
|
||||
-- applyAspectHint does nothing when the supplied (x,y) fits
|
||||
-- the desired range
|
||||
prop_aspect_fits =
|
||||
forAll ((,,,) <$> pos <*> pos <*> pos <*> pos) $ \ (x,y,a,b) ->
|
||||
let f v = applyAspectHint ((x, y+a), (x+b, y)) v
|
||||
in and [ noOverflows (*) x (y+a), noOverflows (*) (x+b) y ]
|
||||
==> f (x,y) == (x,y)
|
||||
|
||||
where pos = choose (0, 65535)
|
||||
mul a b = toInteger (a*b) /= toInteger a * toInteger b
|
||||
|
||||
prop_point_within r @ (Rectangle x y w h) =
|
||||
forAll ((,) <$>
|
||||
choose (0, fromIntegral w - 1) <*>
|
||||
choose (0, fromIntegral h - 1)) $
|
||||
\(dx,dy) ->
|
||||
and [ dx > 0, dy > 0,
|
||||
noOverflows (\ a b -> a + abs b) x w,
|
||||
noOverflows (\ a b -> a + abs b) y h ]
|
||||
==> pointWithin (x+dx) (y+dy) r
|
||||
|
||||
prop_point_within_mirror r (x,y) = pointWithin x y r == pointWithin y x (mirrorRect r)
|
70
tests/Properties/Shift.hs
Normal file
70
tests/Properties/Shift.hs
Normal file
@@ -0,0 +1,70 @@
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
module Properties.Shift where
|
||||
|
||||
import Test.QuickCheck
|
||||
import Instances
|
||||
import Utils
|
||||
|
||||
import XMonad.StackSet hiding (filter)
|
||||
|
||||
import qualified Data.List as L
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- shift
|
||||
|
||||
-- shift is fully reversible on current window, when focus and master
|
||||
-- are the same. otherwise, master may move.
|
||||
prop_shift_reversible (x :: T) = do
|
||||
i <- arbitraryTag x
|
||||
case peek y of
|
||||
Nothing -> return True
|
||||
Just _ -> return $ normal ((view n . shift n . view i . shift i) y) == normal y
|
||||
where
|
||||
y = swapMaster x
|
||||
n = currentTag 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 on current window is the same as shift
|
||||
prop_shift_win_focus (x :: T) = do
|
||||
n <- arbitraryTag x
|
||||
case peek x of
|
||||
Nothing -> return True
|
||||
Just w -> return $ shiftWin n w x == shift n x
|
||||
|
||||
-- shiftWin on a non-existant window is identity
|
||||
prop_shift_win_indentity (x :: T) = do
|
||||
n <- arbitraryTag x
|
||||
w <- arbitrary `suchThat` \w' -> not (w' `member` x)
|
||||
return $ shiftWin n w x == x
|
||||
|
||||
-- shiftWin leaves the current screen as it is, if neither n is the tag
|
||||
-- of the current workspace nor w on the current workspace
|
||||
prop_shift_win_fix_current = do
|
||||
x <- arbitrary `suchThat` \(x' :: T) ->
|
||||
-- Invariant, otherWindows are NOT in the current workspace.
|
||||
let otherWindows = allWindows x' L.\\ index x'
|
||||
in length(tags x') >= 2 && length(otherWindows) >= 1
|
||||
-- Sadly we have to construct `otherWindows` again, for the actual StackSet
|
||||
-- that got chosen.
|
||||
let otherWindows = allWindows x L.\\ index x
|
||||
-- We know such tag must exists, due to the precondition
|
||||
n <- arbitraryTag x `suchThat` (/= currentTag x)
|
||||
-- we know length is >= 1, from above precondition
|
||||
idx <- choose(0, length(otherWindows) - 1)
|
||||
let w = otherWindows !! idx
|
||||
return $ (current $ x) == (current $ shiftWin n w x)
|
||||
|
51
tests/Properties/Stack.hs
Normal file
51
tests/Properties/Stack.hs
Normal file
@@ -0,0 +1,51 @@
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
module Properties.Stack where
|
||||
|
||||
import Test.QuickCheck
|
||||
import Instances
|
||||
|
||||
import XMonad.StackSet hiding (filter)
|
||||
import qualified XMonad.StackSet as S (filter)
|
||||
|
||||
import Data.Maybe
|
||||
|
||||
|
||||
-- The list returned by index should be the same length as the actual
|
||||
-- windows kept in the zipper
|
||||
prop_index_length (x :: T) =
|
||||
case stack . workspace . current $ x of
|
||||
Nothing -> length (index x) == 0
|
||||
Just it -> length (index x) == length (focus it : up it ++ down it)
|
||||
|
||||
|
||||
-- For all windows in the stackSet, findTag should identify the
|
||||
-- correct workspace
|
||||
prop_findIndex (x :: T) =
|
||||
and [ tag w == fromJust (findTag i x)
|
||||
| w <- workspace (current x) : map workspace (visible x) ++ hidden x
|
||||
, t <- maybeToList (stack w)
|
||||
, i <- focus t : up t ++ down t
|
||||
]
|
||||
|
||||
prop_allWindowsMember (NonEmptyWindowsStackSet x) = do
|
||||
-- Reimplementation of arbitraryWindow, but to make sure that
|
||||
-- implementation doesn't change in the future, and stop using allWindows,
|
||||
-- which is a key component in this test (together with member).
|
||||
let ws = allWindows x
|
||||
-- We know that there are at least 1 window in a NonEmptyWindowsStackSet.
|
||||
idx <- choose(0, (length ws) - 1)
|
||||
return $ member (ws!!idx) x
|
||||
|
||||
|
||||
-- preserve order
|
||||
prop_filter_order (x :: T) =
|
||||
case stack $ workspace $ current x of
|
||||
Nothing -> True
|
||||
Just s@(Stack i _ _) -> integrate' (S.filter (/= i) s) == filter (/= i) (integrate' (Just s))
|
||||
|
||||
-- differentiate should return Nothing if the list is empty or Just stack, with
|
||||
-- the first element of the list is current, and the rest of the list is down.
|
||||
prop_differentiate xs =
|
||||
if null xs then differentiate xs == Nothing
|
||||
else (differentiate xs) == Just (Stack (head xs) [] (tail xs))
|
||||
where _ = xs :: [Int]
|
135
tests/Properties/StackSet.hs
Normal file
135
tests/Properties/StackSet.hs
Normal file
@@ -0,0 +1,135 @@
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
module Properties.StackSet where
|
||||
|
||||
import Test.QuickCheck
|
||||
import Instances
|
||||
import Utils
|
||||
|
||||
import XMonad.StackSet hiding (filter)
|
||||
|
||||
import Data.Maybe
|
||||
|
||||
import Data.List (nub)
|
||||
-- ---------------------------------------------------------------------
|
||||
-- QuickCheck properties for the StackSet
|
||||
|
||||
-- Some general hints for creating StackSet properties:
|
||||
--
|
||||
-- * ops that mutate the StackSet are usually local
|
||||
-- * most ops on StackSet should either be trivially reversible, or
|
||||
-- idempotent, or both.
|
||||
------------------------------------------------------------------------
|
||||
|
||||
-- Basic data invariants of the StackSet
|
||||
--
|
||||
-- With the new zipper-based StackSet, tracking focus is no longer an
|
||||
-- issue: the data structure enforces focus by construction.
|
||||
--
|
||||
-- But we still need to ensure there are no duplicates, and master/and
|
||||
-- the xinerama mapping aren't checked by the data structure at all.
|
||||
--
|
||||
-- * no element should ever appear more than once in a StackSet
|
||||
-- * the xinerama screen map should be:
|
||||
-- -- keys should always index valid workspaces
|
||||
-- -- monotonically ascending in the elements
|
||||
-- * the current workspace should be a member of the xinerama screens
|
||||
--
|
||||
invariant (s :: T) = and
|
||||
-- no duplicates
|
||||
[ noDuplicates
|
||||
|
||||
-- TODO: Fix this.
|
||||
-- all this xinerama stuff says we don't have the right structure
|
||||
-- , validScreens
|
||||
-- , validWorkspaces
|
||||
-- , inBounds
|
||||
]
|
||||
where
|
||||
ws = concat [ focus t : up t ++ down t
|
||||
| w <- workspace (current s) : map workspace (visible s) ++ hidden s
|
||||
, t <- maybeToList (stack w)] :: [Char]
|
||||
noDuplicates = nub ws == ws
|
||||
|
||||
-- validScreens = monotonic . sort . M. . (W.current s : W.visible : W$ s
|
||||
|
||||
-- validWorkspaces = and [ w `elem` allworkspaces | w <- (M.keys . screens) s ]
|
||||
-- where allworkspaces = map tag $ current s : prev s ++ next s
|
||||
|
||||
-- inBounds = and [ w >=0 && w < size s | (w,sc) <- M.assocs (screens s) ]
|
||||
|
||||
monotonic [] = True
|
||||
monotonic (x:[]) = True
|
||||
monotonic (x:y:zs) | x == y-1 = monotonic (y:zs)
|
||||
| otherwise = False
|
||||
|
||||
prop_invariant = invariant
|
||||
|
||||
-- and check other ops preserve invariants
|
||||
prop_empty_I (SizedPositive n) l = forAll (choose (1, fromIntegral n)) $ \m ->
|
||||
forAll (vector m) $ \ms ->
|
||||
invariant $ new l [0..fromIntegral n-1] ms
|
||||
|
||||
prop_view_I n (x :: T) =
|
||||
invariant $ view n x
|
||||
|
||||
prop_greedyView_I n (x :: T) =
|
||||
invariant $ greedyView n x
|
||||
|
||||
prop_focusUp_I (SizedPositive n) (x :: T) =
|
||||
invariant $ applyN (Just n) focusUp x
|
||||
prop_focusMaster_I (SizedPositive n) (x :: T) =
|
||||
invariant $ applyN (Just n) focusMaster x
|
||||
prop_focusDown_I (SizedPositive n) (x :: T) =
|
||||
invariant $ applyN (Just n) focusDown x
|
||||
|
||||
prop_focus_I (SizedPositive n) (x :: T) =
|
||||
case peek x of
|
||||
Nothing -> True
|
||||
Just _ -> let w = focus . fromJust . stack . workspace . current $
|
||||
applyN (Just n) focusUp x
|
||||
in invariant $ focusWindow w x
|
||||
|
||||
prop_insertUp_I n (x :: T) = invariant $ insertUp n x
|
||||
|
||||
prop_delete_I (x :: T) = invariant $
|
||||
case peek x of
|
||||
Nothing -> x
|
||||
Just i -> delete i x
|
||||
|
||||
prop_swap_master_I (x :: T) = invariant $ swapMaster x
|
||||
|
||||
prop_swap_left_I (SizedPositive n) (x :: T) =
|
||||
invariant $ applyN (Just n) swapUp x
|
||||
prop_swap_right_I (SizedPositive n) (x :: T) =
|
||||
invariant $ applyN (Just n) swapDown x
|
||||
|
||||
prop_shift_I (x :: T) = do
|
||||
n <- arbitraryTag x
|
||||
return $ invariant $ shift (fromIntegral n) x
|
||||
|
||||
prop_shift_win_I (nex :: NonEmptyWindowsStackSet) = do
|
||||
let NonEmptyWindowsStackSet x = nex
|
||||
w <- arbitraryWindow nex
|
||||
n <- arbitraryTag x
|
||||
return $ invariant $ shiftWin n w x
|
||||
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
|
||||
|
||||
-- empty StackSets have no windows in them
|
||||
prop_empty (EmptyStackSet x) =
|
||||
all (== Nothing) [ stack w | w <- workspace (current x)
|
||||
: map workspace (visible x) ++ hidden x ]
|
||||
|
||||
-- empty StackSets always have focus on first workspace
|
||||
prop_empty_current (EmptyStackSet x) = currentTag x == head (tags x)
|
||||
|
||||
-- no windows will be a member of an empty workspace
|
||||
prop_member_empty i (EmptyStackSet x) = member i x == False
|
||||
|
||||
-- peek either yields nothing on the Empty workspace, or Just a valid window
|
||||
prop_member_peek (x :: T) =
|
||||
case peek x of
|
||||
Nothing -> True {- then we don't know anything -}
|
||||
Just i -> member i x
|
47
tests/Properties/Swap.hs
Normal file
47
tests/Properties/Swap.hs
Normal file
@@ -0,0 +1,47 @@
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
module Properties.Swap where
|
||||
|
||||
import Test.QuickCheck
|
||||
import Instances
|
||||
import Utils
|
||||
|
||||
import XMonad.StackSet hiding (filter)
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- swapUp, swapDown, swapMaster: reordiring windows
|
||||
|
||||
-- swap is trivially reversible
|
||||
prop_swap_left (x :: T) = (swapUp (swapDown x)) == x
|
||||
prop_swap_right (x :: T) = (swapDown (swapUp x)) == x
|
||||
-- TODO swap is reversible
|
||||
-- swap is reversible, but involves moving focus back the window with
|
||||
-- master on it. easy to do with a mouse...
|
||||
{-
|
||||
prop_promote_reversible x b = (not . null . fromMaybe [] . flip index x . current $ x) ==>
|
||||
(raiseFocus y . promote . raiseFocus z . promote) x == x
|
||||
where _ = x :: T
|
||||
dir = if b then LT else GT
|
||||
(Just y) = peek x
|
||||
(Just (z:_)) = flip index x . current $ x
|
||||
-}
|
||||
|
||||
-- swap doesn't change focus
|
||||
prop_swap_master_focus (x :: T) = peek x == (peek $ swapMaster x)
|
||||
-- = case peek x of
|
||||
-- Nothing -> True
|
||||
-- Just f -> focus (stack (workspace $ current (swap x))) == f
|
||||
prop_swap_left_focus (x :: T) = peek x == (peek $ swapUp x)
|
||||
prop_swap_right_focus (x :: T) = peek x == (peek $ swapDown x)
|
||||
|
||||
-- swap is local
|
||||
prop_swap_master_local (x :: T) = hidden_spaces x == hidden_spaces (swapMaster x)
|
||||
prop_swap_left_local (x :: T) = hidden_spaces x == hidden_spaces (swapUp x)
|
||||
prop_swap_right_local (x :: T) = hidden_spaces x == hidden_spaces (swapDown x)
|
||||
|
||||
-- rotation through the height of a stack gets us back to the start
|
||||
prop_swap_all_l (x :: T) = (foldr (const swapUp) x [1..n]) == x
|
||||
where n = length (index x)
|
||||
prop_swap_all_r (x :: T) = (foldr (const swapDown) x [1..n]) == x
|
||||
where n = length (index x)
|
||||
|
||||
prop_swap_master_idempotent (x :: T) = swapMaster (swapMaster x) == swapMaster x
|
47
tests/Properties/View.hs
Normal file
47
tests/Properties/View.hs
Normal file
@@ -0,0 +1,47 @@
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
module Properties.View where
|
||||
|
||||
import Test.QuickCheck
|
||||
import Instances
|
||||
import Utils
|
||||
|
||||
import XMonad.StackSet hiding (filter)
|
||||
|
||||
import Data.List (sortBy)
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- viewing workspaces
|
||||
|
||||
-- view sets the current workspace to 'n'
|
||||
prop_view_current (x :: T) = do
|
||||
n <- arbitraryTag x
|
||||
return $ (tag . workspace . current . view n) x == n
|
||||
|
||||
-- view *only* sets the current workspace, and touches Xinerama.
|
||||
-- no workspace contents will be changed.
|
||||
prop_view_local (x :: T) = do
|
||||
n <- arbitraryTag x
|
||||
return $ workspaces x == workspaces (view n x)
|
||||
where
|
||||
workspaces a = sortBy (\s t -> tag s `compare` tag t) $
|
||||
workspace (current a)
|
||||
: map workspace (visible a) ++ hidden a
|
||||
|
||||
-- TODO: Fix this
|
||||
-- view should result in a visible xinerama screen
|
||||
-- prop_view_xinerama (x :: T) (n :: NonNegative Int) = i `tagMember` x ==>
|
||||
-- M.member i (screens (view i x))
|
||||
-- where
|
||||
-- i = fromIntegral n
|
||||
|
||||
-- view is idempotent
|
||||
prop_view_idem (x :: T) = do
|
||||
n <- arbitraryTag x
|
||||
return $ view n (view n x) == (view n x)
|
||||
|
||||
-- view is reversible, though shuffles the order of hidden/visible
|
||||
prop_view_reversible (x :: T) = do
|
||||
n <- arbitraryTag x
|
||||
return $ normal (view n' (view n x)) == normal x
|
||||
where
|
||||
n' = currentTag x
|
65
tests/Properties/Workspace.hs
Normal file
65
tests/Properties/Workspace.hs
Normal file
@@ -0,0 +1,65 @@
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
module Properties.Workspace where
|
||||
|
||||
import Test.QuickCheck
|
||||
import Instances
|
||||
import Utils
|
||||
|
||||
import XMonad.StackSet hiding (filter)
|
||||
|
||||
import Data.Maybe
|
||||
|
||||
-- looking up the tag of the current workspace should always produce a tag.
|
||||
prop_lookup_current (x :: T) = lookupWorkspace scr x == Just tg
|
||||
where
|
||||
(Screen (Workspace tg _ _) scr _) = current x
|
||||
|
||||
-- looking at a visible tag
|
||||
prop_lookup_visible = do
|
||||
-- make sure we have some xinerama screens.
|
||||
x <- arbitrary `suchThat` \(x' :: T) -> visible x' /= []
|
||||
let tags = [ tag (workspace y) | y <- visible x ]
|
||||
scr = last [ screen y | y <- visible x ]
|
||||
return $ fromJust (lookupWorkspace scr x) `elem` tags
|
||||
|
||||
|
||||
prop_currentTag (x :: T) =
|
||||
currentTag x == tag (workspace (current x))
|
||||
|
||||
-- Rename a given tag if present in the StackSet.
|
||||
prop_rename1 (x::T) = do
|
||||
o <- arbitraryTag x
|
||||
n <- arbitrary `suchThat` \n' -> not $ n' `tagMember` x
|
||||
-- Rename o to n
|
||||
let y = renameTag o n x
|
||||
return $ 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
|
||||
in and [ n `tagMember` y | n <- xs ]
|
||||
|
||||
-- adding a tag should create a new hidden workspace
|
||||
prop_ensure_append (x :: T) l = do
|
||||
n <- arbitrary `suchThat` \n' -> not $ n' `tagMember` x
|
||||
let ts = tags x
|
||||
y = ensureTags l (n:ts) x
|
||||
return $ hidden y /= hidden x -- doesn't append, renames
|
||||
&& and [ isNothing (stack z) && layout z == l | z <- hidden y, tag z == n ]
|
||||
|
||||
|
||||
|
||||
|
||||
prop_mapWorkspaceId (x::T) = x == mapWorkspace id x
|
||||
|
||||
prop_mapWorkspaceInverse (x::T) = x == mapWorkspace predTag (mapWorkspace succTag x)
|
||||
where predTag w = w { tag = pred $ tag w }
|
||||
succTag w = w { tag = succ $ tag w }
|
||||
|
||||
prop_mapLayoutId (x::T) = x == mapLayout id x
|
||||
|
||||
prop_mapLayoutInverse (x::T) = x == mapLayout pred (mapLayout succ x)
|
||||
|
||||
|
47
tests/Utils.hs
Normal file
47
tests/Utils.hs
Normal file
@@ -0,0 +1,47 @@
|
||||
{-# LANGUAGE RankNTypes #-}
|
||||
module Utils where
|
||||
|
||||
import XMonad.StackSet hiding (filter)
|
||||
import Graphics.X11.Xlib.Types (Rectangle(..))
|
||||
import Data.List (sortBy)
|
||||
|
||||
-- Useful operation, the non-local workspaces
|
||||
hidden_spaces x = map workspace (visible x) ++ hidden x
|
||||
|
||||
|
||||
-- normalise workspace list
|
||||
normal s = s { hidden = sortBy g (hidden s), visible = sortBy f (visible s) }
|
||||
where
|
||||
f = \a b -> tag (workspace a) `compare` tag (workspace b)
|
||||
g = \a b -> tag a `compare` tag b
|
||||
|
||||
|
||||
noOverlaps [] = True
|
||||
noOverlaps [_] = True
|
||||
noOverlaps xs = and [ verts a `notOverlap` verts b
|
||||
| a <- xs
|
||||
, b <- filter (a /=) xs
|
||||
]
|
||||
where
|
||||
verts (Rectangle a b w h) = (a,b,a + fromIntegral w - 1, b + fromIntegral h - 1)
|
||||
|
||||
notOverlap (left1,bottom1,right1,top1)
|
||||
(left2,bottom2,right2,top2)
|
||||
= (top1 < bottom2 || top2 < bottom1)
|
||||
|| (right1 < left2 || right2 < left1)
|
||||
|
||||
|
||||
applyN :: (Integral n) => Maybe n -> (a -> a) -> a -> a
|
||||
applyN Nothing f v = v
|
||||
applyN (Just 0) f v = v
|
||||
applyN (Just n) f v = applyN (Just $ n-1) f (f v)
|
||||
|
||||
tags x = map tag $ workspaces x
|
||||
|
||||
|
||||
-- | noOverflows op a b is True if @a `op` fromIntegral b@ overflows (or
|
||||
-- otherwise gives the same answer when done using Integer
|
||||
noOverflows :: (Integral b, Integral c) =>
|
||||
(forall a. Integral a => a -> a -> a) -> b -> c -> Bool
|
||||
noOverflows op a b = toInteger (a `op` fromIntegral b) == toInteger a `op` toInteger b
|
||||
|
@@ -1,10 +0,0 @@
|
||||
#!/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 $
|
||||
map (dropWhile (==' ')) $ lines foo
|
||||
loc = length actual_loc
|
||||
putStrLn $ show loc
|
||||
print loc
|
||||
-- uncomment the following to check for mistakes in isntcomment
|
||||
-- putStr $ unlines $ actual_loc
|
||||
-- print actual_loc
|
||||
|
||||
isntcomment ('-':'-':_) = False
|
||||
isntcomment ('{':'-':_) = False -- pragmas
|
||||
|
@@ -1,3 +1,4 @@
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
-- Unlike the rest of xmonad, this file is copyright under the terms of the
|
||||
-- GPL.
|
||||
|
||||
@@ -12,7 +13,7 @@
|
||||
-- Format for the docstrings in Config.hs takes the following form:
|
||||
--
|
||||
-- -- mod-x %! Frob the whatsit
|
||||
--
|
||||
--
|
||||
-- "Frob the whatsit" will be used as the description for keybinding "mod-x"
|
||||
--
|
||||
-- If the keybinding name is omitted, it will try to guess from the rest of the
|
||||
@@ -34,16 +35,16 @@ import Distribution.PackageDescription
|
||||
import Text.PrettyPrint.HughesPJ
|
||||
import Distribution.Text
|
||||
|
||||
import Text.Pandoc
|
||||
import Text.Pandoc -- works with 1.15.x
|
||||
|
||||
releaseDate = "25 October 09"
|
||||
releaseDate = "31 December 2012"
|
||||
|
||||
trim :: String -> String
|
||||
trim = reverse . dropWhile isSpace . reverse . dropWhile isSpace
|
||||
|
||||
guessKeys line = concat $ intersperse "-" (modifiers ++ [map toLower key])
|
||||
where modifiers = map (!!1) (line =~ "(mod|shift|control)Mask")
|
||||
(_, _, _, [key]) = line =~ "xK_(\\w+)" :: (String, String, String, [String])
|
||||
(_, _, _, [key]) = line =~ "xK_([_[:alnum:]]+)" :: (String, String, String, [String])
|
||||
|
||||
binding :: [String] -> (String, String)
|
||||
binding [ _, bindingLine, "", desc ] = (guessKeys bindingLine, desc)
|
||||
@@ -65,30 +66,34 @@ main = do
|
||||
releaseName <- (show . disp . package . packageDescription)
|
||||
`liftM`readPackageDescription normal "xmonad.cabal"
|
||||
keybindings <- (intercalate "\n\n" . map markdownDefn . allBindings)
|
||||
`liftM` readFile "./XMonad/Config.hs"
|
||||
`liftM` readFile "./src/XMonad/Config.hs"
|
||||
|
||||
let manHeader = unwords [".TH xmonad 1","\""++releaseDate++"\"",releaseName,"\"xmonad manual\""]
|
||||
writeOpts = defaultWriterOptions -- { writerLiterateHaskell = True }
|
||||
|
||||
parsed <- readMarkdown defaultParserState { stateLiterateHaskell = True }
|
||||
Right parsed <- readMarkdown def
|
||||
. unlines
|
||||
. replace "___KEYBINDINGS___" keybindings
|
||||
. lines
|
||||
<$> readFile "./man/xmonad.1.markdown"
|
||||
|
||||
Right template <- getDefaultTemplate Nothing "man"
|
||||
writeFile "./man/xmonad.1"
|
||||
. (manHeader ++)
|
||||
. writeMan writeOpts
|
||||
. writeMan def{ writerStandalone = True, writerTemplate = template }
|
||||
$ parsed
|
||||
putStrLn "Documentation created: man/xmonad.1"
|
||||
|
||||
Right template <- getDefaultTemplate Nothing "html"
|
||||
writeFile "./man/xmonad.1.html"
|
||||
. writeHtmlString writeOpts
|
||||
{ writerHeader = "<h1>"++releaseName++"</h1>"++
|
||||
"<p>Section: xmonad manual (1)<br>"++
|
||||
. writeHtmlString def
|
||||
{ writerVariables =
|
||||
[("include-before"
|
||||
,"<h1>"++releaseName++"</h1>"++
|
||||
"<p>Section: xmonad manual (1)<br/>"++
|
||||
"Updated: "++releaseDate++"</p>"++
|
||||
"<hr>"
|
||||
"<hr/>")]
|
||||
, writerStandalone = True
|
||||
, writerTemplate = template
|
||||
, writerTableOfContents = True }
|
||||
$ parsed
|
||||
putStrLn "Documentation created: man/xmonad.1.html"
|
||||
|
33
util/hpcReport.sh
Normal file
33
util/hpcReport.sh
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
if [[ ! ( -e xmonad.cabal && -e dist/hpc/tix/properties/properties.tix ) ]]; then
|
||||
echo "run in the same dir as xmonad.cabal after having run
|
||||
|
||||
cabal configure --enable-tests --enable-library-coverage; cabal test
|
||||
|
||||
"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
propsExclude=$(find tests/Properties -name '*.hs' \
|
||||
| sed -e 's_/_._g' -e 's_.hs$__' -e 's_^tests._--exclude=_' )
|
||||
|
||||
hpcFlags="
|
||||
--hpcdir=dist/hpc/mix/
|
||||
dist/hpc/tix/properties/properties.tix
|
||||
"
|
||||
|
||||
|
||||
if [[ ! (-e dist/hpc/mix/Main.mix) ]]; then
|
||||
mv dist/hpc/mix/properties/* dist/hpc/mix/
|
||||
mv dist/hpc/mix/xmonad-*/xmonad-*/* dist/hpc/mix/xmonad-*/
|
||||
fi
|
||||
|
||||
|
||||
hpc markup --destdir=dist/hpc $hpcFlags > /dev/null
|
||||
echo "see dist/hpc/hpc_index.html
|
||||
"
|
||||
hpc report $hpcFlags
|
110
xmonad.cabal
110
xmonad.cabal
@@ -1,5 +1,5 @@
|
||||
name: xmonad
|
||||
version: 0.9.1
|
||||
version: 0.12
|
||||
homepage: http://xmonad.org
|
||||
synopsis: A tiling window manager
|
||||
description:
|
||||
@@ -17,22 +17,39 @@ license: BSD3
|
||||
license-file: LICENSE
|
||||
author: Spencer Janssen
|
||||
maintainer: xmonad@haskell.org
|
||||
extra-source-files: README TODO CONFIG STYLE tests/loc.hs tests/Properties.hs
|
||||
extra-source-files: README.md CHANGES.md TODO CONFIG STYLE
|
||||
tests/*.hs
|
||||
tests/Properties/*.hs
|
||||
tests/Properties/Layout/*.hs
|
||||
man/xmonad.1.markdown man/xmonad.1 man/xmonad.1.html
|
||||
util/GenerateManpage.hs
|
||||
cabal-version: >= 1.2
|
||||
util/hpcReport.sh
|
||||
cabal-version: >= 1.8
|
||||
bug-reports: https://github.com/xmonad/xmonad/issues
|
||||
build-type: Simple
|
||||
|
||||
data-files: man/xmonad.hs
|
||||
tested-with:
|
||||
GHC==7.6.3,
|
||||
GHC==7.8.4,
|
||||
GHC==7.10.2
|
||||
|
||||
flag small_base
|
||||
description: Choose the new smaller, split-up base package.
|
||||
data-files: man/xmonad.hs, man/xmonad.1, man/xmonad.1.html
|
||||
|
||||
source-repository head
|
||||
type: git
|
||||
location: https://github.com/xmonad/xmonad
|
||||
|
||||
flag testing
|
||||
description: Testing mode, only build minimal components
|
||||
default: False
|
||||
|
||||
flag generatemanpage
|
||||
description: Build the tool for generating the man page
|
||||
default: False
|
||||
manual: True
|
||||
|
||||
library
|
||||
hs-source-dirs: src
|
||||
exposed-modules: XMonad
|
||||
XMonad.Main
|
||||
XMonad.Core
|
||||
@@ -41,39 +58,68 @@ library
|
||||
XMonad.ManageHook
|
||||
XMonad.Operations
|
||||
XMonad.StackSet
|
||||
other-modules: Paths_xmonad
|
||||
|
||||
build-depends: base < 5 && >=3,
|
||||
containers,
|
||||
data-default,
|
||||
directory,
|
||||
extensible-exceptions,
|
||||
filepath,
|
||||
setlocale,
|
||||
mtl,
|
||||
process,
|
||||
unix,
|
||||
utf8-string >= 0.3 && < 1.1,
|
||||
X11>=1.5 && < 1.7
|
||||
|
||||
if true
|
||||
ghc-options: -funbox-strict-fields -Wall
|
||||
|
||||
if impl(ghc >= 6.12.1)
|
||||
ghc-options: -fno-warn-unused-do-bind
|
||||
if impl(ghc < 7.0.0)
|
||||
extensions: UndecidableInstances
|
||||
-- needed for XMonad.Config's instance Default (XConfig a)
|
||||
|
||||
if flag(small_base)
|
||||
build-depends: base < 4 && >=3, containers, directory, process, filepath
|
||||
else
|
||||
build-depends: base < 3
|
||||
build-depends: X11>=1.5.0.0 && < 1.6, mtl, unix
|
||||
|
||||
ghc-options: -funbox-strict-fields -Wall
|
||||
ghc-prof-options: -prof -auto-all
|
||||
extensions: CPP
|
||||
|
||||
if flag(testing)
|
||||
buildable: False
|
||||
|
||||
executable xmonad
|
||||
main-is: Main.hs
|
||||
other-modules: XMonad
|
||||
XMonad.Main
|
||||
XMonad.Core
|
||||
XMonad.Config
|
||||
XMonad.Layout
|
||||
XMonad.ManageHook
|
||||
XMonad.Operations
|
||||
XMonad.StackSet
|
||||
main-is: Main.hs
|
||||
build-depends: base,
|
||||
mtl,
|
||||
unix,
|
||||
X11,
|
||||
xmonad
|
||||
ghc-options: -Wall
|
||||
if impl(ghc >= 6.12.1)
|
||||
ghc-options: -Wall -fno-warn-unused-do-bind
|
||||
|
||||
ghc-options: -funbox-strict-fields -Wall
|
||||
ghc-prof-options: -prof -auto-all
|
||||
extensions: CPP
|
||||
executable generatemanpage
|
||||
main-is: GenerateManpage.hs
|
||||
hs-source-dirs: util
|
||||
if flag(generatemanpage)
|
||||
build-depends: base,
|
||||
Cabal,
|
||||
pandoc,
|
||||
pretty,
|
||||
regex-posix
|
||||
else
|
||||
buildable: False
|
||||
|
||||
if flag(testing)
|
||||
cpp-options: -DTESTING
|
||||
hs-source-dirs: . tests/
|
||||
build-depends: QuickCheck < 2
|
||||
ghc-options: -Werror
|
||||
if flag(testing) && flag(small_base)
|
||||
build-depends: random
|
||||
|
||||
-- note util/hpcReport.sh
|
||||
test-suite properties
|
||||
type: exitcode-stdio-1.0
|
||||
hs-source-dirs: tests
|
||||
build-depends: base,
|
||||
containers,
|
||||
extensible-exceptions,
|
||||
QuickCheck >= 2,
|
||||
X11,
|
||||
xmonad
|
||||
main-is: Properties.hs
|
||||
|
Reference in New Issue
Block a user