138 Commits
v0.10 ... v0.13

Author SHA1 Message Date
Peter Jones
76f4a16258 Add a stack.yaml file for testing and easy Hackage upload 2017-02-10 16:20:01 -07:00
Peter Jones
8f2eb540d7 Update documentation after final release review 2017-02-10 15:48:44 -07:00
Peter Jones
ba2d75b930 Build the man page for 0.13 2017-02-10 15:46:45 -07:00
Peter Jones
acf0652952 Add a release date 2017-02-09 16:25:41 -07:00
Peter Jones
e4d231920c Bump version to 0.13 2017-02-09 16:09:52 -07:00
Peter J. Jones
980828feea Merge pull request #76 from SirBoonami/master
Rare crash fix due to uncaught exception from `getWindowAttributes`
2017-02-09 15:04:41 -07:00
Felix Hirn
2e5ae02059 Updated CHANGES.md 2017-02-09 22:34:26 +01:00
Felix Hirn
50eb1844eb Fixed a crash due to an unhandled getWindowAttributes exception 2017-02-09 22:28:57 +01:00
Peter Jones
f18bda7dc7 Having an executable build script implies force = True' in recompile'
This is slight change to the original implementation.  This version
forces a recompile if XMonad detects a custom build script.  The
previous version took into consideration the time stamps of the source
files.

