mirror of
https://github.com/xmonad/xmonad.git
synced 2025-07-25 17:21:52 -07:00
Compare commits
118 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
bb13853929 | ||
|
3d1720c3f3 | ||
|
0614ffb65c | ||
|
85b47fc3ac | ||
|
1a99280227 | ||
|
e8133eb9a6 | ||
|
4ccaff8f25 | ||
|
56dc186e68 | ||
|
10b2efe81c | ||
|
49c69fa73b | ||
|
120ebce490 | ||
|
c0cf91303f | ||
|
80f1c6f027 | ||
|
c54e7088f0 | ||
|
1f3a27f9b9 | ||
|
ec97d83f3f | ||
|
f0975b734c | ||
|
2324266fae | ||
|
3b0559c6cc | ||
|
886a0d4041 | ||
|
98f39eabc1 | ||
|
425c3c0872 | ||
|
29c9819daa | ||
|
3c2b09c213 | ||
|
64a660894d | ||
|
27b1ce9dd7 | ||
|
5caf235f6b | ||
|
4ef9c12d13 | ||
|
d6705fd595 | ||
|
7c1065c43f | ||
|
af104509c3 | ||
|
586ee75a9a | ||
|
013da018a1 | ||
|
71cb355948 | ||
|
19069b3d4b | ||
|
969fca9406 | ||
|
61f00e65f1 | ||
|
db11089e70 | ||
|
e601a7d16d | ||
|
0dd23bddfa | ||
|
55b14d4850 | ||
|
9df514b378 | ||
|
b6d92b4e38 | ||
|
ecf1a0ca0d | ||
|
d216e95f97 | ||
|
af3d3818c8 | ||
|
d065038c8a | ||
|
10bc213349 | ||
|
d22d93b43f | ||
|
871a80fee7 | ||
|
2d59f5157c | ||
|
0738262d9e | ||
|
63d6a66133 | ||
|
fe6215d309 | ||
|
c3cb4ad65f | ||
|
126f891d11 | ||
|
d3383ce0f5 | ||
|
c96a59fa0d | ||
|
12a45b4b99 | ||
|
462957b2f0 | ||
|
3ec3536761 | ||
|
179b6a30f4 | ||
|
3dc65c3d2e | ||
|
2e6312776b | ||
|
3897cab7c9 | ||
|
0c97a89754 | ||
|
5afdc16387 | ||
|
10b843ad21 | ||
|
bc320b69da | ||
|
89a8cc88c3 | ||
|
76f4a16258 | ||
|
8f2eb540d7 | ||
|
ba2d75b930 | ||
|
acf0652952 | ||
|
e4d231920c | ||
|
980828feea | ||
|
2e5ae02059 | ||
|
50eb1844eb | ||
|
f18bda7dc7 | ||
|
2d8cad02fe | ||
|
2baab28602 | ||
|
ef65f901ce | ||
|
f2da028ff9 | ||
|
bad3ce7a5e | ||
|
e1c555e3e6 | ||
|
ab20f7df8d | ||
|
a70bf6a6a3 | ||
|
f58b2399bd | ||
|
91d23656a3 | ||
|
d6b6189cc1 | ||
|
0248e3c9fa | ||
|
40fc10b6a5 | ||
|
3a140badf5 | ||
|
2b103ede55 | ||
|
4565e2c90e | ||
|
285ee2f836 | ||
|
7e9c9ccb1f | ||
|
dc078490d0 | ||
|
202e239ea4 | ||
|
e159ec36fe | ||
|
0b1ccc75ef | ||
|
b0f9a3d0b9 | ||
|
75d297a633 | ||
|
5f5e737d9c | ||
|
a39ed3ee1b | ||
|
e05a046bca | ||
|
12ddc800ab | ||
|
2fab1bb9f5 | ||
|
1b17d1c378 | ||
|
f490ced673 | ||
|
0919ecfbde | ||
|
41b7b1341e | ||
|
0f0aa5e8cb | ||
|
ad4417c8e0 | ||
|
b0f7643cc5 | ||
|
8b055621e9 | ||
|
dc6a972bc1 | ||
|
e4a3eede18 |
24
.github/ISSUE_TEMPLATE.md
vendored
Normal file
24
.github/ISSUE_TEMPLATE.md
vendored
Normal 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
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal 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
|
14
.gitignore
vendored
14
.gitignore
vendored
@@ -1,14 +1,9 @@
|
||||
.cabal-sandbox/
|
||||
cabal.sandbox.config
|
||||
.hpc/
|
||||
*.hi
|
||||
*.o
|
||||
*.p_hi
|
||||
*.prof
|
||||
*.tix
|
||||
cabal.config
|
||||
dist
|
||||
dist-*
|
||||
|
||||
# editor temp files
|
||||
|
||||
@@ -23,3 +18,12 @@ tags
|
||||
|
||||
# stack artifacts
|
||||
/.stack-work/
|
||||
|
||||
# cabal-install artifacts
|
||||
/.*.environment.*-*
|
||||
/.cabal-sandbox/
|
||||
/cabal.config
|
||||
/cabal.project.local
|
||||
/cabal.sandbox.config
|
||||
/dist-newstyle/
|
||||
/dist/
|
||||
|
167
.travis.yml
167
.travis.yml
@@ -1,82 +1,135 @@
|
||||
# This file has been generated -- see https://github.com/hvr/multi-ghc-travis
|
||||
# This Travis job script has been generated by a script via
|
||||
#
|
||||
# runghc make_travis_yml_2.hs '-o' '.travis.yml' 'xmonad.cabal' 'libxrandr-dev'
|
||||
#
|
||||
# For more information, see https://github.com/haskell-CI/haskell-ci
|
||||
#
|
||||
language: c
|
||||
sudo: false
|
||||
|
||||
git:
|
||||
submodules: false # whether to recursively clone submodules
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cabsnap
|
||||
- $HOME/.cabal/packages
|
||||
- $HOME/.cabal/store
|
||||
|
||||
before_cache:
|
||||
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
|
||||
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.tar
|
||||
# remove files that are regenerated by 'cabal update'
|
||||
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*
|
||||
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json
|
||||
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache
|
||||
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar
|
||||
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx
|
||||
|
||||
- rm -rfv $HOME/.cabal/packages/head.hackage
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- env: CABALVER=1.16 GHCVER=7.6.3
|
||||
compiler: ": #GHC 7.6.3"
|
||||
addons: {apt: {packages: [cabal-install-1.16,ghc-7.6.3], sources: [hvr-ghc]}}
|
||||
- env: CABALVER=1.18 GHCVER=7.8.4
|
||||
compiler: ": #GHC 7.8.4"
|
||||
addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4], sources: [hvr-ghc]}}
|
||||
- env: CABALVER=1.22 GHCVER=7.10.2
|
||||
compiler: ": #GHC 7.10.2"
|
||||
addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.2], sources: [hvr-ghc]}}
|
||||
- compiler: "ghc-8.6.1"
|
||||
env: GHCHEAD=true
|
||||
addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.6.1,libxrandr-dev], sources: [hvr-ghc]}}
|
||||
- compiler: "ghc-8.4.3"
|
||||
# env: TEST=--disable-tests BENCH=--disable-benchmarks
|
||||
addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.4.3,libxrandr-dev], sources: [hvr-ghc]}}
|
||||
- compiler: "ghc-8.2.2"
|
||||
# env: TEST=--disable-tests BENCH=--disable-benchmarks
|
||||
addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.2.2,libxrandr-dev], sources: [hvr-ghc]}}
|
||||
- compiler: "ghc-8.0.2"
|
||||
# env: TEST=--disable-tests BENCH=--disable-benchmarks
|
||||
addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.0.2,libxrandr-dev], sources: [hvr-ghc]}}
|
||||
|
||||
allow_failures:
|
||||
- compiler: "ghc-8.6.1"
|
||||
|
||||
before_install:
|
||||
- unset CC
|
||||
- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
|
||||
- HC=${CC}
|
||||
- HCPKG=${HC/ghc/ghc-pkg}
|
||||
- unset CC
|
||||
- ROOTDIR=$(pwd)
|
||||
- mkdir -p $HOME/.local/bin
|
||||
- "PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$HOME/local/bin:$PATH"
|
||||
- HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') ))
|
||||
- echo $HCNUMVER
|
||||
|
||||
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
|
||||
- cabal --version
|
||||
- echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
|
||||
- BENCH=${BENCH---enable-benchmarks}
|
||||
- TEST=${TEST---enable-tests}
|
||||
- HADDOCK=${HADDOCK-true}
|
||||
- UNCONSTRAINED=${UNCONSTRAINED-true}
|
||||
- NOINSTALLEDCONSTRAINTS=${NOINSTALLEDCONSTRAINTS-false}
|
||||
- GHCHEAD=${GHCHEAD-false}
|
||||
- travis_retry cabal update -v
|
||||
- "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config"
|
||||
- rm -fv cabal.project cabal.project.local
|
||||
# Overlay Hackage Package Index for GHC HEAD: https://github.com/hvr/head.hackage
|
||||
- |
|
||||
if $GHCHEAD; then
|
||||
sed -i 's/-- allow-newer: .*/allow-newer: *:base/' ${HOME}/.cabal/config
|
||||
for pkg in $($HCPKG list --simple-output); do pkg=$(echo $pkg | sed 's/-[^-]*$//'); sed -i "s/allow-newer: /allow-newer: *:$pkg, /" ${HOME}/.cabal/config; done
|
||||
|
||||
# check whether current requested install-plan matches cached package-db snapshot
|
||||
- if diff -u installplan.txt $HOME/.cabsnap/installplan.txt;
|
||||
then
|
||||
echo "cabal build-cache HIT";
|
||||
rm -rfv .ghc;
|
||||
cp -a $HOME/.cabsnap/ghc $HOME/.ghc;
|
||||
cp -a $HOME/.cabsnap/lib $HOME/.cabsnap/share $HOME/.cabsnap/bin $HOME/.cabal/;
|
||||
else
|
||||
echo "cabal build-cache MISS";
|
||||
rm -rf $HOME/.cabsnap;
|
||||
mkdir -p $HOME/.ghc $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin;
|
||||
cabal install --only-dependencies --enable-tests --enable-benchmarks;
|
||||
fi
|
||||
echo 'repository head.hackage' >> ${HOME}/.cabal/config
|
||||
echo ' url: http://head.hackage.haskell.org/' >> ${HOME}/.cabal/config
|
||||
echo ' secure: True' >> ${HOME}/.cabal/config
|
||||
echo ' root-keys: 07c59cb65787dedfaef5bd5f987ceb5f7e5ebf88b904bbd4c5cbdeb2ff71b740' >> ${HOME}/.cabal/config
|
||||
echo ' 2e8555dde16ebd8df076f1a8ef13b8f14c66bad8eafefd7d9e37d0ed711821fb' >> ${HOME}/.cabal/config
|
||||
echo ' 8f79fd2389ab2967354407ec852cbe73f2e8635793ac446d09461ffb99527f6e' >> ${HOME}/.cabal/config
|
||||
echo ' key-threshold: 3' >> ${HOME}/.cabal.config
|
||||
|
||||
# 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
|
||||
grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'
|
||||
|
||||
cabal new-update head.hackage -v
|
||||
fi
|
||||
- grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'
|
||||
- "printf 'packages: \".\"\\n' > cabal.project"
|
||||
- "if [ $HCNUMVER -lt 80600 ]; then printf 'package xmonad\\n flags: +generatemanpage\n' >> cabal.project; fi"
|
||||
- touch cabal.project.local
|
||||
- "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- xmonad | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"
|
||||
- cat cabal.project || true
|
||||
- cat cabal.project.local || true
|
||||
- if [ -f "./configure.ac" ]; then
|
||||
(cd "." && autoreconf -i);
|
||||
fi
|
||||
- rm -f cabal.project.freeze
|
||||
- cabal new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project" --dep -j2 all
|
||||
- cabal new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project" --dep -j2 all
|
||||
- rm -rf .ghc.environment.* "."/dist
|
||||
- DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
|
||||
|
||||
# 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
|
||||
# test that source-distributions can be generated
|
||||
- (cd "." && cabal sdist)
|
||||
- mv "."/dist/xmonad-*.tar.gz ${DISTDIR}/
|
||||
- cd ${DISTDIR} || false
|
||||
- find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;
|
||||
- "printf 'packages: xmonad-*/*.cabal\\n' > cabal.project"
|
||||
- "if [ $HCNUMVER -lt 80600 ]; then printf 'package xmonad\\n flags: +generatemanpage\n' >> cabal.project; fi"
|
||||
- touch cabal.project.local
|
||||
- "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- xmonad | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"
|
||||
- cat cabal.project || true
|
||||
- cat cabal.project.local || true
|
||||
# this builds all libraries and executables (without tests/benchmarks)
|
||||
- cabal new-build -w ${HC} --disable-tests --disable-benchmarks all
|
||||
|
||||
# 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")
|
||||
# build & run tests, build benchmarks
|
||||
- cabal new-build -w ${HC} ${TEST} ${BENCH} all
|
||||
- if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} ${BENCH} all; fi
|
||||
|
||||
# cabal check
|
||||
- (cd xmonad-* && cabal check)
|
||||
|
||||
# haddock
|
||||
- rm -rf ./dist-newstyle
|
||||
- if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi
|
||||
|
||||
# Build without installed constraints for packages in global-db
|
||||
- if $UNCONSTRAINED; then rm -f cabal.project.local; echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks all; else echo "Not building without installed constraints"; fi
|
||||
|
||||
# REGENDATA ["-o",".travis.yml","xmonad.cabal","libxrandr-dev"]
|
||||
# EOF
|
||||
|
142
CHANGES.md
142
CHANGES.md
@@ -1,5 +1,147 @@
|
||||
# Change Log / Release Notes
|
||||
|
||||
## unknown (unknown)
|
||||
|
||||
## 0.15 (September 30, 2018)
|
||||
|
||||
* Reimplement `sendMessage` to deal properly with windowset changes made
|
||||
during handling.
|
||||
|
||||
* Add new library functions `windowBracket` and `modifyWindowSet` to
|
||||
`XMonad.Operations`.
|
||||
|
||||
## 0.14.2 (August 21, 2018)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Add the sample configuration file xmonad.hs again to the release tarball.
|
||||
[https://github.com/xmonad/xmonad/issues/181]
|
||||
|
||||
## 0.14.1 (August 20, 2018)
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* The cabal build no longer installs xmonad.hs, xmonad.1, and xmonad.1.html
|
||||
as data files. The location cabal picks for chose files isn't useful as
|
||||
standard tools like man(1) won't find them there. Instead, we rely on
|
||||
distributors to pick up the files from the source tarball during the build
|
||||
and to install them into proper locations where their users expect them.
|
||||
[https://github.com/xmonad/xmonad/pull/127]
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Add support for GHC 8.6.x by providing an instance for 'MonadFail X'. A
|
||||
side effect of that change is that our code no longer compiles with GHC
|
||||
versions prior to 8.0.x. We could work around that, no doubt, but the
|
||||
resulting code would require CPP and Cabal flags and whatnot. It feels more
|
||||
reasonable to just require a moderately recent compiler instead of going
|
||||
through all that trouble.
|
||||
|
||||
* xmonad no longer always recompile on startup. Now it only does so if the
|
||||
executable does not have the name that would be used for the compilation
|
||||
output. The purpose of recompiling and executing the results in this case is
|
||||
so that the `xmonad` executable in the package can be used with custom
|
||||
configurations.
|
||||
|
||||
### Enhancements
|
||||
|
||||
* Whenever xmonad recompiles, it now explains how it is attempting to
|
||||
recompile, by outputting logs to stderr. If you are using xmonad as a custom
|
||||
X session, then this will end up in a `.xsession-errors` file.
|
||||
|
||||
## 0.14 (July 30, 2018)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* The state file that xmonad uses while restarting itself is now
|
||||
removed after it is processed. This fixes a bug that manifested
|
||||
in several different ways:
|
||||
|
||||
- Names of old workspaces would be resurrected after a restart
|
||||
- Screen sizes would be wrong after changing monitor configuration (#90)
|
||||
- `spawnOnce` stopped working (xmonad/xmonad-contrib#155)
|
||||
- Focus did not follow when moving between workspaces (#87)
|
||||
- etc.
|
||||
|
||||
* Recover old behavior (in 0.12) when `focusFollowsMouse == True`:
|
||||
the focus follows when the mouse enters another workspace
|
||||
but not moving into any window.
|
||||
|
||||
* Compiles with GHC 8.4.1
|
||||
|
||||
* Restored compatability with GHC version prior to 8.0.1 by removing the
|
||||
dependency on directory version 1.2.3.
|
||||
|
||||
|
||||
## 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
|
||||
|
141
CONTRIBUTING.md
Normal file
141
CONTRIBUTING.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# 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.
|
||||
|
||||
* Make sure you read the section on rebasing and squashing commits
|
||||
below.
|
||||
|
||||
## Rebasing and Squashing Commits
|
||||
|
||||
Under no circumstances should you ever merge the master branch into
|
||||
your feature branch. This makes it nearly impossible to review your
|
||||
changes and we *will not accept your PR* if you do this.
|
||||
|
||||
Instead of merging you should rebase your changes on top of the master
|
||||
branch. If a core team member asks you to "rebase your changes" this
|
||||
is what they are talking about.
|
||||
|
||||
It's also helpful to squash all of your commits so that your pull
|
||||
request only contains a single commit. Again, this makes it easier to
|
||||
review your changes and identify the changes later on in the Git
|
||||
history.
|
||||
|
||||
### How to Rebase Your Changes
|
||||
|
||||
The goal of rebasing is to bring recent changes from the master branch
|
||||
into your feature branch. This often helps resolve conflicts where
|
||||
you have changed a file that also changed in a recently merged pull
|
||||
request (i.e. the `CHANGES.md` file). Here is how you do that.
|
||||
|
||||
1. Make sure that you have a `git remote` configured for the main
|
||||
repository. I like to call this remote `upstream`:
|
||||
|
||||
$ git remote add upstream https://github.com/xmonad/xmonad-contrib.git
|
||||
|
||||
2. Pull from upstream and rewrite your changes on top of master. For
|
||||
this to work you should not have any modified files in your
|
||||
working directory. Run these commands from within your feature
|
||||
branch (the branch you are asking to be merged):
|
||||
|
||||
$ git fetch --all
|
||||
$ git pull --rebase upstream master
|
||||
|
||||
3. If the rebase was successful you can now push your feature branch
|
||||
back to GitHub. You need to force the push since your commits
|
||||
have been rewritten and have new IDs:
|
||||
|
||||
$ git push --force-with-lease
|
||||
|
||||
4. Your pull request should now be conflict-free and only contain the
|
||||
changes that you actually made.
|
||||
|
||||
### How to Squash Commits
|
||||
|
||||
The goal of squashing commits is to produce a clean Git history where
|
||||
each pull request contains just one commit.
|
||||
|
||||
1. Use `git log` to see how many commits you are including in your
|
||||
pull request. (If you've already submitted your pull request you
|
||||
can see this in the GitHub interface.)
|
||||
|
||||
2. Rebase all of those commits into a single commit. Assuming you
|
||||
want to squash the last four (4) commits into a single commit:
|
||||
|
||||
$ git rebase -i HEAD~4
|
||||
|
||||
3. Git will open your editor and display the commits you are
|
||||
rebasing with the word "pick" in front of them.
|
||||
|
||||
4. Leave the first listed commit as "pick" and change the remaining
|
||||
commits from "pick" to "squash".
|
||||
|
||||
5. Save the file and exit your editor. Git will create a new commit
|
||||
and open your editor so you can modify the commit message.
|
||||
|
||||
6. If everything was successful you can push your changed history
|
||||
back up to GitHub:
|
||||
|
||||
$ git push --force-with-lease
|
||||
|
||||
[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
83
MAINTAINERS.md
Normal 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
|
18
README.md
18
README.md
@@ -1,5 +1,7 @@
|
||||
# xmonad: A Tiling Window Manager
|
||||
|
||||
[](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:
|
||||
@@ -58,11 +60,19 @@ We'll now walk through the complete list of toolchain dependencies.
|
||||
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
|
||||
# for xmonad
|
||||
$ apt-get install libx11-dev libxinerama-dev libxext-dev libxrandr-dev libxss-dev
|
||||
|
||||
# for xmonad-contrib
|
||||
$ apt-get install libxft-dev
|
||||
|
||||
Then build and install with:
|
||||
|
||||
$ cabal install
|
||||
|
||||
## Running xmonad
|
||||
|
||||
Add:
|
||||
If you built XMonad using `cabal` then add:
|
||||
|
||||
exec $HOME/.cabal/bin/xmonad
|
||||
|
||||
@@ -70,7 +80,7 @@ to the last line of your `.xsession` or `.xinitrc` file.
|
||||
|
||||
## Configuring
|
||||
|
||||
See the `CONFIG` document.
|
||||
See the [CONFIG][] document and the [example configuration file][example-config].
|
||||
|
||||
## XMonadContrib
|
||||
|
||||
@@ -115,3 +125,5 @@ For a program dispatch menu:
|
||||
[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
|
||||
|
14
TODO
14
TODO
@@ -1,14 +0,0 @@
|
||||
= Release management =
|
||||
|
||||
* generate, and push website haddocks with xmonad-web/gen-docs.sh
|
||||
* generate manpage, generate html manpage
|
||||
* double check README build instructions
|
||||
* bump xmonad.cabal version and X11 version
|
||||
* update cabal "tested-with:" fields
|
||||
* upload X11 and xmonad to Hackage
|
||||
* update #xmonad topic
|
||||
* check examples/text in user-facing Config.hs
|
||||
* check tour.html and intro.html are up to date, and mention all core bindings
|
||||
* confirm template config is type correct
|
||||
* update haskellwiki notable changes since x.x
|
||||
* email announce
|
1
cabal.project
Normal file
1
cabal.project
Normal file
@@ -0,0 +1 @@
|
||||
packages: ./
|
29
man/xmonad.1
29
man/xmonad.1
@@ -1,10 +1,10 @@
|
||||
.TH xmonad 1 "31 December 2012" xmonad-0.12 "xmonad manual".\" Automatically generated by Pandoc 1.15.1
|
||||
.\" Automatically generated by Pandoc 2.2.1
|
||||
.\"
|
||||
.TH "XMONAD" "1" "30 September 2018" "Tiling Window Manager" ""
|
||||
.hy
|
||||
.TH "" "" "" "" ""
|
||||
.SH Name
|
||||
.PP
|
||||
xmonad \- a tiling window manager
|
||||
xmonad \- Tiling Window Manager
|
||||
.SH Description
|
||||
.PP
|
||||
\f[I]xmonad\f[] is a minimalist tiling window manager for X, written in
|
||||
@@ -41,7 +41,7 @@ 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".
|
||||
\f[I]xmonad\f[] places each window into a \[lq]workspace\[rq].
|
||||
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
|
||||
@@ -67,31 +67,32 @@ and visible workspaces are swapped.
|
||||
xmonad has several flags which you may pass to the executable.
|
||||
These flags are:
|
||||
.TP
|
||||
.B \-\-recompile
|
||||
.B \[en]recompile
|
||||
Recompiles your configuration in \f[I]~/.xmonad/xmonad.hs\f[]
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B \-\-restart
|
||||
.B \[en]restart
|
||||
Causes the currently running \f[I]xmonad\f[] process to restart
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B \-\-replace
|
||||
.B \[en]replace
|
||||
Replace the current window manager with xmonad
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B \-\-version
|
||||
.B \[en]version
|
||||
Display version of \f[I]xmonad\f[]
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B \-\-verbose\-version
|
||||
.B \[en]verbose\-version
|
||||
Display detailed version of \f[I]xmonad\f[]
|
||||
.RS
|
||||
.RE
|
||||
.SS Default keyboard bindings
|
||||
.PP
|
||||
##Default keyboard bindings
|
||||
.TP
|
||||
.B mod\-shift\-return
|
||||
Launch terminal
|
||||
@@ -209,6 +210,12 @@ beginners)
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-question
|
||||
Run xmessage with a summary of the default keybindings (useful for
|
||||
beginners)
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-[1..9]
|
||||
Switch to workspace N
|
||||
.RS
|
||||
@@ -262,7 +269,7 @@ 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.
|
||||
in \f[I]~/.xmonad/lib/\f[] are available in GHC's searchpath.
|
||||
Hierarchical modules are supported: for example, the file
|
||||
\f[I]~/.xmonad/lib/XMonad/Stack/MyAdditions.hs\f[] could contain:
|
||||
.IP
|
||||
|
@@ -1,21 +1,97 @@
|
||||
<!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">
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="Content-Style-Type" content="text/css" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<title></title>
|
||||
<style type="text/css">code{white-space: pre;}</style>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<meta name="author" content="" />
|
||||
<meta name="dcterms.date" content="2018-09-30" />
|
||||
<title>XMONAD(1) Tiling Window Manager</title>
|
||||
<style type="text/css">
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
a.sourceLine { display: inline-block; line-height: 1.25; }
|
||||
a.sourceLine { pointer-events: none; color: inherit; text-decoration: inherit; }
|
||||
a.sourceLine:empty { height: 1.2em; }
|
||||
.sourceCode { overflow: visible; }
|
||||
code.sourceCode { white-space: pre; position: relative; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
code.sourceCode { white-space: pre-wrap; }
|
||||
a.sourceLine { text-indent: -1em; padding-left: 1em; }
|
||||
}
|
||||
pre.numberSource a.sourceLine
|
||||
{ position: relative; left: -4em; }
|
||||
pre.numberSource a.sourceLine::before
|
||||
{ content: attr(data-line-number);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; pointer-events: all; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
a.sourceLine::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
</style>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<h1>xmonad-0.12</h1><p>Section: xmonad manual (1)<br/>Updated: 31 December 2012</p><hr/>
|
||||
<div id="TOC">
|
||||
<header>
|
||||
<h1 class="title">XMONAD(1) Tiling Window Manager</h1>
|
||||
<p class="author"></p>
|
||||
<p class="date">30 September 2018</p>
|
||||
</header>
|
||||
<nav 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>
|
||||
@@ -23,37 +99,37 @@
|
||||
</ul></li>
|
||||
<li><a href="#bugs">Bugs</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<h1 id="name">Name</h1>
|
||||
<p>xmonad - a tiling window manager</p>
|
||||
<p>xmonad - Tiling Window Manager</p>
|
||||
<h1 id="description">Description</h1>
|
||||
<p><em>xmonad</em> is a minimalist tiling window manager for X, written in Haskell. Windows are managed using automatic layout algorithms, which can be dynamically reconfigured. At any time windows are arranged so as to maximize the use of screen real estate. All features of the window manager are accessible purely from the keyboard: a mouse is entirely optional. <em>xmonad</em> is configured in Haskell, and custom layout algorithms may be implemented by the user in config files. A principle of <em>xmonad</em> is predictability: the user should know in advance precisely the window arrangement that will result from any action.</p>
|
||||
<p>By default, <em>xmonad</em> provides three layout algorithms: tall, wide and fullscreen. In tall or wide mode, windows are tiled and arranged to prevent overlap and maximize screen use. Sets of windows are grouped together on virtual screens, and each screen retains its own layout, which may be reconfigured dynamically. Multiple physical monitors are supported via Xinerama, allowing simultaneous display of a number of screens.</p>
|
||||
<p>By utilizing the expressivity of a modern functional language with a rich static type system, <em>xmonad</em> provides a complete, featureful window manager in less than 1200 lines of code, with an emphasis on correctness and robustness. Internal properties of the window manager are checked using a combination of static guarantees provided by the type system, and type-based automated testing. A benefit of this is that the code is simple to understand, and easy to modify.</p>
|
||||
<h1 id="usage">Usage</h1>
|
||||
<p><em>xmonad</em> places each window into a "workspace". Each workspace can have any number of windows, which you can cycle though with mod-j and mod-k. Windows are either displayed full screen, tiled horizontally, or tiled vertically. You can toggle the layout mode with mod-space, which will cycle through the available modes.</p>
|
||||
<p><em>xmonad</em> places each window into a “workspace”. Each workspace can have any number of windows, which you can cycle though with mod-j and mod-k. Windows are either displayed full screen, tiled horizontally, or tiled vertically. You can toggle the layout mode with mod-space, which will cycle through the available modes.</p>
|
||||
<p>You can switch to workspace N with mod-N. For example, to switch to workspace 5, you would press mod-5. Similarly, you can move the current window to another workspace with mod-shift-N.</p>
|
||||
<p>When running with multiple monitors (Xinerama), each screen has exactly 1 workspace visible. mod-{w,e,r} switch the focus between screens, while shift-mod-{w,e,r} move the current window to that screen. When <em>xmonad</em> starts, workspace 1 is on screen 1, workspace 2 is on screen 2, etc. When switching workspaces to one that is already visible, the current and visible workspaces are swapped.</p>
|
||||
<h2 id="flags">Flags</h2>
|
||||
<p>xmonad has several flags which you may pass to the executable. These flags are:</p>
|
||||
<dl>
|
||||
<dt>--recompile</dt>
|
||||
<dt>–recompile</dt>
|
||||
<dd>Recompiles your configuration in <em>~/.xmonad/xmonad.hs</em>
|
||||
</dd>
|
||||
<dt>--restart</dt>
|
||||
<dt>–restart</dt>
|
||||
<dd>Causes the currently running <em>xmonad</em> process to restart
|
||||
</dd>
|
||||
<dt>--replace</dt>
|
||||
<dt>–replace</dt>
|
||||
<dd>Replace the current window manager with xmonad
|
||||
</dd>
|
||||
<dt>--version</dt>
|
||||
<dt>–version</dt>
|
||||
<dd>Display version of <em>xmonad</em>
|
||||
</dd>
|
||||
<dt>--verbose-version</dt>
|
||||
<dt>–verbose-version</dt>
|
||||
<dd>Display detailed version of <em>xmonad</em>
|
||||
</dd>
|
||||
</dl>
|
||||
<h2 id="default-keyboard-bindings">Default keyboard bindings</h2>
|
||||
<p>##Default keyboard bindings</p>
|
||||
<dl>
|
||||
<dt>mod-shift-return</dt>
|
||||
<dd>Launch terminal
|
||||
@@ -124,6 +200,9 @@
|
||||
<dt>mod-shift-slash</dt>
|
||||
<dd>Run xmessage with a summary of the default keybindings (useful for beginners)
|
||||
</dd>
|
||||
<dt>mod-question</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>
|
||||
@@ -155,9 +234,9 @@
|
||||
<p>xmonad is customized in ~/.xmonad/xmonad.hs, and then restarted with mod-q.</p>
|
||||
<p>You can find many extensions to the core feature set in the xmonad- contrib package, available through your package manager or from <a href="http://xmonad.org">xmonad.org</a>.</p>
|
||||
<h2 id="modular-configuration">Modular Configuration</h2>
|
||||
<p>As of <em>xmonad-0.9</em>, any additional Haskell modules may be placed in <em>~/.xmonad/lib/</em> are available in GHC's searchpath. Hierarchical modules are supported: for example, the file <em>~/.xmonad/lib/XMonad/Stack/MyAdditions.hs</em> could contain:</p>
|
||||
<pre class="haskell"><code>module XMonad.Stack.MyAdditions (function1) where
|
||||
function1 = error "function1: Not implemented yet!"</code></pre>
|
||||
<p>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>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb1-1" data-line-number="1"><span class="kw">module</span> <span class="dt">XMonad.Stack.MyAdditions</span> (function1) <span class="kw">where</span></a>
|
||||
<a class="sourceLine" id="cb1-2" data-line-number="2"> function1 <span class="fu">=</span> error <span class="st">"function1: Not implemented yet!"</span></a></code></pre></div>
|
||||
<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>
|
||||
|
@@ -1,7 +1,12 @@
|
||||
#Name
|
||||
xmonad - a tiling window manager
|
||||
% XMONAD(1) Tiling Window Manager
|
||||
%
|
||||
% 30 September 2018
|
||||
|
||||
#Description
|
||||
# Name
|
||||
|
||||
xmonad - Tiling Window Manager
|
||||
|
||||
# Description
|
||||
|
||||
_xmonad_ is a minimalist tiling window manager for X, written in Haskell.
|
||||
Windows are managed using automatic layout algorithms, which can be
|
||||
@@ -28,7 +33,7 @@ combination of static guarantees provided by the type system, and
|
||||
type-based automated testing. A benefit of this is that the code is simple
|
||||
to understand, and easy to modify.
|
||||
|
||||
#Usage
|
||||
# Usage
|
||||
|
||||
_xmonad_ places each window into a "workspace". Each workspace can have
|
||||
any number of windows, which you can cycle though with mod-j and mod-k.
|
||||
@@ -47,7 +52,8 @@ starts, workspace 1 is on screen 1, workspace 2 is on screen 2, etc. When
|
||||
switching workspaces to one that is already visible, the current and
|
||||
visible workspaces are swapped.
|
||||
|
||||
##Flags
|
||||
## Flags
|
||||
|
||||
xmonad has several flags which you may pass to the executable.
|
||||
These flags are:
|
||||
|
||||
@@ -70,12 +76,13 @@ These flags are:
|
||||
|
||||
___KEYBINDINGS___
|
||||
|
||||
#Examples
|
||||
# Examples
|
||||
|
||||
To use xmonad as your window manager add to your _~/.xinitrc_ file:
|
||||
|
||||
> exec xmonad
|
||||
|
||||
#Customization
|
||||
# Customization
|
||||
xmonad is customized in ~/.xmonad/xmonad.hs, and then restarted
|
||||
with mod-q.
|
||||
|
||||
@@ -83,7 +90,7 @@ You can find many extensions to the core feature set in the xmonad-
|
||||
contrib package, available through your package manager or from
|
||||
[xmonad.org].
|
||||
|
||||
##Modular Configuration
|
||||
## Modular Configuration
|
||||
As of _xmonad-0.9_, any additional Haskell modules may be placed in
|
||||
_~/.xmonad/lib/_ are available in GHC's searchpath. Hierarchical modules
|
||||
are supported: for example, the file
|
||||
@@ -97,7 +104,7 @@ module XMonad.Stack.MyAdditions (function1) where
|
||||
Your xmonad.hs may then import XMonad.Stack.MyAdditions as if that
|
||||
module was contained within xmonad or xmonad-contrib.
|
||||
|
||||
#Bugs
|
||||
# Bugs
|
||||
Probably. If you find any, please report them to the [bugtracker]
|
||||
|
||||
[xmonad.org]: http://xmonad.org
|
||||
|
@@ -221,9 +221,9 @@ keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
||||
, ((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)
|
||||
, ((modMask .|. shiftMask, xK_slash ), helpCommand) -- %! 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 -"))
|
||||
, ((modMask , xK_question), helpCommand) -- %! Run xmessage with a summary of the default keybindings (useful for beginners)
|
||||
]
|
||||
++
|
||||
-- mod-[1..9] %! Switch to workspace N
|
||||
@@ -237,6 +237,9 @@ keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
||||
[((m .|. modMask, key), screenWorkspace sc >>= flip whenJust (windows . f))
|
||||
| (key, sc) <- zip [xK_w, xK_e, xK_r] [0..]
|
||||
, (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
|
||||
where
|
||||
helpCommand :: X ()
|
||||
helpCommand = spawn ("echo " ++ show help ++ " | xmessage -file -")
|
||||
|
||||
-- | Mouse bindings: default actions bound to mouse events
|
||||
mouseBindings :: XConfig Layout -> M.Map (KeyMask, Button) (Window -> X ())
|
||||
@@ -320,9 +323,9 @@ help = unlines ["The default modifier key is 'alt'. Default keybindings:",
|
||||
"-- quit, or restart",
|
||||
"mod-Shift-q Quit xmonad",
|
||||
"mod-q Restart xmonad",
|
||||
"mod-[1..9] Switch to workSpace N",
|
||||
"",
|
||||
"-- 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",
|
||||
@@ -330,4 +333,4 @@ help = unlines ["The default modifier key is 'alt'. Default keybindings:",
|
||||
"-- 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"]
|
||||
"mod-button3 Set the window to floating mode and resize by dragging"]
|
||||
|
@@ -1,5 +1,5 @@
|
||||
{-# LANGUAGE ExistentialQuantification, FlexibleInstances, GeneralizedNewtypeDeriving,
|
||||
MultiParamTypeClasses, TypeSynonymInstances, CPP, DeriveDataTypeable #-}
|
||||
MultiParamTypeClasses, TypeSynonymInstances, DeriveDataTypeable #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
@@ -25,8 +25,10 @@ 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, atom_WM_TAKE_FOCUS, 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)
|
||||
@@ -34,13 +36,16 @@ import XMonad.StackSet hiding (modify)
|
||||
import Prelude
|
||||
import Control.Exception.Extensible (fromException, try, bracket, throw, finally, SomeException(..))
|
||||
import qualified Control.Exception.Extensible as E
|
||||
import Control.Applicative
|
||||
import Control.Applicative(Applicative, pure, (<$>), (<*>))
|
||||
import Control.Monad.Fail
|
||||
import Control.Monad.State
|
||||
import Control.Monad.Reader
|
||||
import Data.Semigroup
|
||||
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
|
||||
@@ -49,11 +54,12 @@ 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)
|
||||
import Data.Monoid
|
||||
import Data.Monoid hiding ((<>))
|
||||
import System.Environment (lookupEnv)
|
||||
|
||||
import qualified Data.Map as M
|
||||
import qualified Data.Set as S
|
||||
@@ -68,7 +74,7 @@ data XState = XState
|
||||
, extensibleState :: !(M.Map String (Either String StateExtension))
|
||||
-- ^ stores custom state information.
|
||||
--
|
||||
-- The module "XMonad.Utils.ExtensibleState" in xmonad-contrib
|
||||
-- The module "XMonad.Util.ExtensibleState" in xmonad-contrib
|
||||
-- provides additional information and a simple interface for using this.
|
||||
}
|
||||
|
||||
@@ -142,12 +148,15 @@ data ScreenDetail = SD { screenRect :: !Rectangle } deriving (Eq,Show, Read)
|
||||
-- instantiated on 'XConf' and 'XState' automatically.
|
||||
--
|
||||
newtype X a = X (ReaderT XConf (StateT XState IO) a)
|
||||
deriving (Functor, Monad, MonadIO, MonadState XState, MonadReader XConf, Typeable)
|
||||
deriving (Functor, Monad, MonadFail, MonadIO, MonadState XState, MonadReader XConf, Typeable)
|
||||
|
||||
instance Applicative X where
|
||||
pure = return
|
||||
(<*>) = ap
|
||||
|
||||
instance Semigroup a => Semigroup (X a) where
|
||||
(<>) = liftM2 (<>)
|
||||
|
||||
instance (Monoid a) => Monoid (X a) where
|
||||
mempty = return mempty
|
||||
mappend = liftM2 mappend
|
||||
@@ -162,6 +171,9 @@ newtype Query a = Query (ReaderT Window X a)
|
||||
runQuery :: Query a -> Window -> X a
|
||||
runQuery (Query m) w = runReaderT m w
|
||||
|
||||
instance Semigroup a => Semigroup (Query a) where
|
||||
(<>) = liftM2 (<>)
|
||||
|
||||
instance Monoid a => Monoid (Query a) where
|
||||
mempty = return mempty
|
||||
mappend = liftM2 mappend
|
||||
@@ -207,6 +219,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
|
||||
@@ -431,73 +449,247 @@ 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)
|
||||
|
||||
-- | Helper function to retrieve the various XDG directories.
|
||||
-- This has been based on the implementation shipped with GHC version 8.0.1 or
|
||||
-- higher. Put here to preserve compatibility with older GHC versions.
|
||||
getXDGDirectory :: XDGDirectory -> FilePath -> IO FilePath
|
||||
getXDGDirectory xdgDir suffix =
|
||||
normalise . (</> suffix) <$>
|
||||
case xdgDir of
|
||||
XDGData -> get "XDG_DATA_HOME" ".local/share"
|
||||
XDGConfig -> get "XDG_CONFIG_HOME" ".config"
|
||||
XDGCache -> get "XDG_CACHE_HOME" ".cache"
|
||||
where
|
||||
get name fallback = do
|
||||
env <- lookupEnv name
|
||||
case env of
|
||||
Nothing -> fallback'
|
||||
Just path
|
||||
| isRelative path -> fallback'
|
||||
| otherwise -> return path
|
||||
where
|
||||
fallback' = (</> fallback) <$> getHomeDirectory
|
||||
data XDGDirectory = XDGData | XDGConfig | XDGCache
|
||||
|
||||
-- | 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 do
|
||||
isExe <- isExecutable buildscript
|
||||
if isExe
|
||||
then do
|
||||
trace $ "XMonad will use build script at " ++ show buildscript ++ " to recompile."
|
||||
return True
|
||||
else do
|
||||
trace $ unlines
|
||||
[ "XMonad will not use build script, because " ++ show buildscript ++ " is not executable."
|
||||
, "Suggested resolution to use it: chmod u+x " ++ show buildscript
|
||||
]
|
||||
return False
|
||||
else do
|
||||
trace $
|
||||
"XMonad will use ghc to recompile, because " ++ show buildscript ++ " does not exist."
|
||||
return False
|
||||
|
||||
shouldRecompile <-
|
||||
if useBuildscript || force
|
||||
then return True
|
||||
else if any (binT <) (srcT : libTs)
|
||||
then do
|
||||
trace "XMonad doing recompile because some files have changed."
|
||||
return True
|
||||
else do
|
||||
trace "XMonad skipping recompile because it is not forced (e.g. via --recompile), and neither xmonad.hs nor any *.hs / *.lhs / *.hsc files in lib/ have been changed."
|
||||
return False
|
||||
|
||||
if shouldRecompile
|
||||
then do
|
||||
-- temporarily disable SIGCHLD ignoring:
|
||||
uninstallSignalHandlers
|
||||
status <- bracket (openFile err WriteMode) hClose $ \h ->
|
||||
waitForProcess =<< runProcess "ghc" ["--make", "xmonad.hs", "-i", "-ilib", "-fforce-recomp", "-main-is", "main", "-v0", "-o",binn] (Just dir)
|
||||
Nothing Nothing Nothing (Just h)
|
||||
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
|
||||
|
||||
-- now, if it fails, run xmessage to let the user know:
|
||||
when (status /= ExitSuccess) $ do
|
||||
ghcErr <- readFile err
|
||||
let msg = unlines $
|
||||
["Error detected while loading xmonad configuration file: " ++ src]
|
||||
++ lines (if null ghcErr then show status else ghcErr)
|
||||
++ ["","Please check the file for errors."]
|
||||
-- nb, the ordering of printing, then forking, is crucial due to
|
||||
-- lazy evaluation
|
||||
hPutStrLn stderr msg
|
||||
forkProcess $ executeFile "xmessage" True ["-default", "okay", msg] Nothing
|
||||
return ()
|
||||
if status == ExitSuccess
|
||||
then trace "XMonad recompilation process exited with success!"
|
||||
else do
|
||||
ghcErr <- readFile err
|
||||
let msg = unlines $
|
||||
["Error detected while loading xmonad configuration file: " ++ src]
|
||||
++ lines (if null ghcErr then show status else ghcErr)
|
||||
++ ["","Please check the file for errors."]
|
||||
-- nb, the ordering of printing, then forking, is crucial due to
|
||||
-- lazy evaluation
|
||||
hPutStrLn stderr msg
|
||||
forkProcess $ executeFile "xmessage" True ["-default", "okay", replaceUnicode msg] Nothing
|
||||
return ()
|
||||
return (status == ExitSuccess)
|
||||
else return True
|
||||
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 <$> 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 ()
|
||||
|
@@ -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 |||
|
||||
|
||||
|
@@ -13,10 +13,10 @@
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Main (xmonad) where
|
||||
module XMonad.Main (xmonad, launch) where
|
||||
|
||||
import System.Locale.SetLocale
|
||||
import Control.Arrow (second)
|
||||
import qualified Control.Exception.Extensible as E
|
||||
import Data.Bits
|
||||
import Data.List ((\\))
|
||||
import Data.Function
|
||||
@@ -37,7 +37,7 @@ 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)
|
||||
@@ -59,33 +59,29 @@ xmonad :: (LayoutClass l Window, Read (l Window)) => XConfig l -> IO ()
|
||||
xmonad conf = do
|
||||
installSignalHandlers -- important to ignore SIGCHLD to avoid zombies
|
||||
|
||||
let launch serializedWinset serializedExtState args = do
|
||||
let launch' args = do
|
||||
catchIO buildLaunch
|
||||
conf' @ XConfig { layoutHook = Layout l }
|
||||
<- handleExtraArgs conf args conf{ layoutHook = Layout (layoutHook conf) }
|
||||
withArgs [] $
|
||||
xmonadNoargs (conf' { layoutHook = l })
|
||||
serializedWinset
|
||||
serializedExtState
|
||||
withArgs [] $ launch (conf' { layoutHook = l })
|
||||
|
||||
args <- getArgs
|
||||
case args of
|
||||
("--resume": ws : xs : args') -> launch (Just ws) (Just xs) args'
|
||||
("--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' -> do
|
||||
sendReplace
|
||||
launch Nothing Nothing args'
|
||||
_ -> launch Nothing Nothing args
|
||||
"--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
|
||||
@@ -99,13 +95,13 @@ usage = do
|
||||
" --restart Request a running xmonad process to restart" :
|
||||
[]
|
||||
|
||||
-- | Build "~\/.xmonad\/xmonad.hs" with ghc, then execute it. If there are no
|
||||
-- errors, this function does not return. An exception is raised in any of
|
||||
-- these cases:
|
||||
-- | 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 "~\/.xmonad\/xmonad.hs" and "~\/.xmonad\/xmonad-$arch-$os" missing
|
||||
-- * both the configuration file and executable are missing
|
||||
--
|
||||
-- * xmonad.hs fails to compile
|
||||
--
|
||||
@@ -117,12 +113,18 @@ usage = do
|
||||
--
|
||||
buildLaunch :: IO ()
|
||||
buildLaunch = do
|
||||
recompile False
|
||||
dir <- getXMonadDir
|
||||
args <- getArgs
|
||||
whoami <- getProgName
|
||||
let compiledConfig = "xmonad-"++arch++"-"++os
|
||||
unless (whoami == compiledConfig) $
|
||||
unless (whoami == compiledConfig) $ do
|
||||
trace $ concat
|
||||
[ "XMonad is recompiling and replacing itself another XMonad process because the current process is called "
|
||||
, show whoami
|
||||
, " but the compiled configuration should be called "
|
||||
, show compiledConfig
|
||||
]
|
||||
recompile False
|
||||
dir <- getXMonadDataDir
|
||||
args <- getArgs
|
||||
executeFile (dir </> compiledConfig) False args Nothing
|
||||
|
||||
sendRestart :: IO ()
|
||||
@@ -144,15 +146,28 @@ sendReplace = do
|
||||
rootw <- rootWindow dpy dflt
|
||||
replace dpy dflt rootw
|
||||
|
||||
|
||||
-- |
|
||||
-- The main entry point
|
||||
-- | Entry point into xmonad for custom builds.
|
||||
--
|
||||
xmonadNoargs :: (LayoutClass l Window, Read (l Window)) => XConfig l
|
||||
-> Maybe String -- ^ serialized windowset
|
||||
-> Maybe String -- ^ serialized extensible state
|
||||
-> IO ()
|
||||
xmonadNoargs initxmc serializedWinset serializedExtstate = do
|
||||
-- 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
|
||||
setLocale LC_ALL (Just "")
|
||||
-- ignore SIGPIPE and SIGCHLD
|
||||
@@ -176,6 +191,7 @@ xmonadNoargs initxmc serializedWinset serializedExtstate = 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.def
|
||||
return (fromMaybe nbc_ v)
|
||||
@@ -187,22 +203,8 @@ xmonadNoargs initxmc serializedWinset serializedExtstate = do
|
||||
hSetBuffering stdout NoBuffering
|
||||
|
||||
let layout = layoutHook xmc
|
||||
lreads = readsLayout layout
|
||||
initialWinset = let padToLen n xs = take (max n (length xs)) $ xs ++ repeat ""
|
||||
in new layout (padToLen (length xinesc) (workspaces xmc)) $ map SD xinesc
|
||||
maybeRead reads' s = case reads' s of
|
||||
[(x, "")] -> Just x
|
||||
_ -> Nothing
|
||||
|
||||
winset = fromMaybe initialWinset $ do
|
||||
s <- serializedWinset
|
||||
ws <- maybeRead reads s
|
||||
return . W.ensureTags layout (workspaces xmc)
|
||||
$ W.mapLayout (fromMaybe layout . maybeRead lreads) ws
|
||||
extState = fromMaybe M.empty $ do
|
||||
dyns <- serializedExtstate
|
||||
vals <- maybeRead reads dyns
|
||||
return . M.fromList . map (second Left) $ vals
|
||||
|
||||
cf = XConf
|
||||
{ display = dpy
|
||||
@@ -218,14 +220,24 @@ xmonadNoargs initxmc serializedWinset serializedExtstate = do
|
||||
|
||||
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
|
||||
@@ -240,6 +252,7 @@ xmonadNoargs initxmc serializedWinset serializedExtstate = 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
|
||||
@@ -290,10 +303,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
|
||||
@@ -356,7 +369,13 @@ handle e@(ButtonEvent {ev_window = w,ev_event_type = t,ev_button = b })
|
||||
-- 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 Xlib cannot find a child that contains the pointer,
|
||||
-- it returns None(0)
|
||||
when (w' == 0 || w == w') (focus w)
|
||||
|
||||
-- left a window, check if we need to focus root
|
||||
handle e@(CrossingEvent {ev_event_type = t})
|
||||
@@ -367,8 +386,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)
|
||||
@@ -382,7 +399,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)
|
||||
@@ -416,7 +433,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
|
||||
@@ -428,6 +445,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
|
||||
@@ -458,7 +478,7 @@ grabKeys = do
|
||||
forM_ (keysymToKeycodes sym) $ \kc ->
|
||||
mapM_ (grab kc . (mask .|.)) =<< extraModifiers
|
||||
|
||||
-- | XXX comment me
|
||||
-- | Grab the buttons
|
||||
grabButtons :: X ()
|
||||
grabButtons = do
|
||||
XConf { display = dpy, theRoot = rootw } <- ask
|
||||
|
@@ -1,6 +1,5 @@
|
||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
|
||||
|
||||
{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
|
||||
-- --------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Operations
|
||||
@@ -22,18 +21,22 @@ import XMonad.Layout (Full(..))
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import Data.Maybe
|
||||
import Data.Monoid (Endo(..))
|
||||
import Data.Monoid (Endo(..),Any(..))
|
||||
import Data.List (nub, (\\), find)
|
||||
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.Applicative((<$>), (<*>))
|
||||
import Control.Arrow (second)
|
||||
import Control.Monad (void)
|
||||
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 +114,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 +157,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
|
||||
@@ -169,6 +177,25 @@ windows f = do
|
||||
unless isMouseFocused $ clearEvents enterWindowMask
|
||||
asks (logHook . config) >>= userCodeDef ()
|
||||
|
||||
-- | Modify the @WindowSet@ in state with no special handling.
|
||||
modifyWindowSet :: (WindowSet -> WindowSet) -> X ()
|
||||
modifyWindowSet f = modify $ \xst -> xst { windowset = f (windowset xst) }
|
||||
|
||||
-- | Perform an @X@ action and check its return value against a predicate p.
|
||||
-- If p holds, unwind changes to the @WindowSet@ and replay them using @windows@.
|
||||
windowBracket :: (a -> Bool) -> X a -> X a
|
||||
windowBracket p action = withWindowSet $ \old -> do
|
||||
a <- action
|
||||
when (p a) . withWindowSet $ \new -> do
|
||||
modifyWindowSet $ \_ -> old
|
||||
windows $ \_ -> new
|
||||
return a
|
||||
|
||||
-- | A version of @windowBracket@ that discards the return value, and handles an
|
||||
-- @X@ action reporting its need for refresh via @Any@.
|
||||
windowBracket_ :: X Any -> X ()
|
||||
windowBracket_ = void . windowBracket getAny
|
||||
|
||||
-- | Produce the actual rectangle from a screen and a ratio on that screen.
|
||||
scaleRationalRect :: Rectangle -> W.RationalRect -> Rectangle
|
||||
scaleRationalRect (Rectangle sx sy sw sh) (W.RationalRect rx ry rw rh)
|
||||
@@ -181,6 +208,19 @@ 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
|
||||
@@ -233,10 +273,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)
|
||||
@@ -351,15 +391,16 @@ setFocusX w = withWindowSet $ \ws -> do
|
||||
-- Message handling
|
||||
|
||||
-- | Throw a message to the current 'LayoutClass' possibly modifying how we
|
||||
-- layout the windows, then refresh.
|
||||
-- layout the windows, in which case changes are handled through a refresh.
|
||||
sendMessage :: Message a => a -> X ()
|
||||
sendMessage a = do
|
||||
sendMessage a = windowBracket_ $ do
|
||||
w <- W.workspace . W.current <$> gets windowset
|
||||
ml' <- handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing
|
||||
whenJust ml' $ \l' ->
|
||||
windows $ \ws -> ws { W.current = (W.current ws)
|
||||
modifyWindowSet $ \ws -> ws { W.current = (W.current ws)
|
||||
{ W.workspace = (W.workspace $ W.current ws)
|
||||
{ W.layout = l' }}}
|
||||
return (Any $ isJust ml')
|
||||
|
||||
-- | Send a message to all layouts, without refreshing.
|
||||
broadcastMessage :: Message a => a -> X ()
|
||||
@@ -423,6 +464,79 @@ 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. The state file is removed after reading it.
|
||||
readStateFile :: (LayoutClass l Window, Read (l Window)) => XConfig l -> X (Maybe XState)
|
||||
readStateFile xmc = do
|
||||
path <- stateFileName
|
||||
|
||||
-- I'm trying really hard here to make sure we read the entire
|
||||
-- contents of the file before it is removed from the file system.
|
||||
sf' <- userCode . io $ do
|
||||
raw <- withFile path ReadMode readStrict
|
||||
return $! maybeRead reads raw
|
||||
|
||||
io (removeFile path)
|
||||
|
||||
return $ do
|
||||
sf <- join sf'
|
||||
|
||||
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
|
||||
|
||||
readStrict :: Handle -> IO String
|
||||
readStrict h = hGetContents h >>= \s -> length s `seq` return s
|
||||
|
||||
-- | 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'.
|
||||
@@ -430,13 +544,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
|
||||
@@ -444,20 +553,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
|
||||
let bw = (fromIntegral . wa_border_width) wa
|
||||
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
|
||||
@@ -507,7 +623,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
|
||||
@@ -515,21 +631,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)
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
@@ -542,8 +663,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.
|
||||
|
@@ -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
7
stack.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
resolver: lts-7.19
|
||||
|
||||
packages:
|
||||
- ./
|
||||
|
||||
extra-deps:
|
||||
- X11-1.8
|
@@ -18,7 +18,7 @@ prop_delete x =
|
||||
where _ = x :: T
|
||||
|
||||
-- delete is reversible with 'insert'.
|
||||
-- It is the identiy, except for the 'master', which is reset on insert and delete.
|
||||
-- It is the identity, except for the 'master', which is reset on insert and delete.
|
||||
--
|
||||
prop_delete_insert (x :: T) =
|
||||
case peek x of
|
||||
|
@@ -4,23 +4,27 @@ 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
|
||||
prop_abort x = unsafePerformIO $ C.catch (abort "fail")
|
||||
(\(C.SomeException e) -> return $ show e == "xmonad: StackSet: fail" )
|
||||
-- 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
|
||||
_ = x :: Int
|
||||
check (C.SomeException e) =
|
||||
return $ "xmonad: StackSet: fail" `isPrefixOf` show e
|
||||
|
||||
-- new should fail with an abort
|
||||
prop_new_abort x = unsafePerformIO $ C.catch f
|
||||
(\(C.SomeException e) -> return $ show e == "xmonad: StackSet: non-positive argument to StackSet.new" )
|
||||
prop_new_abort :: Int -> Bool
|
||||
prop_new_abort _ = unsafePerformIO $ C.catch f check
|
||||
where
|
||||
f = new undefined{-layout-} [] [] `seq` return False
|
||||
|
||||
_ = x :: Int
|
||||
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 -}
|
||||
|
@@ -1,57 +1,85 @@
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
-- Unlike the rest of xmonad, this file is copyright under the terms of the
|
||||
-- GPL.
|
||||
|
||||
-- Generates a in-memory version of "man/xmonad.1.markdown" that has the list
|
||||
-- of known key-bindings is inserted automatically from "Config.hs". That
|
||||
-- document is then rendered with Pandoc as "man/xmonad.1" and
|
||||
-- "man/xmonad.1.html".
|
||||
--
|
||||
-- Generates man/xmonad.1 from man/xmonad.1.in by filling the list of
|
||||
-- keybindings with values scraped from Config.hs
|
||||
--
|
||||
-- Uses cabal to grab the xmonad version from xmonad.cabal
|
||||
--
|
||||
-- Uses pandoc to convert the "xmonad.1.markdown" to "xmonad.1"
|
||||
--
|
||||
-- Format for the docstrings in Config.hs takes the following form:
|
||||
--
|
||||
-- -- 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
|
||||
-- line. For example:
|
||||
--
|
||||
-- [ ((modMask .|. shiftMask, xK_Return), spawn "xterm") -- %! Launch an xterm
|
||||
--
|
||||
-- Here, mod-shift-return will be used as the keybinding name.
|
||||
import Control.Monad
|
||||
import Control.Applicative
|
||||
import Text.Regex.Posix
|
||||
-- Unlike the rest of xmonad, this file is released under the GNU General
|
||||
-- Public License version 2 or later.
|
||||
|
||||
import Control.Monad.IO.Class (liftIO)
|
||||
import Data.Char
|
||||
import Data.List
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.IO as TIO
|
||||
import Text.Pandoc
|
||||
import Text.Regex.Posix
|
||||
|
||||
import Distribution.PackageDescription.Parse
|
||||
import Distribution.Verbosity
|
||||
import Distribution.Package
|
||||
import Distribution.PackageDescription
|
||||
import Text.PrettyPrint.HughesPJ
|
||||
import Distribution.Text
|
||||
main :: IO ()
|
||||
main = do
|
||||
keybindings <- guessBindings
|
||||
|
||||
import Text.Pandoc -- works with 1.15.x
|
||||
markdownSource <- readFile "./man/xmonad.1.markdown"
|
||||
|
||||
releaseDate = "31 December 2012"
|
||||
runIOorExplode $ do
|
||||
parsed <- readMarkdown (def { readerStandalone = True, readerExtensions = pandocExtensions })
|
||||
. T.pack
|
||||
. unlines
|
||||
. replace "___KEYBINDINGS___" keybindings
|
||||
. lines
|
||||
$ markdownSource
|
||||
|
||||
trim :: String -> String
|
||||
trim = reverse . dropWhile isSpace . reverse . dropWhile isSpace
|
||||
manTemplate <- getDefaultTemplate "man"
|
||||
manBody <- writeMan def { writerTemplate = Just manTemplate } parsed
|
||||
liftIO $ TIO.writeFile "./man/xmonad.1" $ manBody
|
||||
liftIO $ putStrLn "Documentation created: man/xmonad.1"
|
||||
|
||||
guessKeys line = concat $ intersperse "-" (modifiers ++ [map toLower key])
|
||||
where modifiers = map (!!1) (line =~ "(mod|shift|control)Mask")
|
||||
(_, _, _, [key]) = line =~ "xK_([_[:alnum:]]+)" :: (String, String, String, [String])
|
||||
htmltemplate <- getDefaultTemplate "html"
|
||||
htmlBody <- writeHtml5String def
|
||||
{ writerTemplate = Just htmltemplate
|
||||
, writerTableOfContents = True }
|
||||
parsed
|
||||
liftIO $ TIO.writeFile "./man/xmonad.1.html" htmlBody
|
||||
liftIO $ putStrLn "Documentation created: man/xmonad.1.html"
|
||||
|
||||
-- | The 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 name of the key binding is omitted, the function tries to guess it
|
||||
-- from the rest of the line. For example:
|
||||
--
|
||||
-- @
|
||||
-- [ ((modMask .|. shiftMask, xK_Return), spawn "xterm") -- %! Launch an xterm
|
||||
-- @
|
||||
--
|
||||
-- Here, "mod-shift-return" will be used as the key binding name.
|
||||
|
||||
guessBindings :: IO String
|
||||
guessBindings = do
|
||||
buf <- readFile "./src/XMonad/Config.hs"
|
||||
return (intercalate "\n\n" (map markdownDefn (allBindings buf)))
|
||||
|
||||
allBindings :: String -> [(String, String)]
|
||||
allBindings xs = map (binding . map trim) (xs =~ "(.*)--(.*)%!(.*)")
|
||||
|
||||
binding :: [String] -> (String, String)
|
||||
binding [ _, bindingLine, "", desc ] = (guessKeys bindingLine, desc)
|
||||
binding [ _, _, keyCombo, desc ] = (keyCombo, desc)
|
||||
binding x = error ("binding: called with unexpected argument " ++ show x)
|
||||
|
||||
allBindings :: String -> [(String, String)]
|
||||
allBindings xs = map (binding . map trim) (xs =~ "(.*)--(.*)%!(.*)")
|
||||
guessKeys :: String -> String
|
||||
guessKeys line =
|
||||
case keys of
|
||||
[key] -> concat $ intersperse "-" (modifiers ++ [map toLower key])
|
||||
_ -> error ("guessKeys: unexpected number of keys " ++ show keys)
|
||||
where
|
||||
modifiers = map (!!1) (line =~ "(mod|shift|control)Mask")
|
||||
(_, _, _, keys) = line =~ "xK_([_[:alnum:]]+)" :: (String, String, String, [String])
|
||||
|
||||
-- FIXME: What escaping should we be doing on these strings?
|
||||
markdownDefn :: (String, String) -> String
|
||||
@@ -60,40 +88,5 @@ markdownDefn (key, desc) = key ++ "\n: " ++ desc
|
||||
replace :: Eq a => a -> a -> [a] -> [a]
|
||||
replace x y = map (\a -> if a == x then y else a)
|
||||
|
||||
-- rawSystem "pandoc" ["--read=markdown","--write=man","man/xmonad.1.markdown"]
|
||||
|
||||
main = do
|
||||
releaseName <- (show . disp . package . packageDescription)
|
||||
`liftM`readPackageDescription normal "xmonad.cabal"
|
||||
keybindings <- (intercalate "\n\n" . map markdownDefn . allBindings)
|
||||
`liftM` readFile "./src/XMonad/Config.hs"
|
||||
|
||||
let manHeader = unwords [".TH xmonad 1","\""++releaseDate++"\"",releaseName,"\"xmonad manual\""]
|
||||
|
||||
Right parsed <- readMarkdown def
|
||||
. unlines
|
||||
. replace "___KEYBINDINGS___" keybindings
|
||||
. lines
|
||||
<$> readFile "./man/xmonad.1.markdown"
|
||||
|
||||
Right template <- getDefaultTemplate Nothing "man"
|
||||
writeFile "./man/xmonad.1"
|
||||
. (manHeader ++)
|
||||
. writeMan def{ writerStandalone = True, writerTemplate = template }
|
||||
$ parsed
|
||||
putStrLn "Documentation created: man/xmonad.1"
|
||||
|
||||
Right template <- getDefaultTemplate Nothing "html"
|
||||
writeFile "./man/xmonad.1.html"
|
||||
. writeHtmlString def
|
||||
{ writerVariables =
|
||||
[("include-before"
|
||||
,"<h1>"++releaseName++"</h1>"++
|
||||
"<p>Section: xmonad manual (1)<br/>"++
|
||||
"Updated: "++releaseDate++"</p>"++
|
||||
"<hr/>")]
|
||||
, writerStandalone = True
|
||||
, writerTemplate = template
|
||||
, writerTableOfContents = True }
|
||||
$ parsed
|
||||
putStrLn "Documentation created: man/xmonad.1.html"
|
||||
trim :: String -> String
|
||||
trim = reverse . dropWhile isSpace . reverse . dropWhile isSpace
|
||||
|
194
xmonad.cabal
194
xmonad.cabal
@@ -1,125 +1,127 @@
|
||||
name: xmonad
|
||||
version: 0.12
|
||||
homepage: http://xmonad.org
|
||||
version: 0.15
|
||||
synopsis: A tiling window manager
|
||||
description:
|
||||
xmonad is a tiling window manager for X. Windows are arranged
|
||||
automatically to tile the screen without gaps or overlap, maximising
|
||||
screen use. All features of the window manager are accessible from
|
||||
the keyboard: a mouse is strictly optional. xmonad is written and
|
||||
extensible in Haskell. Custom layout algorithms, 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 screens.
|
||||
category: System
|
||||
description: xmonad is a tiling window manager for X. Windows are arranged
|
||||
automatically to tile the screen without gaps or overlap, maximising
|
||||
screen use. All features of the window manager are accessible from the
|
||||
keyboard: a mouse is strictly optional. xmonad is written and
|
||||
extensible in Haskell. Custom layout algorithms, 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
|
||||
screens.
|
||||
license: BSD3
|
||||
license-file: LICENSE
|
||||
author: Spencer Janssen
|
||||
author: Spencer Janssen, Don Stewart, Adam Vogt, David Roundy, Jason Creighton,
|
||||
Brent Yorgey, Peter Jones, Peter Simons, Andrea Rossato, Devin Mullins,
|
||||
Lukas Mai, Alec Berryman, Stefan O'Rear, Daniel Wagner, Peter J. Jones,
|
||||
Daniel Schoepe, Karsten Schoelzel, Neil Mitchell, Joachim Breitner,
|
||||
Peter De Wachter, Eric Mertens, Geoff Reedy, Michiel Derhaeg,
|
||||
Philipp Balzarek, Valery V. Vorotyntsev, Alex Tarkovsky, Fabian Beuke,
|
||||
Felix Hirn, Michael Sloan, Tomas Janousek, Vanessa McHale, Nicolas Pouillard,
|
||||
Aaron Denney, Austin Seipp, Benno Fünfstück, Brandon S Allbery, Chris Mears,
|
||||
Christian Thiemann, Clint Adams, Daniel Neri, David Lazar, Ferenc Wagner,
|
||||
Francesco Ariis, Gábor Lipták, Ivan N. Veselov, Ivan Tarasov, Javran Cheng,
|
||||
Jens Petersen, Joey Hess, Jonne Ransijn, Josh Holland, Khudyakov Alexey,
|
||||
Klaus Weidner, Michael G. Sloan, Mikkel Christiansen, Nicolas Dudebout,
|
||||
Ondřej Súkup, Paul Hebble, Shachaf Ben-Kiki, Siim Põder, Tim McIver,
|
||||
Trevor Elliott, Wouter Swierstra, Conrad Irwin, Tim Thelion
|
||||
maintainer: xmonad@haskell.org
|
||||
extra-source-files: README.md CHANGES.md TODO CONFIG STYLE
|
||||
tested-with: GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.3, GHC == 8.6.1
|
||||
category: System
|
||||
homepage: http://xmonad.org
|
||||
bug-reports: https://github.com/xmonad/xmonad/issues
|
||||
build-type: Simple
|
||||
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
|
||||
man/xmonad.1.markdown
|
||||
man/xmonad.1
|
||||
man/xmonad.1.html
|
||||
man/xmonad.hs
|
||||
util/GenerateManpage.hs
|
||||
util/hpcReport.sh
|
||||
cabal-version: >= 1.8
|
||||
bug-reports: https://github.com/xmonad/xmonad/issues
|
||||
build-type: Simple
|
||||
|
||||
tested-with:
|
||||
GHC==7.6.3,
|
||||
GHC==7.8.4,
|
||||
GHC==7.10.2
|
||||
|
||||
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
|
||||
default: False
|
||||
manual: True
|
||||
description: Testing mode, only build minimal components
|
||||
|
||||
flag generatemanpage
|
||||
description: Build the tool for generating the man page
|
||||
default: False
|
||||
manual: True
|
||||
default: False
|
||||
manual: True
|
||||
description: Build the tool for generating the man page
|
||||
|
||||
library
|
||||
hs-source-dirs: src
|
||||
exposed-modules: XMonad
|
||||
XMonad.Main
|
||||
XMonad.Core
|
||||
XMonad.Config
|
||||
XMonad.Layout
|
||||
XMonad.ManageHook
|
||||
XMonad.Operations
|
||||
XMonad.StackSet
|
||||
other-modules: Paths_xmonad
|
||||
exposed-modules: XMonad
|
||||
XMonad.Config
|
||||
XMonad.Core
|
||||
XMonad.Layout
|
||||
XMonad.Main
|
||||
XMonad.ManageHook
|
||||
XMonad.Operations
|
||||
XMonad.StackSet
|
||||
other-modules: Paths_xmonad
|
||||
hs-source-dirs: src
|
||||
build-depends: base >= 4.9 && < 5
|
||||
, X11 >= 1.8 && < 1.10
|
||||
, containers
|
||||
, data-default
|
||||
, directory
|
||||
, extensible-exceptions
|
||||
, filepath
|
||||
, mtl
|
||||
, process
|
||||
, setlocale
|
||||
, unix
|
||||
, utf8-string >= 0.3 && < 1.1
|
||||
ghc-options: -funbox-strict-fields -Wall -fno-warn-unused-do-bind
|
||||
|
||||
build-depends: base < 5 && >=3,
|
||||
containers,
|
||||
data-default,
|
||||
directory,
|
||||
extensible-exceptions,
|
||||
filepath,
|
||||
setlocale,
|
||||
mtl,
|
||||
process,
|
||||
unix,
|
||||
utf8-string >= 0.3 && < 1.1,
|
||||
X11>=1.5 && < 1.7
|
||||
|
||||
if true
|
||||
ghc-options: -funbox-strict-fields -Wall
|
||||
|
||||
if impl(ghc >= 6.12.1)
|
||||
ghc-options: -fno-warn-unused-do-bind
|
||||
if impl(ghc < 7.0.0)
|
||||
extensions: UndecidableInstances
|
||||
-- needed for XMonad.Config's instance Default (XConfig a)
|
||||
|
||||
|
||||
ghc-prof-options: -prof -auto-all
|
||||
|
||||
if flag(testing)
|
||||
buildable: False
|
||||
if flag(testing)
|
||||
buildable: False
|
||||
|
||||
executable xmonad
|
||||
main-is: Main.hs
|
||||
build-depends: base,
|
||||
mtl,
|
||||
unix,
|
||||
X11,
|
||||
xmonad
|
||||
ghc-options: -Wall
|
||||
if impl(ghc >= 6.12.1)
|
||||
ghc-options: -Wall -fno-warn-unused-do-bind
|
||||
main-is: Main.hs
|
||||
build-depends: base, X11, mtl, unix, xmonad
|
||||
ghc-options: -Wall -fno-warn-unused-do-bind
|
||||
|
||||
executable generatemanpage
|
||||
main-is: GenerateManpage.hs
|
||||
hs-source-dirs: util
|
||||
if flag(generatemanpage)
|
||||
build-depends: base,
|
||||
Cabal,
|
||||
pandoc,
|
||||
pretty,
|
||||
regex-posix
|
||||
else
|
||||
buildable: False
|
||||
main-is: GenerateManpage.hs
|
||||
hs-source-dirs: util
|
||||
|
||||
if flag(generatemanpage)
|
||||
build-depends: base, pandoc >= 2, regex-posix, text
|
||||
else
|
||||
buildable: False
|
||||
|
||||
-- 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
|
||||
type: exitcode-stdio-1.0
|
||||
main-is: Properties.hs
|
||||
other-modules: Instances
|
||||
Properties.Delete
|
||||
Properties.Failure
|
||||
Properties.Floating
|
||||
Properties.Focus
|
||||
Properties.GreedyView
|
||||
Properties.Insert
|
||||
Properties.Layout.Full
|
||||
Properties.Layout.Tall
|
||||
Properties.Screen
|
||||
Properties.Shift
|
||||
Properties.Stack
|
||||
Properties.StackSet
|
||||
Properties.Swap
|
||||
Properties.View
|
||||
Properties.Workspace
|
||||
Utils
|
||||
hs-source-dirs: tests
|
||||
build-depends: base, QuickCheck >= 2, X11, containers, extensible-exceptions, xmonad
|
||||
|
Reference in New Issue
Block a user