For a custom build script, the source files may be located in another
location, or there could be dependencies unknown to XMonad.  Better to
just always call the build script and let it work out if something
needs to be built.
2017-02-08 19:48:42 -07:00
Peter Jones
2d8cad02fe Improve function docs for setWindowBorderWithFallback 2017-02-08 16:53:40 -07:00
Peter Jones
2baab28602 When looking for a directory, prefer ~/.xmonad over XDG BDS
This fixes issue #71
2017-02-07 13:28:53 -07:00
Peter J. Jones
ef65f901ce Merge pull request #65 from HebaruSan/focus-race-condition
Don't send focus events for the wrong windows
2017-02-06 17:29:32 -07:00
Peter Jones
f2da028ff9 Add CONTRIBUTING.md and GitHub templates 2017-01-12 12:23:48 -07:00
Brent Yorgey
bad3ce7a5e Merge pull request #62 from pjones/feature/recompile
Support for custom config locations and recompile strategies
2017-01-03 17:56:40 -05:00
Peter Jones
e1c555e3e6 Implement recommendations from Brent's review 2017-01-03 15:46:07 -07:00
Peter Jones
ab20f7df8d Stop using the term XDG unqualified
This change makes it clear that xmonad can use the "XDG Base Directory
Specification" a la the directory package without making any other XDG
claims.
2017-01-03 15:15:26 -07:00
Peter Jones
a70bf6a6a3 Fix another compatibility issue with GHC 7.6.x and 7.8.x 2017-01-03 15:15:26 -07:00
Peter Jones
f58b2399bd Fix compatibility issue with GHC 7.6.x and 7.8.x 2017-01-03 15:15:25 -07:00
Peter Jones
91d23656a3 Fix compile issue when using directory-1.2.3 2017-01-03 15:15:24 -07:00
Peter Jones
d6b6189cc1 Add release notes for changes in this PR 2017-01-03 15:15:23 -07:00
Peter Jones
0248e3c9fa Custom entry point for people using build tools (cabal, stack, etc.)
The xmonadNoArgs function is now exposed as launch.  This allows users
to start xmonad from a custom binary and skip the configuration
complication check. (This is related to issue #62.)

In order to make the new launch function easier to use, resume state
has been removed from the command line and is now stored in a
temporary file.  As a bonus this fixes issue #12.

This commit also includes a method of migrating from older command
line state to the newer state file.  This should allow you have an
older xmonad instance start a newer xmonad and maintain state.
2017-01-03 15:15:22 -07:00
Peter Jones
40fc10b6a5 Allow customization of xmonad directories
Users can specify directory overrides via environment variables.  If
those aren't set, xmonad now prefers XDG directories.  If ~/.xmonad
exists and none of the others do, it will be used instead.

See: xmonad/xmonad#61
2017-01-03 15:15:22 -07:00
Paul Hebble
3a140badf5 Don't send focus events for the wrong windows
Prevents focus switching loop with UpdatePointer.
From https://code.google.com/archive/p/xmonad/issues/200
2016-12-24 21:01:44 -06:00
Brent Yorgey
2b103ede55 Merge pull request #64 from bennofs/fix-63
fix #63: window jumping to origin position when dragging
2016-12-21 13:49:57 -05:00
Benno Fünfstück
4565e2c90e fix #63: window jumping to origin position when dragging
We need to make sure that the StackSet contains the right
position for the window at all times while dragging. Previously,
the window was only placed at a different position. If, for any
reason, a layout refresh happens while the window is being dragged,
then the window will jump back to the old position because the StackSet
still contains the position of the window where it was as the drag started.

This patch fixes that issue by calling float after each mouse movement, which
ensures that the position for the window is updated in the StackSet.

Fixes #63
2016-12-15 18:42:54 +01:00
Peter Jones
285ee2f836 Add build status badge from Travis 2016-12-14 14:46:52 -07:00
Peter J. Jones
7e9c9ccb1f Merge pull request #59 from xmonad/feature/fix-PR9
Fetch border color from the window's colormap
2016-12-14 14:39:08 -07:00
Peter Jones
dc078490d0 Guard most calls to getWindowAttributes since it may throw an exception
This is a continuation of the work done by Adam Sjøgren (@asjo) to
resolve an issue where RGBA windows have transparent borders.  It also
fixes bugs related to windows suddenly disappearing right before
xmonad calls getWindowAttributes.

For more information see xmonad/xmonad#9
2016-12-14 14:18:45 -07:00
Peter Jones
202e239ea4 Refactor xmonad/xmonad#9 and remove explicit exception handling 2016-12-14 14:18:44 -07:00
Peter J. Jones
e159ec36fe Merge pull request #53 from Philonous/rebuild_script
Add support for (re-) building using script
2016-12-13 12:24:41 -07:00
Philipp Balzarek
0b1ccc75ef update CHANGES.md 2016-12-10 11:21:06 +01:00
Philipp Balzarek
b0f9a3d0b9 clean up build-script handling 2016-12-10 11:10:04 +01:00
Philipp Balzarek
75d297a633 Add support for rebuild script
(#46)
2016-12-10 11:10:04 +01:00
Peter J. Jones
5f5e737d9c Merge pull request #57 from xmonad/prof-flag
only turn on -prof -auto-all with profiling flag
2016-12-08 11:00:52 -07:00
geekosaur
a39ed3ee1b Merge pull request #58 from xmonad/manual-testing
cabal: set 'testing' flag to manual
2016-11-15 13:27:18 -05:00
Brent Yorgey
e05a046bca cabal: set 'testing' flag to manual 2016-11-15 05:12:40 -06:00
Brent Yorgey
12ddc800ab only turn on -prof -auto-all with profiling flag 2016-11-09 10:20:51 -06:00
Brent Yorgey
2fab1bb9f5 test GHC 8 on Travis 2016-11-09 10:09:01 -06:00
Brent Yorgey
1b17d1c378 Merge pull request #48 from mschristiansen/tests
Fix failing tests on GHC 8 and reduce warnings.
2016-11-09 11:06:46 -05:00
Mikkel Christiansen
f490ced673 Fix failing tests on GHC 8 and reduce warnings.
Fix test failures on GHC 8 for `abort` and `new_abort` caused by `error`
appending the stack trace to the error message (since base
4.9.0.0)[1]. This fixes #36.

An alternative is to use `errorWithoutStackTrace` (new in base 4.9.0.0),
but this then requires use of CPP for backwards compatibility.

Remove type constraints prompting GHC to warn about redundant
constraints.

Tested with 7.6.3, 7.8.4, 7.10.3, 8.0.1 (all on NixOS).

[1] https://hackage.haskell.org/package/base-4.9.0.0/docs/GHC-Stack.html
2016-11-09 12:03:34 +02:00
Brent Yorgey
0919ecfbde Merge pull request #51 from mgsloan/no-unicode-with-xmessage
Replace unicode characters in ghc error messages
2016-11-08 23:09:30 -05:00
Brent Yorgey
41b7b1341e Merge pull request #33 from dlahoti/patch-1
replace "XXX comment me" with actual (albeit rudimentary) comments in `mouseMoveWindow` and `mouseResizeWindow`
2016-11-08 23:01:07 -05:00
Michael Sloan
0f0aa5e8cb Replace unicode characters in ghc error messages 2016-10-14 17:40:51 -07:00
dlahoti
ad4417c8e0 clicked -> dragged 2016-05-15 10:46:14 -04:00
dlahoti
b0f7643cc5 replace "XXX comment me" with actual comments 2016-05-15 10:32:28 -04:00
Francesco Ariis
8b055621e9 clarify GenerateManpage.hs license 2016-05-12 11:08:37 -05:00
geekosaur
dc6a972bc1 Merge pull request #29 from windo/fix_help
help: correct section for workspace switching
2016-03-18 08:49:31 -04:00
Siim Põder
e4a3eede18 help: correct section for workspace switching 2016-03-18 09:49:15 +00:00
Brent Yorgey
a0ffe7e47d pandoc, etc. should not be a dependency when not building generatemanpage 2015-12-18 14:23:38 -06:00
Brent Yorgey
b00b94fda7 Merge pull request #13 from pjones/release-0.12
Release 0.12
2015-12-18 11:25:07 -06:00
Peter Jones
45a78ba802 Finial tweaks before release 2015-12-14 13:30:55 -07:00
Peter Jones
4c0717e9cc Make it easier to update the man page 2015-12-11 11:19:40 -07:00
Peter Jones
30b4ff5e40 Update development references (darcs, code.google.com, etc.)
* All references to darcs have been updated to git

  * Most Google Code references have been changed to GitHub

  * References to specific issues at code.google.com have been left
    alone

  * Updated the GenerateManpage.hs to work with the latest version of
    pandoc
2015-12-11 11:17:26 -07:00
Peter Jones
b68ebc797a Remove references to development snapshots from darcs 2015-12-11 10:34:31 -07:00
Peter Jones
eb4ef5b23f Update CONFIG instructions: defaultConfig -> def 2015-12-11 10:00:53 -07:00
Peter Jones
73224be21b Bring in entries from https://wiki.haskell.org/Xmonad/Notable_changes_since_0.11 2015-12-09 12:54:14 -07:00
Peter Jones
7e287ec815 Revert a typo that wasn't actually a typo 2015-12-08 16:46:16 -07:00
Peter Jones
60f472faa2 Fix a couple of links (darcs -> git) 2015-12-08 16:28:56 -07:00
Peter Jones
bd72c6e1e2 Add CHANGES.md 2015-12-08 13:50:20 -07:00
Peter Jones
b5402e76d3 Fix some bad formatting 2015-12-08 13:31:54 -07:00
Peter Jones
39dc00b16f Add a file extension to the README file to make GitHub happy 2015-12-08 13:30:11 -07:00
Peter Jones
038f77de5a Reformat the README as proper markdown for GitHub and Hackage 2015-12-08 13:28:08 -07:00
Adam Vogt
59e731ea11 minor update to STYLE and TODO 2015-11-28 16:23:24 -05:00
Brent Yorgey
3ce9dcbbb5 add generated .travis.yml 2015-11-06 14:49:32 -06:00
Brent Yorgey
f25afdab9f .cabal: update tested-with field 2015-11-06 14:49:15 -06:00
brandon s allbery kf8nh
577d5ae968 Add gitignore
Shamelessly ganked from cabal, with obvious project-related files removed.
2015-08-22 13:32:54 -04:00
Josh Holland
29bcd465c2 Bump dependency on utf8-string
The release of utf8-string version 1 just removed some deprecated APIs
that are not used by XMonad.

Fixes #598.
2015-05-05 10:27:18 +00:00
Adam Vogt
307b82a53d Make ~/.xmonad/xmonad-$arch-$os handle args like /usr/bin/xmonad 2015-04-14 18:48:21 +00:00
Adam Vogt
197b0091f8 remove unused FFI pragma 2015-03-27 19:27:59 +00:00
Adam Vogt
69c5dae00d Avoid using instances removed in QuickCheck-2.7
see https://github.com/nick8325/quickcheck/issues/31 for details
2015-03-01 13:50:49 +00:00
Adam Vogt
28c3482411 use setLocale properly
passing in Nothing returns the locale, while Just "" sets the locale according
to environment variables (which is what was being done before).

Thanks to geekosaur & v_v for finding this.
2015-02-27 02:52:28 +00:00
Adam Vogt
73ee008cf6 generate man/xmonad.1 man/xmonad.1.html with a more recent pandoc (1.13) 2014-12-22 00:40:33 +00:00
Adam Vogt
82a1cae123 build with ghc-6.12 again 2014-08-15 03:38:08 +00:00
Adam Vogt
d01b913594 depend on the setlocale package 2014-10-20 14:10:06 +00:00
Adam Vogt
d9e3ebf531 don't encode paths for spawnPID (#348) 2014-10-20 14:04:10 +00:00
Adam Vogt
252c9d5eee stop floating Gimp with the default manageHook
most people have probably moved on to gimp-2.8,
which doesn't really need to be floating
2014-07-04 22:40:25 +00:00
Joey Hess
5f0b1601d5 needs update for mplayer2
xmonad floats mplayer by default. However, Debian has switched to
mplayer2, and so on upgrade, it will stop floating. This can be easily
fixed in the user's config file, but here is a patch that avoids
bothering the user with breakage on upgrade.
2014-07-03 05:43:59 +00:00
Adam Vogt
d60791e3f5 minor formatting of manpage 2014-05-11 17:17:35 +00:00
Adam Vogt
f0054fdde7 update GenerateManpage to pandoc 1.12 2014-05-11 17:14:41 +00:00
Adam Vogt
a9de363b14 make GenerateManpage look in src/ 2014-05-11 17:14:07 +00:00
Daniel Wagner
7eb6ba0126 use only POSIX-standard regexen in GenerateManpage 2014-05-11 15:46:02 +00:00
Adam Vogt
f03d2cdf74 make the check for overflow cleaner 2014-05-07 02:49:30 +00:00
Adam Vogt
16c0cb9a33 make cabal test show a FAIL when at least one test fails 2014-05-03 05:46:29 +00:00
Adam Vogt
fdf3fb2c58 remove unnecessary CPP 2014-05-03 04:48:15 +00:00
Adam Vogt
22d5e7eaa3 add missing files to sdist 2014-05-03 04:47:40 +00:00
Adam Vogt
f837b830fc note -fno-warn-unused-do-bind in STYLE 2014-05-03 04:41:05 +00:00
Adam Vogt
939c0558e6 update README 2014-05-03 04:40:52 +00:00
Adam Vogt
fbd406eb03 add some more forgotten properties 2014-05-03 03:41:44 +00:00
Adam Vogt
ecde376224 make hpc report cover the testsuite too 2014-05-03 03:41:25 +00:00
Adam Vogt
edf3394821 run more tests (and add a couple) 2014-05-03 02:11:03 +00:00
Adam Vogt
20be322b08 put hpc reports in dist/hpc/ 2014-05-02 19:29:07 +00:00
Adam Vogt
f8f53fdff8 add forgotten tests back 2014-05-02 19:28:58 +00:00
Adam Vogt
1da1e2e21e updated hpc 2014-05-02 18:49:04 +00:00
Adam Vogt
4026075bc6 clean up cabal file
We don't support ghc-6.6 anymore, so no need to include this split_base stuff.
2014-05-02 18:07:21 +00:00
Adam Vogt
8863761d66 update testsuite (mostly due Jesper Reenberg)
* use quickcheck2
* run them using cabal's test-suite field
* split up Properties into separate files
2014-05-02 18:01:46 +00:00
Adam Vogt
d67dcd8c4b hack to avoid crash when fewer workspaces than screens (#543) 2014-05-02 17:59:10 +00:00
Adam Vogt
aa84841289 OPTIONS_GHC is the preferred pragma name 2014-05-02 17:57:34 +00:00
Adam Vogt
d10abdcdd0 move library part to src/ 2014-05-02 17:51:59 +00:00
Adam Vogt
3073826dfc avoid warnings from missing Prelude.catch in ghc>=7.6 2014-05-02 05:58:23 +00:00
Adam Vogt
daed0062c6 derive Applicative instance for Query 2014-05-02 05:57:43 +00:00
Adam Vogt
abd737cfb4 Bump version to 0.12
This is to make contrib build failures resulting from by data-default
lead people to do the right thing: update xmonad-core.
2013-07-20 19:21:24 +00:00
Daniel Wagner
e719be4e69 warning police: name userCodeDef's argument defValue instead of def 2013-05-28 16:44:01 +00:00
Daniel Wagner
ec1a20c727 depend on data-default, and deprecate the monomorphic name defaultConfig 2013-05-28 00:35:31 +00:00
Daniel Wagner
8f039ec434 use "modm" instead of "modMask" in the sample config 2013-01-06 17:44:14 +00:00
Daniel Wagner
057fcc5162 define the "help" string in the sample configuration bundled with xmonad 2013-01-06 17:42:54 +00:00
mwlochbaum
8e7634f543 configurableEventMasks 2013-02-05 18:28:58 +00:00
Daniel Wagner
40cb12ce17 Grab all keycodes linked to each keysym, not just one
This patch is based heavily on the one contributed by
svein.ove@aas.no, but updated to avoid causing a conflict and to work
with the newest X11 bindings. The name of the patch (and comment
below) are copied verbatim from his patch.

XKeysymToKeycode only gives the first code bound to a given symbol. To
handle the case where multiple keys are bound to the same symbol,
XKeycodeToKeysym is used instead, searching through all possible
keycodes for each sym.
2013-01-18 22:54:46 +00:00
Adam Vogt
b803fd74a5 Issue 135 use wa_border_width for floating windows (neoraider) 2013-01-15 17:07:15 +00:00
Adam Vogt
d386a230f6 Add flags for call to ghc closing issue 240
The -main-is flag goes back to at least ghc 6.10, and maybe the warning that
this otherwise redundant flag enables (when xmonad.hs isn't a  module Main)
also dates back that far.
2013-01-01 03:50:34 +00:00
Adam Vogt
e87456ab77 bump cabal-version to satisfy hackage 2013-01-01 01:40:56 +00:00
Adam Vogt
cdc22f0849 TAG 0.11 2013-01-01 01:30:31 +00:00
Adam Vogt
70413b2e22 Bump version to 0.11 2012-12-31 18:48:10 +00:00
Adam Vogt
67ffde0dfb Add more metadata to cabal file 2012-12-31 18:46:52 +00:00
Adam Vogt
d904fb1cc4 Update generated manpage (and releaseDate in util/GenerateManpage.hs) 2012-12-31 16:34:03 +00:00
Adam Vogt
4120be8ba0 Copy help binding from XMonad.Config to man/xmonad.hs 2012-12-31 16:33:05 +00:00
Adam Vogt
e015155131 Shorter hyperlinks to hackage in README. 2012-12-31 10:53:58 +00:00
Adam Vogt
4c1536cd18 Add clickJustFocuses option to template configuration. 2012-12-31 10:44:20 +00:00
Adam Vogt
a34a5e979a Add configuration option clickToFocus (issue 225)
To summarize this allows clicks which change the focus to also be passed on to
that window.
2012-01-03 01:39:16 +00:00
conrad.irwin
38faddf9de pass mouse clicks on to focused windows (experimental)
Originally: http://www.haskell.org/pipermail/xmonad/2008-June/005807.html
2011-05-25 04:34:13 +00:00
Adam Vogt
3ab2b28711 HCAR.tex convert line endings. 2012-11-18 19:40:06 +00:00
gwern0
934ff6a562 HCAR.tex: update to Janis's master version per his instructions 2012-11-18 01:03:10 +00:00
Adam Vogt
f8b07d8956 Add generated manpage and html manpage to the repo.
The intention of adding these files to the data-files
is so that they get included in the upload to hackage:
people might like manpage but not have to install pandoc.

It's not really clear that this is the best solution.
2012-11-08 23:11:39 +00:00
Adam Vogt
67d436a4e6 Resolve conflicts Geoff Reedy's window focus hack. 2010-02-22 14:45:12 +00:00
Geoff Reedy
c6fef373dc Give focus to windows that don't set the input hint 2009-10-10 23:19:07 +00:00
Geoff Reedy
2d4f304c0a implement the ICCCM WM_TAKE_FOCUS protocol 2009-06-22 05:19:11 +00:00
Geoff Reedy
1df8ea3d0e track currently processing event 2009-06-22 03:56:49 +00:00
Adam Vogt
490719c035 resolve HCar.tex conflict 2012-11-08 22:35:14 +00:00
gwern0
3cd001e8df HCAR.tex: update with Janis's master version 2012-10-03 19:04:25 +00:00
gwern0
b0dda7b351 HCAR.tex: update per Janis 2012-05-16 21:13:52 +00:00
gwern0
d8495adf0d HCAR.tex: update per Janis 2012-05-13 21:15:22 +00:00
gwern0
06f35a650e Config.hs: implement mod-shift-/ newbie keybinding guide per http://code.google.com/p/xmonad/issues/detail?id=182 2012-01-13 01:04:10 +00:00
gwern0
56f5ecb320 Config.hs: rm commented out keybinding (dead for years) 2012-01-13 00:01:15 +00:00
Adam Vogt
ff674a27e2 Include manual pages in data-files. 2011-12-04 00:11:37 +00:00
Adam Vogt
6c51745122 Correctly identify source files in ~/.lib (David McLean) 2012-04-30 15:42:22 +00:00
Adam Vogt
108c2280ef Address versioning problems related to X11 1.6 release.
Bump version to 0.10.1 since cabal uses hackage dependencies even when the
locally installed package differs.

Allow X11-1.6 dependency.
2012-03-20 00:49:24 +00:00
Adam Vogt
e70b489936 Drop PlainConfig from HCAR.tex: it doesn't exist in contrib.
The code for that moved out to a separate project:
http://braincrater.wordpress.com/2008/08/28/announcing-xmonad-light/
2011-12-11 00:44:05 +00:00
gwern0
450c3a34fe HCAR: update module count, date, versions, maintainer 2011-12-04 02:59:31 +00:00
Adam Vogt
32f416a3c2 Minor updates to supporting files (for 0.10 release). 2011-11-18 23:13:24 +00:00
50 changed files with 3084 additions and 1647 deletions

24
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,24 @@
### Problem Description
Describe the problem you are having, what you expect to happen
instead, and how to reproduce the problem.
### Configuration File
Please include the smallest configuration file that reproduces the
problem you are experiencing:
```haskell
module Main (main) where
import XMonad
main :: IO ()
main = xmonad def
```
### Checklist
- [ ] I've read [CONTRIBUTING.md](https://github.com/xmonad/xmonad/blob/master/CONTRIBUTING.md)
- [ ] I tested my configuration with [xmonad-testing](https://github.com/xmonad/xmonad-testing)

14
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,14 @@
### Description
Include a description for your changes, including the motivation
behind them.
### Checklist
- [ ] I've read [CONTRIBUTING.md](https://github.com/xmonad/xmonad/blob/master/CONTRIBUTING.md)
- [ ] I've confirmed these changes don't belong in xmonad-contrib instead
- [ ] I tested my changes with [xmonad-testing](https://github.com/xmonad/xmonad-testing)
- [ ] I updated the `CHANGES.md` file

26
.gitignore vendored Normal file
View File

@@ -0,0 +1,26 @@
.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/
/cabal.project.local

85
.travis.yml Normal file
View File

@@ -0,0 +1,85 @@
# 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.3
compiler: ": #GHC 7.10.3"
addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3], sources: [hvr-ghc]}}
- env: CABALVER=1.24 GHCVER=8.0.1
compiler: ": #GHC 8.0.1"
addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.1], 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 $HOME/.cabsnap/installplan.txt 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

94
CHANGES.md Normal file
View File

@@ -0,0 +1,94 @@
# Change Log / Release Notes
## 0.13 (February 10, 2017)
### Breaking Changes
* When restarting xmonad, resume state is no longer passed to the
next process via the command line. Instead, a temporary state
file is created and xmonad's state is serialized to that file.
When upgrading to 0.13 from a previous version, the `--resume`
command line option will automatically migrate to a state file.
This fixes issue #12.
### Enhancements
* You can now control which directory xmonad uses for finding your
configuration file and which one is used for storing the compiled
version of your configuration. In order of preference:
1. New environment variables. If you want to use these ensure
you set the correct environment variable and also create the
directory it references:
- `XMONAD_CONFIG_DIR`
- `XMONAD_CACHE_DIR`
- `XMONAD_DATA_DIR`
2. The `~/.xmonad` directory.
3. XDG Base Directory Specification directories, if they exist:
- `XDG_CONFIG_HOME/xmonad`
- `XDG_CACHE_HOME/xmonad`
- `XDG_DATA_HOME/xmonad`
If none of these directories exist then one will be created using
the following logic: If the relevant environment variable
mentioned in step (1) above is set, the referent directory will be
created and used. Otherwise `~/.xmonad` will be created and used.
This fixes a few issues, notably #7 and #56.
* A custom build script can be used when xmonad is given the
`--recompile` command line option. If an executable named `build`
exists in the xmonad configuration directory it will be called
instead of `ghc`. It takes one argument, the name of the
executable binary it must produce.
This fixes #8. (One of two possible custom build solutions. See
the next entry for another solution.)
* For users who build their xmonad configuration using tools such as
cabal or stack, there is another option for executing xmonad.
Instead of running the `xmonad` executable directly, arrange to
have your login manager run your configuration binary instead.
Then, in your binary, use the new `launch` command instead of
`xmonad`.
This will keep xmonad from using its configuration file
checking/compiling code and directly start the window manager
without `exec`ing any other binary.
See the documentation for the `launch` function in `XMonad.Main`
for more details.
Fixes #8. (Second way to have a custom build environment for
XMonad. See previous entry for another solution.)
## 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
View File

@@ -21,7 +21,7 @@ $HOME/.xmonad/xmonad.hs :
import XMonad
main = xmonad $ defaultConfig
main = xmonad $ def
{ borderWidth = 2
, terminal = "urxvt"
, normalBorderColor = "#cccccc"

66
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,66 @@
# Contributing to xmonad and xmonad-contrib
## Before Creating a GitHub Issue
New issue submissions should adhere to the following guidelines:
* Does your issue have to do with [xmonad][], [xmonad-contrib][], or
maybe even with the [X11][] library?
Please submit your issue to the **correct** GitHub repository.
* To help you figure out which repository to submit your issue to,
and to help us resolve the problem you are having, create the
smallest configuration file you can that reproduces the problem.
You may find that the [xmonad-testing][] repository is helpful in
reproducing the problem with a smaller configuration file.
Once you've done that please include the configuration file with
your GitHub issue.
* If possible, use the [xmonad-testing][] repository to test your
configuration with the bleeding-edge development version of xmonad
and xmonad-contrib. We might have already fixed your problem.
## Contributing Changes/Patches
Have a change to xmonad that you want included in the next release?
Awesome! Here are a few things to keep in mind:
* Review the above section about creating GitHub issues.
* It's always best to talk with the community before making any
nontrivial changes to xmonad. There are a couple of ways you can
chat with us:
- Post a message to the [mailing list][ml].
- Join the `#xmonad` IRC channel on `chat.freenode.org`.
* Continue reading this document!
## Expediting Reviews and Merges
Here are some tips for getting your changes merged into xmonad:
* If your changes can go into [xmonad-contrib][] instead
of [xmonad][], please do so. We rarely accept new features to
xmonad. (Not that we don't accept changes to xmonad, just that we
prefer changes to xmonad-contrib instead.)
* Change the fewest files as possible. If it makes sense, submit a
completely new module to xmonad-contrib.
* Your changes should include relevant entries in the `CHANGES.md`
file. Help us communicate changes to the community.
* Make sure you test your changes using the [xmonad-testing][]
repository. Include a new configuration file that shows off your
changes if possible by creating a PR on that repository as well.
[xmonad]: https://github.com/xmonad/xmonad
[xmonad-contrib]: https://github.com/xmonad/xmonad-contrib
[xmonad-testing]: https://github.com/xmonad/xmonad-testing
[x11]: https://github.com/xmonad/X11
[ml]: https://mail.haskell.org/cgi-bin/mailman/listinfo/xmonad

83
MAINTAINERS.md Normal file
View File

@@ -0,0 +1,83 @@
# XMonad Maintainers
## The XMonad Core Team
* Adam Vogt [GitHub][aavogt]
* Brandon S Allbery [GitHub][geekosaur], IRC: `geekosaur`
* Brent Yorgey [GitHub][byorgey], IRC: `byorgey`
* Daniel Wagner [GitHub][dmwit], IRC: `dmwit`
* David Lazar [GitHub][davidlazar]
* Devin Mullins [GitHub][twifkak]
* Peter J. Jones [GitHub][pjones], [Twitter][twitter:pjones], [OpenPGP Key][pgp:pjones], IRC: `pmade`
## Release Procedures
When the time comes to release another version of XMonad and Contrib...
1. Create a release branch (e.g., `release-0.XX`).
This will allow you to separate the release process from main
development. Changes you make on this branch will be merged back
into `master` as one of the last steps.
2. Update the version number in the `*.cabal` files and verify
dependencies and documentation. This includes the `tested-with:`
field.
3. Use the [packdeps][] tool to ensure you have the dependency
versions correct. If you need to update the version of a
dependency then you should rebuild and retest.
4. Review documentation files and make sure they are accurate:
- `README.md`
- `CHANGES.md`
- and the `example-config.hs` in the `xmonad-testing` repo
5. Generate the manpage:
* `cabal configure` with the `-fgeneratemanpage` flag
* Build the project
* Run the `generatemanpage` tool from the top level of this repo
* Review the man page: `man -l man/xmonad.1`
6. Tag the repository with the release version (e.g., `v0.13`)
7. Build the project tarballs (`cabal sdist`)
8. Upload the packages to Hackage (`cabal upload`)
9. Merge the release branches into `master`
10. Update the website:
* Generate and push haddocks with `xmonad-web/gen-docs.sh`
* Check that `tour.html` and `intro.html` are up to date, and
mention all core bindings
11. Update the topic for the IRC channel (`#xmonad`)
12. Send the `announce-0.XX.txt` file to:
- XMonad mailing list
- Haskell Cafe
[packdeps]: http://hackage.haskell.org/package/packdeps
[aavogt]: https://github.com/orgs/xmonad/people/aavogt
[geekosaur]: https://github.com/orgs/xmonad/people/geekosaur
[byorgey]: https://github.com/orgs/xmonad/people/byorgey
[dmwit]: https://github.com/orgs/xmonad/people/dmwit
[davidlazar]: https://github.com/orgs/xmonad/people/davidlazar
[twifkak]: https://github.com/orgs/xmonad/people/twifkak
[pjones]: https://github.com/orgs/xmonad/people/pjones
[twitter:pjones]: https://twitter.com/contextualdev
[pgp:pjones]: http://pgp.mit.edu/pks/lookup?op=get&search=0x526722D1204284CB

91
Main.hs
View File

@@ -16,94 +16,5 @@ module Main (main) where
import XMonad
import Control.Monad (unless)
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
["--replace"] -> launch
["--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" :
" --replace Replace the running window manager with xmonad" :
" --restart Request a running xmonad process to restart" :
#ifdef TESTING
" --run-tests Run the test suite" :
#endif
[]
-- | Build "~\/.xmonad\/xmonad.hs" with ghc, then execute it. If there are no
-- errors, this function does not return. An exception is raised in any of
-- 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
View File

@@ -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

121
README.md Normal file
View File

@@ -0,0 +1,121 @@
# xmonad: A Tiling Window Manager
[![Build Status](https://travis-ci.org/xmonad/xmonad.svg?branch=master)](https://travis-ci.org/xmonad/xmonad)
[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
If you built XMonad using `cabal` then add:
exec $HOME/.cabal/bin/xmonad
to the last line of your `.xsession` or `.xinitrc` file.
## Configuring
See the [CONFIG][] document and the [example configuration file][example-config].
## 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/
[example-config]: https://github.com/xmonad/xmonad-testing/blob/master/example-config.hs
[config]: https://github.com/xmonad/xmonad/blob/master/CONFIG

5
STYLE
View File

@@ -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.

23
TODO
View File

@@ -1,23 +0,0 @@
- 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 manpage, generate html manpage
* double check README build instructions
* test core with 6.6 and 6.8
* bump xmonad.cabal version and X11 version
* upload X11 and xmonad to Hackage
* update links to hackage in download.html
* update #xmonad topic
* check examples/text in user-facing Config.hs
* check tour.html and intro.html are up to date, and mention all core bindings
* confirm template config is type correct

1
cabal.project Normal file
View File

@@ -0,0 +1 @@
packages: ./

View File

@@ -1,7 +1,7 @@
% xmonad-Gx.tex
\begin{hcarentry}{xmonad}
\label{xmonad}
\report{Gwern Branwen}%05/10
\report{Gwern Branwen}%11/11
\status{active development}
\makeheader
@@ -15,16 +15,19 @@ dynamically, and different layouts may be used on each workspace.
Xinerama is fully supported, allowing windows to be tiled on several
physical screens.
Development since the last report has continued apace, with versions
0.8, 0.8.1, 0.9 and 0.9.1 released, with simultaneous releases of the
XMonadContrib library of customizations and extensions, which has now
grown to no less than 205 modules encompassing a dizzying array of features.
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.7}
\item \url{http://haskell.org/haskellwiki/Xmonad/Notable_changes_since_0.8}
\item \url{http://haskell.org/haskellwiki/Xmonad/Notable_changes_since_0.9}
% \item \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
@@ -55,9 +58,9 @@ Binary packages of XMonad and XMonadContrib are available for all major Linux di
\item Homepage:
\url{http://xmonad.org/}
\item Darcs source:
\item Git source:
\texttt{darcs get} \url{http://code.haskell.org/xmonad}
\texttt{git clone} \url{https://github.com/xmonad/xmonad.git}
\item IRC channel:
\verb+#xmonad @@ irc.freenode.org+

282
man/xmonad.1 Normal file
View File

@@ -0,0 +1,282 @@
.TH xmonad 1 "31 December 2012" xmonad-0.13 "xmonad manual".\" Automatically generated by Pandoc 1.19.2.1
.\"
.TH "" "" "" "" ""
.hy
.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
View 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.13</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 &quot;workspace&quot;. 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 &quot;function1: Not implemented yet!&quot;</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>

View File

@@ -76,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-
@@ -89,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.
@@ -99,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

View File

@@ -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"]

View File

@@ -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)
@@ -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 "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,7 +239,6 @@ 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
-- mod-button1 %! Set the window to floating mode and move by dragging
@@ -232,8 +252,8 @@ mouseBindings (XConfig {XMonad.modMask = modMask}) = M.fromList
-- 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",
"",
"-- Workspaces & screens",
"mod-[1..9] Switch to workSpace N",
"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"]

View File

@@ -25,21 +25,25 @@ module XMonad.Core (
StateExtension(..), ExtensionClass(..),
runX, catchX, userCode, userCodeDef, io, catchIO, installSignalHandlers, uninstallSignalHandlers,
withDisplay, withWindowSet, isRoot, runOnWorkspaces,
getAtom, spawn, spawnPID, xfork, getXMonadDir, recompile, trace, whenJust, whenX,
atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, ManageHook, Query(..), runQuery
getAtom, spawn, spawnPID, xfork, recompile, trace, whenJust, whenX,
getXMonadDir, getXMonadCacheDir, getXMonadDataDir, stateFileName,
atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, atom_WM_TAKE_FOCUS, withWindowAttributes,
ManageHook, Query(..), runQuery
) where
import XMonad.StackSet hiding (modify)
import Prelude hiding ( catch )
import Codec.Binary.UTF8.String (encodeString)
import Control.Exception.Extensible (catch, fromException, try, bracket, throw, finally, SomeException(..))
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
import System.Posix.Env (getEnv)
import System.Posix.Process (executeFile, forkProcess, getAnyProcessStatus, createSession)
import System.Posix.Signals
import System.Posix.IO
@@ -48,7 +52,7 @@ import System.Process
import System.Directory
import System.Exit
import Graphics.X11.Xlib
import Graphics.X11.Xlib.Extras (Event)
import Graphics.X11.Xlib.Extras (getWindowAttributes, WindowAttributes, Event)
import Data.Typeable
import Data.List ((\\))
import Data.Maybe (isJust,fromMaybe)
@@ -86,6 +90,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
@@ -108,6 +114,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
}
@@ -144,9 +155,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)
deriving (Functor, Monad, MonadReader Window, MonadIO)
deriving (Functor, Applicative, Monad, MonadReader Window, MonadIO)
runQuery :: Query a -> Window -> X a
runQuery (Query m) w = runReaderT m w
@@ -155,6 +169,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)
@@ -166,7 +183,7 @@ 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 fromException e of
(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'
@@ -180,7 +197,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
@@ -193,6 +210,12 @@ withDisplay f = asks display >>= f
withWindowSet :: (WindowSet -> X a) -> X a
withWindowSet f = gets windowset >>= f
-- | Safely access window attributes.
withWindowAttributes :: Display -> Window -> (WindowAttributes -> X ()) -> X ()
withWindowAttributes dpy win f = do
wa <- userCode (io $ getWindowAttributes dpy win)
catchX (whenJust wa f) (return ())
-- | True if the given window is the root window
isRoot :: Window -> X Bool
isRoot w = (w==) <$> asks theRoot
@@ -202,10 +225,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
@@ -381,7 +405,7 @@ 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` \(SomeException 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.
@@ -392,7 +416,7 @@ spawn x = spawnPID x >> return ()
-- | Like 'spawn', but returns the 'ProcessID' of the launched application
spawnPID :: MonadIO m => String -> m ProcessID
spawnPID x = xfork $ executeFile "/bin/sh" False ["-c", encodeString x] Nothing
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
@@ -416,48 +440,153 @@ runOnWorkspaces job = do
$ current ws : visible ws
modify $ \s -> s { windowset = ws { current = c, visible = v, hidden = h } }
-- | Return the path to @~\/.xmonad@.
-- | Return the path to the xmonad configuration directory. This
-- directory is where user configuration files are stored (e.g, the
-- xmonad.hs file). You may also create a @lib@ subdirectory in the
-- configuration directory and the default recompile command will add
-- it to the GHC include path.
--
-- Several directories are considered. In order of
-- preference:
--
-- 1. The directory specified in the @XMONAD_CONFIG_DIR@ environment variable.
-- 2. The @~\/.xmonad@ directory.
-- 3. The @XDG_CONFIG_HOME/xmonad@ directory.
--
-- The first directory that exists will be used. If none of the
-- directories exist then (1) will be used if it is set, otherwise (2)
-- will be used. Either way, a directory will be created if necessary.
getXMonadDir :: MonadIO m => m String
getXMonadDir = io $ getAppUserDataDirectory "xmonad"
getXMonadDir =
findFirstDirWithEnv "XMONAD_CONFIG_DIR"
[ getAppUserDataDirectory "xmonad"
, getXdgDirectory XdgConfig "xmonad"
]
-- | 'recompile force', recompile @~\/.xmonad\/xmonad.hs@ when any of the
-- following apply:
-- | Return the path to the xmonad cache directory. This directory is
-- used to store temporary files that can easily be recreated. For
-- example, the XPrompt history file.
--
-- Several directories are considered. In order of preference:
--
-- 1. The directory specified in the @XMONAD_CACHE_DIR@ environment variable.
-- 2. The @~\/.xmonad@ directory.
-- 3. The @XDG_CACHE_HOME/xmonad@ directory.
--
-- The first directory that exists will be used. If none of the
-- directories exist then (1) will be used if it is set, otherwise (2)
-- will be used. Either way, a directory will be created if necessary.
getXMonadCacheDir :: MonadIO m => m String
getXMonadCacheDir =
findFirstDirWithEnv "XMONAD_CACHE_DIR"
[ getAppUserDataDirectory "xmonad"
, getXdgDirectory XdgCache "xmonad"
]
-- | Return the path to the xmonad data directory. This directory is
-- used by XMonad to store data files such as the run-time state file
-- and the configuration binary generated by GHC.
--
-- Several directories are considered. In order of preference:
--
-- 1. The directory specified in the @XMONAD_DATA_DIR@ environment variable.
-- 2. The @~\/.xmonad@ directory.
-- 3. The @XDG_DATA_HOME/xmonad@ directory.
--
-- The first directory that exists will be used. If none of the
-- directories exist then (1) will be used if it is set, otherwise (2)
-- will be used. Either way, a directory will be created if necessary.
getXMonadDataDir :: MonadIO m => m String
getXMonadDataDir =
findFirstDirWithEnv "XMONAD_DATA_DIR"
[ getAppUserDataDirectory "xmonad"
, getXdgDirectory XdgData "xmonad"
]
-- | Helper function that will find the first existing directory and
-- return its path. If none of the directories can be found, create
-- and return the first from the list. If the list is empty this
-- function returns the historical @~\/.xmonad@ directory.
findFirstDirOf :: MonadIO m => [IO FilePath] -> m FilePath
findFirstDirOf [] = findFirstDirOf [getAppUserDataDirectory "xmonad"]
findFirstDirOf possibles = do
found <- go possibles
case found of
Just path -> return path
Nothing -> do
primary <- io (head possibles)
io (createDirectoryIfMissing True primary)
return primary
where
go [] = return Nothing
go (x:xs) = do
dir <- io x
exists <- io (doesDirectoryExist dir)
if exists then return (Just dir) else go xs
-- | Simple wrapper around @findFirstDirOf@ that allows the primary
-- path to be specified by an environment variable.
findFirstDirWithEnv :: MonadIO m => String -> [IO FilePath] -> m FilePath
findFirstDirWithEnv envName paths = do
envPath' <- io (getEnv envName)
case envPath' of
Nothing -> findFirstDirOf paths
Just envPath -> findFirstDirOf (return envPath:paths)
-- | Get the name of the file used to store the xmonad window state.
stateFileName :: (Functor m, MonadIO m) => m FilePath
stateFileName = (</> "xmonad.state") <$> getXMonadDataDir
-- | 'recompile force', recompile the xmonad configuration file when
-- any of the following apply:
--
-- * force is 'True'
--
-- * the xmonad executable does not exist
--
-- * the xmonad executable is older than xmonad.hs or any file in
-- ~\/.xmonad\/lib
-- the @lib@ directory (under the configuration directory).
--
-- The -i flag is used to restrict recompilation to the xmonad.hs file only,
-- and any files in the ~\/.xmonad\/lib directory.
-- and any files in the aforementioned @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
-- that file is spawned.
-- Compilation errors (if any) are logged to the @xmonad.errors@ file
-- in the xmonad data directory. If GHC indicates failure with a
-- non-zero exit code, an xmessage displaying that file is spawned.
--
-- 'False' is returned if there are compilation errors.
--
recompile :: MonadIO m => Bool -> m Bool
recompile force = io $ do
dir <- getXMonadDir
cfgdir <- getXMonadDir
datadir <- getXMonadDataDir
let binn = "xmonad-"++arch++"-"++os
bin = dir </> binn
base = dir </> "xmonad"
err = base ++ ".errors"
src = base ++ ".hs"
lib = dir </> "lib"
bin = datadir </> binn
err = datadir </> "xmonad.errors"
src = cfgdir </> "xmonad.hs"
lib = cfgdir </> "lib"
buildscript = cfgdir </> "build"
libTs <- mapM getModTime . Prelude.filter isSource =<< allFiles lib
srcT <- getModTime src
binT <- getModTime bin
if force || any (binT <) (srcT : libTs)
useBuildscript <- do
exists <- doesFileExist buildscript
if exists then isExecutable buildscript else return False
if force || useBuildscript || any (binT <) (srcT : libTs)
then do
-- temporarily disable SIGCHLD ignoring:
uninstallSignalHandlers
status <- bracket (openFile err WriteMode) hClose $ \h ->
waitForProcess =<< runProcess "ghc" ["--make", "xmonad.hs", "-i", "-ilib", "-fforce-recomp", "-v0", "-o",binn] (Just dir)
Nothing Nothing Nothing (Just h)
status <- bracket (openFile err WriteMode) hClose $ \errHandle ->
waitForProcess =<< if useBuildscript
then compileScript bin cfgdir buildscript errHandle
else compileGHC bin cfgdir errHandle
-- re-enable SIGCHLD:
installSignalHandlers
@@ -472,17 +601,36 @@ recompile force = io $ do
-- nb, the ordering of printing, then forking, is crucial due to
-- lazy evaluation
hPutStrLn stderr msg
forkProcess $ executeFile "xmessage" True ["-default", "okay", msg] Nothing
forkProcess $ executeFile "xmessage" True ["-default", "okay", replaceUnicode msg] Nothing
return ()
return (status == ExitSuccess)
else return True
where getModTime f = catch (Just <$> getModificationTime f) (\(SomeException _) -> 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
isExecutable f = E.catch (executable <$> getPermissions f) (\(SomeException _) -> return False)
allFiles t = do
let prep = map (t</>) . Prelude.filter (`notElem` [".",".."])
cs <- prep <$> catch (getDirectoryContents t) (\(SomeException _) -> return [])
cs <- prep <$> E.catch (getDirectoryContents t) (\(SomeException _) -> return [])
ds <- filterM doesDirectoryExist cs
concat . ((cs \\ ds):) <$> mapM allFiles ds
-- Replace some of the unicode symbols GHC uses in its output
replaceUnicode = map $ \c -> case c of
'\8226' -> '*' --
'\8216' -> '`' --
'\8217' -> '`' --
_ -> c
compileGHC bin dir errHandle =
runProcess "ghc" ["--make"
, "xmonad.hs"
, "-i"
, "-ilib"
, "-fforce-recomp"
, "-main-is", "main"
, "-v0"
, "-o", bin
] (Just dir) Nothing Nothing Nothing (Just errHandle)
compileScript bin dir script errHandle =
runProcess script [bin] (Just dir) Nothing Nothing Nothing (Just errHandle)
-- | Conditionally run an action, using a @Maybe a@ to decide.
whenJust :: Monad m => Maybe a -> (a -> m ()) -> m ()

View File

@@ -137,7 +137,7 @@ data ChangeLayout = FirstLayout | NextLayout deriving (Eq, Show, Typeable)
instance Message ChangeLayout
-- | The layout choice combinator
(|||) :: (LayoutClass l a, LayoutClass r a) => l a -> r a -> Choose l r a
(|||) :: l a -> r a -> Choose l r a
(|||) = Choose L
infixr 5 |||

View File

@@ -1,4 +1,4 @@
{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, ForeignFunctionInterface #-}
{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts #-}
----------------------------------------------------------------------------
-- |
-- Module : XMonad.Main
@@ -13,9 +13,10 @@
--
-----------------------------------------------------------------------------
module XMonad.Main (xmonad) where
module XMonad.Main (xmonad, launch) where
import Control.Arrow (second)
import System.Locale.SetLocale
import qualified Control.Exception.Extensible as E
import Data.Bits
import Data.List ((\\))
import Data.Function
@@ -26,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,24 +37,133 @@ import qualified XMonad.StackSet as W
import XMonad.Operations
import System.IO
import System.Directory
import System.Info
import System.Environment
import System.Posix.Process (executeFile)
import System.Exit (exitFailure)
import System.FilePath
import Paths_xmonad (version)
import Data.Version (showVersion)
import Graphics.X11.Xinerama (compiledWithXinerama)
------------------------------------------------------------------------
-- Locale support
#include <locale.h>
foreign import ccall unsafe "locale.h setlocale"
c_setlocale :: CInt -> Ptr CChar -> IO (Ptr CChar)
------------------------------------------------------------------------
-- |
-- The main entry point
--
-- | The 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 initxmc = do
xmonad conf = do
installSignalHandlers -- important to ignore SIGCHLD to avoid zombies
let launch' args = do
catchIO buildLaunch
conf' @ XConfig { layoutHook = Layout l }
<- handleExtraArgs conf args conf{ layoutHook = Layout (layoutHook conf) }
withArgs [] $ launch (conf' { layoutHook = l })
args <- getArgs
case args of
("--resume": ws : xs : args') -> migrateState ws xs >> launch' args'
["--help"] -> usage
["--recompile"] -> recompile True >>= flip unless exitFailure
["--restart"] -> sendRestart
["--version"] -> putStrLn $ unwords shortVersion
["--verbose-version"] -> putStrLn . unwords $ shortVersion ++ longVersion
"--replace" : args' -> sendReplace >> launch' args'
_ -> launch' 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 the xmonad configuration file 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 the configuration file and executable are 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 <- getXMonadDataDir
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
-- | Entry point into xmonad for custom builds.
--
-- This function isn't meant to be called by the typical xmonad user
-- because it:
--
-- * Does not process any command line arguments.
--
-- * Therefore doesn't know how to restart a running xmonad.
--
-- * Does not compile your configuration file since it assumes it's
-- actually running from within your compiled configuration.
--
-- Unless you know what you are doing, you should probably be using
-- the 'xmonad' function instead.
--
-- However, if you are using a custom build environment (such as
-- stack, cabal, make, etc.) you will likely want to call this
-- function instead of 'xmonad'. You probably also want to have a key
-- binding to the 'XMonad.Operations.restart` function that restarts
-- your custom binary with the resume flag set to @True@.
launch :: (LayoutClass l Window, Read (l Window)) => XConfig l -> IO ()
launch initxmc = 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:
@@ -68,16 +173,11 @@ xmonad initxmc = do
rootw <- rootWindow dpy dflt
args <- getArgs
when ("--replace" `elem` args) $ replace dpy dflt rootw
-- If another WM is running, a BadAccess error will be returned. The
-- default error handler will write the exception to stderr and exit with
-- an error.
selectInput dpy rootw $ substructureRedirectMask .|. substructureNotifyMask
.|. enterWindowMask .|. leaveWindowMask .|. structureNotifyMask
.|. buttonPressMask
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
@@ -85,32 +185,21 @@ xmonad initxmc = do
xSetErrorHandler -- in C, I'm too lazy to write the binding: dons
xinesc <- getCleanedScreenInfo dpy
nbc <- do v <- initColor dpy $ normalBorderColor xmc
~(Just nbc_) <- initColor dpy $ normalBorderColor Default.defaultConfig
~(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
let layout = layoutHook xmc
lreads = readsLayout layout
initialWinset = new layout (workspaces xmc) $ map SD xinesc
maybeRead reads' s = case reads' s of
[(x, "")] -> Just x
_ -> Nothing
winset = fromMaybe initialWinset $ do
("--resume" : s : _) <- return args
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
vals <- maybeRead reads dyns
return . M.fromList . map (second Left) $ vals
initialWinset = let padToLen n xs = take (max n (length xs)) $ xs ++ repeat ""
in new layout (padToLen (length xinesc) (workspaces xmc)) $ map SD xinesc
cf = XConf
{ display = dpy
@@ -121,18 +210,29 @@ xmonad initxmc = do
, keyActions = keys xmc xmc
, buttonActions = mouseBindings xmc xmc
, mouseFocused = False
, mousePosition = Nothing }
, mousePosition = Nothing
, currentEvent = Nothing }
st = XState
{ windowset = initialWinset
, numberlockMask = 0
, numberlockMask = 0
, mapped = S.empty
, waitingUnmap = M.empty
, dragging = Nothing
, extensibleState = extState
, extensibleState = M.empty
}
allocaXEvent $ \e ->
runX cf st $ do
-- check for serialized state in a file.
serializedSt <- do
path <- stateFileName
exists <- io (doesFileExist path)
if exists then readStateFile initxmc else return Nothing
-- restore extensibleState if we read it from a file.
let extst = maybe M.empty extensibleState serializedSt
modify (\s -> s {extensibleState = extst})
setNumlockMask
grabKeys
@@ -147,6 +247,7 @@ xmonad initxmc = do
-- those windows. Remove all windows that are no longer top-level
-- children of the root, they may have disappeared since
-- restarting.
let winset = maybe initialWinset windowset serializedSt
windows . const . foldr W.delete winset $ W.allWindows winset \\ ws
-- manage the as-yet-unmanaged windows
@@ -163,7 +264,7 @@ xmonad initxmc = do
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]
@@ -197,10 +298,10 @@ handle (KeyEvent {ev_event_type = t, ev_state = m, ev_keycode = code})
-- manage a new window
handle (MapRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
wa <- io $ getWindowAttributes dpy w -- ignore override windows
-- need to ignore mapping requests by managed windows not on the current workspace
managed <- isClient w
when (not (wa_override_redirect wa) && not managed) $ do manage w
withWindowAttributes dpy w $ \wa -> do -- ignore override windows
-- need to ignore mapping requests by managed windows not on the current workspace
managed <- isClient w
when (not (wa_override_redirect wa) && not managed) $ manage w
-- window destroyed, unmanage it
-- window gone, unmanage it
@@ -247,19 +348,27 @@ 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
-- True in the user's config.
handle e@(CrossingEvent {ev_window = w, ev_event_type = t})
| t == enterNotify && ev_mode e == notifyNormal
= whenX (asks $ focusFollowsMouse . config) (focus w)
= whenX (asks $ focusFollowsMouse . config) $ do
dpy <- asks display
root <- asks theRoot
(_, _, w', _, _, _, _, _) <- io $ queryPointer dpy root
when (w == w') (focus w)
-- left a window, check if we need to focus root
handle e@(CrossingEvent {ev_event_type = t})
@@ -270,8 +379,6 @@ handle e@(CrossingEvent {ev_event_type = t})
-- configure a window
handle e@(ConfigureRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
ws <- gets windowset
wa <- io $ getWindowAttributes dpy w
bw <- asks (borderWidth . config)
if M.member w (floating ws)
@@ -285,7 +392,7 @@ handle e@(ConfigureRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
, wc_sibling = ev_above e
, wc_stack_mode = ev_detail e }
when (member w ws) (float w)
else io $ allocaXEvent $ \ev -> do
else withWindowAttributes dpy w $ \wa -> io $ allocaXEvent $ \ev -> do
setEventType ev configureNotify
setConfigureEvent ev w w
(wa_x wa) (wa_y wa) (wa_width wa)
@@ -319,7 +426,7 @@ handle e = broadcastMessage e -- trace (eventName e) -- ignoring
scan :: Display -> Window -> IO [Window]
scan dpy rootw = do
(_, _, ws) <- queryTree dpy rootw
filterM ok ws
filterM (\w -> ok w `E.catch` skip) ws
-- TODO: scan for windows that are either 'IsViewable' or where WM_STATE ==
-- Iconic
where ok w = do wa <- getWindowAttributes dpy w
@@ -331,6 +438,9 @@ scan dpy rootw = do
return $ not (wa_override_redirect wa)
&& (wa_map_state wa == waIsViewable || ic)
skip :: E.SomeException -> IO Bool
skip _ = return False
setNumlockMask :: X ()
setNumlockMask = do
dpy <- asks display
@@ -348,13 +458,18 @@ 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 ()

View File

@@ -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.Extensible (bracket, catch, SomeException(..))
import Control.Exception.Extensible (bracket, SomeException(..))
import qualified Control.Exception.Extensible as E
import Control.Monad.Reader
import Data.Maybe
import Data.Monoid
@@ -74,10 +74,10 @@ title = ask >>= \w -> liftX $ do
let
getProp =
(internAtom d "_NET_WM_NAME" False >>= getTextProperty d w)
`catch` \(SomeException _) -> 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` \(SomeException _) -> return ""
io $ bracket getProp (xFree . tp_value) extract `E.catch` \(SomeException _) -> return ""
-- | Return the application name.
appName :: Query String

View File

@@ -1,6 +1,5 @@
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
-- --------------------------------------------------------------------------
-- |
-- Module : XMonad.Operations
@@ -24,16 +23,19 @@ 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
import Control.Applicative
import Control.Arrow (second)
import Control.Monad.Reader
import Control.Monad.State
import qualified Control.Exception.Extensible as C
import System.IO
import System.Directory
import System.Posix.Process (executeFile)
import Graphics.X11.Xlib
import Graphics.X11.Xinerama (getScreenInfo)
@@ -111,7 +113,10 @@ windows f = do
mapM_ setInitialProperties newwindows
whenJust (W.peek old) $ \otherw -> io $ setWindowBorder d otherw nbc
whenJust (W.peek old) $ \otherw -> do
nbs <- asks (normalBorderColor . config)
setWindowBorderWithFallback d otherw nbs nbc
modify (\s -> s { windowset = ws })
-- notify non visibility
@@ -151,7 +156,9 @@ windows f = do
mapM_ (uncurry tileWindow) rects
whenJust (W.peek ws) $ \w -> io $ setWindowBorder d w fbc
whenJust (W.peek ws) $ \w -> do
fbs <- asks (focusedBorderColor . config)
setWindowBorderWithFallback d w fbs fbc
mapM_ reveal visible
setTopFocus
@@ -181,12 +188,26 @@ setWMState w v = withDisplay $ \dpy -> do
a <- atom_WM_STATE
io $ changeProperty32 dpy w a a propModeReplace [fromIntegral v, fromIntegral none]
-- | Set the border color using the window's color map, if possible,
-- otherwise fallback to the color in @Pixel@.
setWindowBorderWithFallback :: Display -> Window -> String -> Pixel -> X ()
setWindowBorderWithFallback dpy w color basic = io $
C.handle fallback $ do
wa <- getWindowAttributes dpy w
pixel <- color_pixel . fst <$> allocNamedColor dpy (wa_colormap wa) color
setWindowBorder dpy w pixel
where
fallback :: C.SomeException -> IO ()
fallback e = do hPrint stderr e >> hFlush stderr
setWindowBorder dpy w basic
-- | 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,15 +222,11 @@ reveal w = withDisplay $ \d -> do
io $ mapWindow d w
whenX (isClient w) $ modify (\s -> s { mapped = S.insert w (mapped s) })
-- | The client events that xmonad is interested in
clientMask :: EventMask
clientMask = structureNotifyMask .|. enterWindowMask .|. propertyChangeMask
-- | 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
@@ -236,10 +253,10 @@ clearEvents mask = withDisplay $ \d -> io $ do
-- | tileWindow. Moves and resizes w such that it fits inside the given
-- rectangle, including its border.
tileWindow :: Window -> Rectangle -> X ()
tileWindow w r = withDisplay $ \d -> do
bw <- (fromIntegral . wa_border_width) <$> io (getWindowAttributes d w)
tileWindow w r = withDisplay $ \d -> withWindowAttributes d w $ \wa -> do
-- give all windows at least 1x1 pixels
let least x | x <= bw*2 = 1
let bw = fromIntegral $ wa_border_width wa
least x | x <= bw*2 = 1
| otherwise = x - bw*2
io $ moveResizeWindow d w (rect_x r) (rect_y r)
(least $ rect_width r) (least $ rect_height r)
@@ -283,11 +300,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
-- ---------------------------------------------------------------------
@@ -325,7 +345,27 @@ setFocusX w = withWindowSet $ \ws -> do
-- If we ungrab buttons on the root window, we lose our mouse bindings.
whenX (not <$> isRoot w) $ setButtonGrab False w
io $ setInputFocus dpy w revertToPointerRoot 0
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
@@ -403,6 +443,69 @@ initColor dpy c = C.handle (\(C.SomeException _) -> return Nothing) $
------------------------------------------------------------------------
-- | A type to help serialize xmonad's state to a file.
data StateFile = StateFile
{ sfWins :: W.StackSet WorkspaceId String Window ScreenId ScreenDetail
, sfExt :: [(String, String)]
} deriving (Show, Read)
-- | Write the current window state (and extensible state) to a file
-- so that xmonad can resume with that state intact.
writeStateToFile :: X ()
writeStateToFile = do
let maybeShow (t, Right (PersistentExtension ext)) = Just (t, show ext)
maybeShow (t, Left str) = Just (t, str)
maybeShow _ = Nothing
wsData = W.mapLayout show . windowset
extState = catMaybes . map maybeShow . M.toList . extensibleState
path <- stateFileName
stateData <- gets (\s -> StateFile (wsData s) (extState s))
catchIO (writeFile path $ show stateData)
-- | Read the state of a previous xmonad instance from a file and
-- return that state.
readStateFile :: (LayoutClass l Window, Read (l Window)) => XConfig l -> X (Maybe XState)
readStateFile xmc = do
path <- stateFileName
raw <- userCode $ io (readFile path)
return $ do
sf <- maybeRead reads =<< raw
let winset = W.ensureTags layout (workspaces xmc) $ W.mapLayout (fromMaybe layout . maybeRead lreads) (sfWins sf)
extState = M.fromList . map (second Left) $ sfExt sf
return XState { windowset = winset
, numberlockMask = 0
, mapped = S.empty
, waitingUnmap = M.empty
, dragging = Nothing
, extensibleState = extState
}
where
layout = Layout (layoutHook xmc)
lreads = readsLayout layout
maybeRead reads' s = case reads' s of
[(x, "")] -> Just x
_ -> Nothing
-- | Migrate state from a previously running xmonad instance that used
-- the older @--resume@ technique.
{-# DEPRECATED migrateState "will be removed some point in the future." #-}
migrateState :: (Functor m, MonadIO m) => String -> String -> m ()
migrateState ws xs = do
io (putStrLn "WARNING: --resume is no longer supported.")
whenJust stateData $ \s -> do
path <- stateFileName
catchIO (writeFile path $ show s)
where
stateData = StateFile <$> maybeRead ws <*> maybeRead xs
maybeRead s = case reads s of
[(x, "")] -> Just x
_ -> Nothing
-- | @restart name resume@. Attempt to restart xmonad by executing the program
-- @name@. If @resume@ is 'True', restart with the current window state.
-- When executing another window manager, @resume@ should be 'False'.
@@ -410,13 +513,8 @@ restart :: String -> Bool -> X ()
restart prog resume = do
broadcastMessage ReleaseResources
io . flush =<< asks display
let wsData = show . W.mapLayout show . windowset
maybeShow (t, Right (PersistentExtension ext)) = Just (t, show ext)
maybeShow (t, Left str) = Just (t, str)
maybeShow _ = Nothing
extState = return . show . catMaybes . map maybeShow . M.toList . extensibleState
args <- if resume then gets (\s -> "--resume":wsData s:extState s) else return []
catchIO (executeFile prog True args Nothing)
when resume writeStateToFile
catchIO (executeFile prog True [] Nothing)
------------------------------------------------------------------------
-- | Floating layer support
@@ -424,20 +522,27 @@ restart prog resume = do
-- | Given a window, find the screen it is located on, and compute
-- the geometry of that window wrt. that screen.
floatLocation :: Window -> X (ScreenId, W.RationalRect)
floatLocation w = withDisplay $ \d -> do
ws <- gets windowset
wa <- io $ getWindowAttributes d w
bw <- fi <$> asks (borderWidth . config)
sc <- fromMaybe (W.current ws) <$> pointScreen (fi $ wa_x wa) (fi $ wa_y wa)
floatLocation w =
catchX go $ do
-- Fallback solution if `go' fails. Which it might, since it
-- calls `getWindowAttributes'.
sc <- W.current <$> gets windowset
return (W.screen sc, W.RationalRect 0 0 1 1)
let sr = screenRect . W.screenDetail $ sc
rr = W.RationalRect ((fi (wa_x wa) - fi (rect_x sr)) % fi (rect_width sr))
((fi (wa_y wa) - fi (rect_y sr)) % fi (rect_height sr))
(fi (wa_width wa + bw*2) % fi (rect_width sr))
(fi (wa_height wa + bw*2) % fi (rect_height sr))
return (W.screen sc, rr)
where fi x = fromIntegral x
go = withDisplay $ \d -> do
ws <- gets windowset
wa <- io $ getWindowAttributes d w
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
rr = W.RationalRect ((fi (wa_x wa) - fi (rect_x sr)) % fi (rect_width sr))
((fi (wa_y wa) - fi (rect_y sr)) % fi (rect_height sr))
(fi (wa_width wa + bw*2) % fi (rect_width sr))
(fi (wa_height wa + bw*2) % fi (rect_height sr))
return (W.screen sc, rr)
-- | Given a point, determine the screen (if any) that contains it.
pointScreen :: Position -> Position
@@ -487,7 +592,7 @@ mouseDrag f done = do
clearEvents pointerMotionMask
return z
-- | XXX comment me
-- | drag the window under the cursor with the mouse while it is dragged
mouseMoveWindow :: Window -> X ()
mouseMoveWindow w = whenX (isClient w) $ withDisplay $ \d -> do
io $ raiseWindow d w
@@ -495,21 +600,26 @@ mouseMoveWindow w = whenX (isClient w) $ withDisplay $ \d -> do
(_, _, _, ox', oy', _, _, _) <- io $ queryPointer d w
let ox = fromIntegral ox'
oy = fromIntegral oy'
mouseDrag (\ex ey -> io $ moveWindow d w (fromIntegral (fromIntegral (wa_x wa) + (ex - ox)))
(fromIntegral (fromIntegral (wa_y wa) + (ey - oy))))
mouseDrag (\ex ey -> do
io $ moveWindow d w (fromIntegral (fromIntegral (wa_x wa) + (ex - ox)))
(fromIntegral (fromIntegral (wa_y wa) + (ey - oy)))
float w
)
(float w)
-- | XXX comment me
-- | resize the window under the cursor with the mouse while it is dragged
mouseResizeWindow :: Window -> X ()
mouseResizeWindow w = whenX (isClient w) $ withDisplay $ \d -> do
io $ raiseWindow d w
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 ->
mouseDrag (\ex ey -> do
io $ resizeWindow d w `uncurry`
applySizeHintsContents sh (ex - fromIntegral (wa_x wa),
ey - fromIntegral (wa_y wa)))
ey - fromIntegral (wa_y wa))
float w)
(float w)
-- ---------------------------------------------------------------------
@@ -522,8 +632,12 @@ type D = (Dimension, Dimension)
mkAdjust :: Window -> X (D -> D)
mkAdjust w = withDisplay $ \d -> liftIO $ do
sh <- getWMNormalHints d w
bw <- fmap (fromIntegral . wa_border_width) $ getWindowAttributes d w
return $ applySizeHints bw sh
wa <- C.try $ getWindowAttributes d w
case wa of
Left err -> const (return id) (err :: C.SomeException)
Right wa' ->
let bw = fromIntegral $ wa_border_width wa'
in return $ applySizeHints bw sh
-- | Reduce the dimensions if needed to comply to the given SizeHints, taking
-- window borders into account.

View File

@@ -477,12 +477,12 @@ insertUp a s = if member a s then s else insert
--
-- * otherwise, delete doesn't affect the master.
--
delete :: (Ord a, Eq s) => a -> StackSet i l a s sd -> StackSet i l a s sd
delete :: (Ord a) => a -> StackSet i l a s sd -> StackSet i l a s sd
delete w = sink w . delete' w
-- | Only temporarily remove the window from the stack, thereby not destroying special
-- information saved in the 'Stackset'
delete' :: (Eq a, Eq s) => a -> StackSet i l a s sd -> StackSet i l a s sd
delete' :: (Eq a) => a -> StackSet i l a s sd -> StackSet i l a s sd
delete' w s = s { current = removeFromScreen (current s)
, visible = map removeFromScreen (visible s)
, hidden = map removeFromWorkspace (hidden s) }
@@ -547,7 +547,7 @@ shift n s = maybe s (\w -> shiftWin n w s) (peek s)
-- focused element on that workspace.
-- The actual focused workspace doesn't change. If the window is not
-- found in the stackSet, the original stackSet is returned.
shiftWin :: (Ord a, Eq a, Eq s, Eq i) => i -> a -> StackSet i l a s sd -> StackSet i l a s sd
shiftWin :: (Ord a, Eq s, Eq i) => i -> a -> StackSet i l a s sd -> StackSet i l a s sd
shiftWin n w s = case findTag w s of
Just from | n `tagMember` s && n /= from -> go from s
_ -> s

7
stack.yaml Normal file
View File

@@ -0,0 +1,7 @@
resolver: lts-7.19
packages:
- ./
extra-deps:
- X11-1.8

140
tests/Instances.hs Normal file
View 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

File diff suppressed because it is too large Load Diff

View 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)

View File

@@ -0,0 +1,30 @@
module Properties.Failure where
import XMonad.StackSet hiding (filter)
import qualified Control.Exception.Extensible as C
import System.IO.Unsafe
import Data.List (isPrefixOf)
-- ---------------------------------------------------------------------
-- testing for failure and help out hpc
--
-- Since base 4.9.0.0 `error` appends a stack trace. The tests below
-- use `isPrefixOf` to only test equality on the error message.
--
prop_abort :: Int -> Bool
prop_abort _ = unsafePerformIO $ C.catch (abort "fail") check
where
check (C.SomeException e) =
return $ "xmonad: StackSet: fail" `isPrefixOf` show e
-- new should fail with an abort
prop_new_abort :: Int -> Bool
prop_new_abort _ = unsafePerformIO $ C.catch f check
where
f = new undefined{-layout-} [] [] `seq` return False
check (C.SomeException e) =
return $ "xmonad: StackSet: non-positive argument to StackSet.new" `isPrefixOf` show e
-- TODO: Fix this?
-- prop_view_should_fail = view {- with some bogus data -}

View 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
View 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

View 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

View 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

View 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

View 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

View 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
View 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
View 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]

View 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
View 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
View 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

View 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
View 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

View File

@@ -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"

View File

@@ -1,5 +1,6 @@
-- Unlike the rest of xmonad, this file is copyright under the terms of the
-- GPL.
{-# LANGUAGE FlexibleContexts #-}
-- Unlike the rest of xmonad, this file is released under the GNU General
-- Public License version 2 or later.
--
-- Generates man/xmonad.1 from man/xmonad.1.in by filling the list of
@@ -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 -- works with 1.6
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,12 +66,11 @@ 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
@@ -79,21 +79,20 @@ main = do
Right template <- getDefaultTemplate Nothing "man"
writeFile "./man/xmonad.1"
. (manHeader ++)
. writeMan writeOpts{ writerStandalone = True, writerTemplate = template }
. writeMan def{ writerTemplate = Just template }
$ parsed
putStrLn "Documentation created: man/xmonad.1"
Right template <- getDefaultTemplate Nothing "html"
writeFile "./man/xmonad.1.html"
. writeHtmlString writeOpts
. writeHtmlString def
{ writerVariables =
[("include-before"
,"<h1>"++releaseName++"</h1>"++
"<p>Section: xmonad manual (1)<br/>"++
"Updated: "++releaseDate++"</p>"++
"<hr/>")]
, writerStandalone = True
, writerTemplate = template
, writerTemplate = Just template
, writerTableOfContents = True }
$ parsed
putStrLn "Documentation created: man/xmonad.1.html"

33
util/hpcReport.sh Normal file
View 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

View File

@@ -1,5 +1,5 @@
name: xmonad
version: 0.10
version: 0.13
homepage: http://xmonad.org
synopsis: A tiling window manager
description:
@@ -17,22 +17,46 @@ 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 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.3,
GHC==8.0.1
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
manual: True
flag generatemanpage
description: Build the tool for generating the man page
default: False
manual: True
flag profiling
description: Enable profiling
default: False
manual: True
library
hs-source-dirs: src
exposed-modules: XMonad
XMonad.Main
XMonad.Core
@@ -41,50 +65,69 @@ library
XMonad.ManageHook
XMonad.Operations
XMonad.StackSet
other-modules: Paths_xmonad
if flag(small_base)
build-depends: base < 5 && >=3, containers, directory, process, filepath, extensible-exceptions
else
build-depends: base < 3
build-depends: X11>=1.5.0.0 && < 1.6, mtl, unix,
utf8-string >= 0.3 && < 0.4
build-depends: base < 5 && >=3,
containers,
data-default,
directory >= 1.2.3,
extensible-exceptions,
filepath,
setlocale,
mtl,
process,
unix,
utf8-string >= 0.3 && < 1.1,
X11>=1.8 && < 1.9
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)
ghc-prof-options: -prof -auto-all
extensions: CPP
if flag(profiling)
ghc-prof-options: -prof -auto-all
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
if true
ghc-options: -funbox-strict-fields -Wall
main-is: Main.hs
build-depends: base,
mtl,
unix,
X11,
xmonad
ghc-options: -Wall
if impl(ghc >= 6.12.1)
ghc-options: -fno-warn-unused-do-bind
ghc-options: -Wall -fno-warn-unused-do-bind
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: filepath, process, directory, mtl, unix, X11, base, containers, random, extensible-exceptions
-- 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