mirror of
https://github.com/xmonad/xmonad.git
synced 2025-08-02 13:11:53 -07:00
Compare commits
323 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a09ca446fb | ||
|
5641038500 | ||
|
990555c8ab | ||
|
a207e30751 | ||
|
460096dfc5 | ||
|
0817c6a7ff | ||
|
b59473b016 | ||
|
c2aeaffc03 | ||
|
7f95f5ef07 | ||
|
57b715972b | ||
|
a9866836d9 | ||
|
099233812e | ||
|
9cb13bdd3d | ||
|
86bb4d2a21 | ||
|
30103efbc8 | ||
|
a16541b834 | ||
|
202fecf7ba | ||
|
dd1e02555e | ||
|
4931bc4e41 | ||
|
67267b7346 | ||
|
4806b51a23 | ||
|
98e5d1583d | ||
|
dc48e9e9c9 | ||
|
572d02d8e8 | ||
|
f53db04285 | ||
|
2324d21202 | ||
|
f2c9c75f67 | ||
|
faf5cf7b27 | ||
|
9d0fd62cb2 | ||
|
5cdddab1f1 | ||
|
488b52ffaa | ||
|
83a8bb8d51 | ||
|
b06d885e76 | ||
|
a13a1dcee8 | ||
|
8965e41d06 | ||
|
28afc9bdc6 | ||
|
23f36d7e23 | ||
|
117583e473 | ||
|
bf6e66b100 | ||
|
366c09b3d7 | ||
|
ed5c8667b1 | ||
|
1695aeb28a | ||
|
521e8356fc | ||
|
45a89130d9 | ||
|
79602bfec5 | ||
|
711b28f494 | ||
|
0edb65107b | ||
|
845d770f35 | ||
|
3f1a37f216 | ||
|
165e25f9e0 | ||
|
9189d002dd | ||
|
29475fa7f8 | ||
|
e5a258f19c | ||
|
0c8ed88d8a | ||
|
9442871016 | ||
|
adb363a480 | ||
|
3d65a37c7e | ||
|
54d921c5a6 | ||
|
f61fdbaf0c | ||
|
d88643c639 | ||
|
eaaf0aafcd | ||
|
23df88d778 | ||
|
90d0ca4a2e | ||
|
f3f0c712d8 | ||
|
a5b708ba00 | ||
|
6fc90cd9d3 | ||
|
3009304352 | ||
|
5dd964e109 | ||
|
90c719148b | ||
|
831ca49331 | ||
|
2c9e24e0f6 | ||
|
a854cdaf9b | ||
|
c2904425e9 | ||
|
89ea1356c1 | ||
|
f4a5b88e64 | ||
|
906b9d34b3 | ||
|
c537a0658a | ||
|
8546ea095b | ||
|
c2e632a2b9 | ||
|
b6af6bb86a | ||
|
eee0a0dc39 | ||
|
0f5b5c2297 | ||
|
e25d090112 | ||
|
eb2ee340e4 | ||
|
79278d9475 | ||
|
dbe9c4f799 | ||
|
f6e4e278b5 | ||
|
673de33436 | ||
|
a5b6e09985 | ||
|
bb448cc293 | ||
|
ae4c5e26be | ||
|
54df2e9acd | ||
|
7f6d758ce5 | ||
|
9f64c2ca90 | ||
|
9849800dc5 | ||
|
a902fefaf1 | ||
|
0f708e76b1 | ||
|
6e6f562b0d | ||
|
12d1b31d6c | ||
|
b92bd28d97 | ||
|
e1daf46c75 | ||
|
a8e1249ba7 | ||
|
e3824687c7 | ||
|
c979ee67c0 | ||
|
6c92dd22ad | ||
|
66ac855959 | ||
|
7fab71f5f0 | ||
|
055dce10af | ||
|
292f19eab8 | ||
|
a204c9ed04 | ||
|
78f1a8e716 | ||
|
72794d92b1 | ||
|
546d0b5ddb | ||
|
a03b6e86de | ||
|
856c8b2c8d | ||
|
dbd441cc1b | ||
|
2e89e5ed23 | ||
|
0ebedbb533 | ||
|
c2a1a3c0a6 | ||
|
7d10e470d7 | ||
|
6608f0012b | ||
|
bc8f7ff133 | ||
|
7845145706 | ||
|
79afdfbbb9 | ||
|
8774081c15 | ||
|
60f36e78ba | ||
|
b198b80559 | ||
|
6bbd8b869e | ||
|
97aeb8577c | ||
|
0be6d2bec5 | ||
|
b1fef9b18c | ||
|
b5b95e27ce | ||
|
4e30ef13a7 | ||
|
d92125485a | ||
|
33a86c0cdb | ||
|
3bb653bf9c | ||
|
ebce32d891 | ||
|
b3bd9c90d1 | ||
|
5da25c5413 | ||
|
11d76e284c | ||
|
6d661203d3 | ||
|
30c2eeeeb3 | ||
|
d66e71d464 | ||
|
5c7e61def2 | ||
|
eb48bb4aef | ||
|
5eff329fc6 | ||
|
183e14725f | ||
|
0ab42d4228 | ||
|
52a5e7ca8c | ||
|
f89df98f40 | ||
|
30719202b9 | ||
|
aa18707c3e | ||
|
b77ba03ed9 | ||
|
be1d2269ce | ||
|
7bdc7ab9dc | ||
|
ae97c1f107 | ||
|
782ac25b8e | ||
|
8aa0d4a3e0 | ||
|
1f8e5b43e1 | ||
|
9813e218b0 | ||
|
403e4df624 | ||
|
aa35ea1856 | ||
|
3b6d00ba91 | ||
|
befc4bc8d8 | ||
|
6c31aad683 | ||
|
3e76270245 | ||
|
3a414660fc | ||
|
453010bb6d | ||
|
2ac8f0ea27 | ||
|
ad7288030f | ||
|
206fc918bb | ||
|
f5a60f82ee | ||
|
a1ee3b4530 | ||
|
89218fc57d | ||
|
71af4239bd | ||
|
f1d6316526 | ||
|
92d01e37a0 | ||
|
101c7052f4 | ||
|
7b7feeca42 | ||
|
cbe7ee7c03 | ||
|
8adb8463ab | ||
|
256eb29ef1 | ||
|
4ba9c8b8c1 | ||
|
98173777fe | ||
|
05aeef0dc2 | ||
|
85787ce059 | ||
|
d64aeba80f | ||
|
72cbe0667d | ||
|
af354f7528 | ||
|
1a4c95fac8 | ||
|
42d319545b | ||
|
2e6eb9068d | ||
|
13849c6230 | ||
|
6a7eb85e84 | ||
|
2a3c358533 | ||
|
28637d0db7 | ||
|
b14b3ffcec | ||
|
bbb4a0ef25 | ||
|
9db74715f2 | ||
|
5b064f474d | ||
|
bffa6dc2ce | ||
|
341dea5907 | ||
|
676530307b | ||
|
09425bbe43 | ||
|
1805666e9d | ||
|
40cd2081da | ||
|
66514127f3 | ||
|
fdc3bf0484 | ||
|
f97d2527ff | ||
|
7199d953a7 | ||
|
15653d4669 | ||
|
d64a22d8db | ||
|
2e9f8dc831 | ||
|
e8bfc5bb69 | ||
|
9e5b16ed8a | ||
|
d72da951c9 | ||
|
90101613e7 | ||
|
6caac22df1 | ||
|
e9987b1adf | ||
|
383ffb772e | ||
|
6379307baa | ||
|
d620639f7d | ||
|
a5cee9bac2 | ||
|
131fd3669f | ||
|
56f810d182 | ||
|
46f637e0be | ||
|
095d0e37d6 | ||
|
7e798afd11 | ||
|
669a9aed9e | ||
|
c869129c71 | ||
|
b8523a3c9b | ||
|
400730fe3b | ||
|
6c5204b91c | ||
|
910d99cb74 | ||
|
031bbd6230 | ||
|
05e8c204e9 | ||
|
2c91ea1621 | ||
|
5cdf428f55 | ||
|
22b579bd14 | ||
|
14d9fa247a | ||
|
cfe99998fc | ||
|
9fce3805fc | ||
|
fd243ca1c1 | ||
|
c90df53081 | ||
|
e4659c2475 | ||
|
caae51c399 | ||
|
fb390fa9cc | ||
|
4a0b166998 | ||
|
b9ce5b034b | ||
|
a90558c07e | ||
|
56b0f850bc | ||
|
51a179dc68 | ||
|
8a8d5f71b1 | ||
|
4b69a456cc | ||
|
e12d0be1b2 | ||
|
002326ceb1 | ||
|
758e3d85e6 | ||
|
f5bd77a7f8 | ||
|
519c79a57e | ||
|
0aa40480f9 | ||
|
36dd6afb49 | ||
|
3015968ee4 | ||
|
5bb6c88b41 | ||
|
f875a56620 | ||
|
70a75e5e3f | ||
|
735fb58f6c | ||
|
e8f48b77f9 | ||
|
e363c44bb0 | ||
|
ec1c3e0159 | ||
|
2a1a18023a | ||
|
ff738988d3 | ||
|
fc4657d529 | ||
|
bd961b7866 | ||
|
3df77d6f20 | ||
|
b59c768cdd | ||
|
a37a3cb6e8 | ||
|
823581816a | ||
|
3ea0d74954 | ||
|
b3c860b892 | ||
|
958e701bf4 | ||
|
28e75da77f | ||
|
a812869c0c | ||
|
96fb01b9be | ||
|
6fa0bb7d4f | ||
|
0a040cbc96 | ||
|
96d4f1fe85 | ||
|
30f2d9f325 | ||
|
5be975b4f2 | ||
|
22c370a068 | ||
|
c3e032e08e | ||
|
2c3bf17dfb | ||
|
a926b68838 | ||
|
6dc1e319d1 | ||
|
0db71d552a | ||
|
f52ed1d19e | ||
|
ffcb01ad80 | ||
|
66d2241703 | ||
|
11814bfec3 | ||
|
bbc1c010ed | ||
|
eeeae810ae | ||
|
3c6f52a349 | ||
|
40466b2be2 | ||
|
6a4a742feb | ||
|
f8b243b66e | ||
|
fa9a3abe49 | ||
|
78b967198b | ||
|
68574be2cf | ||
|
2ab37aa4a4 | ||
|
5ab9fede6c | ||
|
a81ba4ba53 | ||
|
b65728032d | ||
|
e747377775 | ||
|
d6f88918de | ||
|
21cd920b61 | ||
|
bb13853929 | ||
|
3d1720c3f3 | ||
|
0614ffb65c | ||
|
85b47fc3ac | ||
|
1a99280227 | ||
|
e8133eb9a6 | ||
|
4ccaff8f25 | ||
|
56dc186e68 | ||
|
10b2efe81c |
16
.github/ISSUE_TEMPLATE.md
vendored
16
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,12 +1,16 @@
|
|||||||
### Problem Description
|
### Problem Description
|
||||||
|
|
||||||
Describe the problem you are having, what you expect to happen
|
Describe the problem you are having and what you expect to happen
|
||||||
instead, and how to reproduce the problem.
|
instead.
|
||||||
|
|
||||||
|
### Steps to Reproduce
|
||||||
|
|
||||||
|
Give detailed step-by-step instructions on how to reproduce the problem.
|
||||||
|
|
||||||
### Configuration File
|
### Configuration File
|
||||||
|
|
||||||
Please include the smallest configuration file that reproduces the
|
Please include the smallest _full_ configuration file that reproduces
|
||||||
problem you are experiencing:
|
the problem you are experiencing:
|
||||||
|
|
||||||
```haskell
|
```haskell
|
||||||
module Main (main) where
|
module Main (main) where
|
||||||
@@ -21,4 +25,6 @@ main = xmonad def
|
|||||||
|
|
||||||
- [ ] I've read [CONTRIBUTING.md](https://github.com/xmonad/xmonad/blob/master/CONTRIBUTING.md)
|
- [ ] 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)
|
- I tested my configuration
|
||||||
|
- [ ] With `xmonad` version XXX (commit XXX if using git)
|
||||||
|
- [ ] With `xmonad-contrib` version XXX (commit XXX if using git)
|
||||||
|
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -9,6 +9,7 @@ behind them.
|
|||||||
|
|
||||||
- [ ] I've confirmed these changes don't belong in xmonad-contrib instead
|
- [ ] 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've considered how to best test these changes (property, unit,
|
||||||
|
manually, ...) and concluded: XXX
|
||||||
|
|
||||||
- [ ] I updated the `CHANGES.md` file
|
- [ ] I updated the `CHANGES.md` file
|
||||||
|
6
.github/dependabot.yml
vendored
Normal file
6
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
33
.github/workflows/generatemanpage.yml
vendored
Normal file
33
.github/workflows/generatemanpage.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
name: Generate manpage
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Clone project
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
set -ex
|
||||||
|
sudo apt install -y pandoc
|
||||||
|
|
||||||
|
- name: Generate manpage
|
||||||
|
run: |
|
||||||
|
set -ex
|
||||||
|
for d in /opt/ghc/*/bin; do PATH="$d:$PATH"; break; done
|
||||||
|
make -B -C man
|
||||||
|
|
||||||
|
- name: Commit/push if changed
|
||||||
|
run: |
|
||||||
|
set -ex
|
||||||
|
git config user.name github-actions
|
||||||
|
git config user.email github-actions@github.com
|
||||||
|
git diff --quiet --exit-code && exit
|
||||||
|
git commit -a -m 'man: Update'
|
||||||
|
git push
|
132
.github/workflows/haskell-ci-hackage.patch
vendored
Normal file
132
.github/workflows/haskell-ci-hackage.patch
vendored
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
Piggy-back on the haskell-ci workflow for automatic releases to Hackage.
|
||||||
|
|
||||||
|
This extends the workflow with two additional triggers:
|
||||||
|
|
||||||
|
* When the Haskell-CI workflow is triggered manually with a non-empty version
|
||||||
|
input (matching the version in the cabal file), a candidate release is
|
||||||
|
uploaded to Hackage and docs are submitted for it as Hackage can't build
|
||||||
|
them itself (https://github.com/haskell/hackage-server/issues/925).
|
||||||
|
|
||||||
|
Note that promoting the candidate on Hackage discards the uploaded docs
|
||||||
|
(https://github.com/haskell/hackage-server/issues/70). Don't do that.
|
||||||
|
|
||||||
|
* When a release is created on GitHub, a final release is uploaded to Hackage
|
||||||
|
and docs are submitted for it.
|
||||||
|
|
||||||
|
The automation uses a special Hackage user: https://hackage.haskell.org/user/xmonad
|
||||||
|
and each repo (X11, xmonad, xmonad-contrib) has its own HACKAGE_API_KEY token
|
||||||
|
set in GitHub repository secrets.
|
||||||
|
|
||||||
|
--- .github/workflows/haskell-ci.yml.orig
|
||||||
|
+++ .github/workflows/haskell-ci.yml
|
||||||
|
@@ -14,8 +14,15 @@
|
||||||
|
#
|
||||||
|
name: Haskell-CI
|
||||||
|
on:
|
||||||
|
- - push
|
||||||
|
- - pull_request
|
||||||
|
+ push:
|
||||||
|
+ pull_request:
|
||||||
|
+ release:
|
||||||
|
+ types:
|
||||||
|
+ - published
|
||||||
|
+ workflow_dispatch:
|
||||||
|
+ inputs:
|
||||||
|
+ version:
|
||||||
|
+ description: candidate version (must match version in cabal file)
|
||||||
|
jobs:
|
||||||
|
linux:
|
||||||
|
name: Haskell-CI - Linux - ${{ matrix.compiler }}
|
||||||
|
@@ -31,6 +38,7 @@
|
||||||
|
compilerVersion: 9.0.2
|
||||||
|
setup-method: ghcup
|
||||||
|
allow-failure: false
|
||||||
|
+ upload: true
|
||||||
|
- compiler: ghc-8.10.7
|
||||||
|
compilerKind: ghc
|
||||||
|
compilerVersion: 8.10.7
|
||||||
|
@@ -237,7 +237,7 @@
|
||||||
|
${CABAL} -vnormal check
|
||||||
|
- name: haddock
|
||||||
|
run: |
|
||||||
|
- $CABAL v2-haddock --disable-documentation $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all
|
||||||
|
+ $CABAL v2-haddock $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH --haddock-for-hackage --builddir $GITHUB_WORKSPACE/haddock all
|
||||||
|
- name: unconstrained build
|
||||||
|
run: |
|
||||||
|
rm -f cabal.project.local
|
||||||
|
@@ -248,3 +248,75 @@
|
||||||
|
with:
|
||||||
|
key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
|
||||||
|
path: ~/.cabal/store
|
||||||
|
+ - name: upload artifacts (sdist)
|
||||||
|
+ if: matrix.upload
|
||||||
|
+ uses: actions/upload-artifact@v3
|
||||||
|
+ with:
|
||||||
|
+ path: ${{ github.workspace }}/sdist/*.tar.gz
|
||||||
|
+ - name: upload artifacts (haddock)
|
||||||
|
+ if: matrix.upload
|
||||||
|
+ uses: actions/upload-artifact@v3
|
||||||
|
+ with:
|
||||||
|
+ path: ${{ github.workspace }}/haddock/*-docs.tar.gz
|
||||||
|
+ - name: hackage upload (candidate)
|
||||||
|
+ if: matrix.upload && github.event_name == 'workflow_dispatch' && github.event.inputs.version != ''
|
||||||
|
+ shell: bash
|
||||||
|
+ run: |
|
||||||
|
+ set -ex
|
||||||
|
+ PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
||||||
|
+ res=$(
|
||||||
|
+ curl \
|
||||||
|
+ --silent --show-error --output /dev/stderr --write-out '%{http_code}' \
|
||||||
|
+ --header "Accept: text/plain" \
|
||||||
|
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||||
|
+ --form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
|
||||||
|
+ https://hackage.haskell.org/packages/candidates/
|
||||||
|
+ )
|
||||||
|
+ [[ $res == 2?? ]] # TODO: --fail-with-body once curl 7.76.0 is available
|
||||||
|
+ res=$(
|
||||||
|
+ curl \
|
||||||
|
+ --silent --show-error --output /dev/stderr --write-out '%{http_code}' \
|
||||||
|
+ -X PUT \
|
||||||
|
+ --header "Accept: text/plain" \
|
||||||
|
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||||
|
+ --header "Content-Type: application/x-tar" \
|
||||||
|
+ --header "Content-Encoding: gzip" \
|
||||||
|
+ --data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
|
||||||
|
+ https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/candidate/docs
|
||||||
|
+ )
|
||||||
|
+ [[ $res == 2?? ]]
|
||||||
|
+ env:
|
||||||
|
+ HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
||||||
|
+ PACKAGE_NAME: ${{ github.event.repository.name }}
|
||||||
|
+ PACKAGE_VERSION: ${{ github.event.inputs.version }}
|
||||||
|
+ - name: hackage upload (release)
|
||||||
|
+ if: matrix.upload && github.event_name == 'release'
|
||||||
|
+ shell: bash
|
||||||
|
+ run: |
|
||||||
|
+ set -ex
|
||||||
|
+ PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
||||||
|
+ res=$(
|
||||||
|
+ curl \
|
||||||
|
+ --silent --show-error --output /dev/stderr --write-out '%{http_code}' \
|
||||||
|
+ --header "Accept: text/plain" \
|
||||||
|
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||||
|
+ --form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
|
||||||
|
+ https://hackage.haskell.org/packages/
|
||||||
|
+ )
|
||||||
|
+ [[ $res == 2?? ]] # TODO: --fail-with-body once curl 7.76.0 is available
|
||||||
|
+ res=$(
|
||||||
|
+ curl \
|
||||||
|
+ --silent --show-error --output /dev/stderr --write-out '%{http_code}' \
|
||||||
|
+ -X PUT \
|
||||||
|
+ --header "Accept: text/plain" \
|
||||||
|
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||||
|
+ --header "Content-Type: application/x-tar" \
|
||||||
|
+ --header "Content-Encoding: gzip" \
|
||||||
|
+ --data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
|
||||||
|
+ https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/docs
|
||||||
|
+ )
|
||||||
|
+ [[ $res == 2?? ]]
|
||||||
|
+ env:
|
||||||
|
+ HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
||||||
|
+ PACKAGE_NAME: ${{ github.event.repository.name }}
|
||||||
|
+ PACKAGE_VERSION: ${{ github.event.release.tag_name }}
|
324
.github/workflows/haskell-ci.yml
vendored
Normal file
324
.github/workflows/haskell-ci.yml
vendored
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
# This GitHub workflow config has been generated by a script via
|
||||||
|
#
|
||||||
|
# haskell-ci 'github' 'cabal.project'
|
||||||
|
#
|
||||||
|
# To regenerate the script (for example after adjusting tested-with) run
|
||||||
|
#
|
||||||
|
# haskell-ci regenerate
|
||||||
|
#
|
||||||
|
# For more information, see https://github.com/haskell-CI/haskell-ci
|
||||||
|
#
|
||||||
|
# version: 0.15.20230312
|
||||||
|
#
|
||||||
|
# REGENDATA ("0.15.20230312",["github","cabal.project"])
|
||||||
|
#
|
||||||
|
name: Haskell-CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
release:
|
||||||
|
types:
|
||||||
|
- published
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: candidate version (must match version in cabal file)
|
||||||
|
jobs:
|
||||||
|
linux:
|
||||||
|
name: Haskell-CI - Linux - ${{ matrix.compiler }}
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
timeout-minutes:
|
||||||
|
60
|
||||||
|
container:
|
||||||
|
image: buildpack-deps:bionic
|
||||||
|
continue-on-error: ${{ matrix.allow-failure }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- compiler: ghc-9.6.1
|
||||||
|
compilerKind: ghc
|
||||||
|
compilerVersion: 9.6.1
|
||||||
|
setup-method: ghcup
|
||||||
|
allow-failure: false
|
||||||
|
- compiler: ghc-9.4.4
|
||||||
|
compilerKind: ghc
|
||||||
|
compilerVersion: 9.4.4
|
||||||
|
setup-method: ghcup
|
||||||
|
allow-failure: false
|
||||||
|
- compiler: ghc-9.2.7
|
||||||
|
compilerKind: ghc
|
||||||
|
compilerVersion: 9.2.7
|
||||||
|
setup-method: ghcup
|
||||||
|
allow-failure: false
|
||||||
|
- compiler: ghc-9.0.2
|
||||||
|
compilerKind: ghc
|
||||||
|
compilerVersion: 9.0.2
|
||||||
|
setup-method: ghcup
|
||||||
|
allow-failure: false
|
||||||
|
upload: true
|
||||||
|
- compiler: ghc-8.10.7
|
||||||
|
compilerKind: ghc
|
||||||
|
compilerVersion: 8.10.7
|
||||||
|
setup-method: ghcup
|
||||||
|
allow-failure: false
|
||||||
|
- compiler: ghc-8.8.4
|
||||||
|
compilerKind: ghc
|
||||||
|
compilerVersion: 8.8.4
|
||||||
|
setup-method: hvr-ppa
|
||||||
|
allow-failure: false
|
||||||
|
- compiler: ghc-8.6.5
|
||||||
|
compilerKind: ghc
|
||||||
|
compilerVersion: 8.6.5
|
||||||
|
setup-method: hvr-ppa
|
||||||
|
allow-failure: false
|
||||||
|
fail-fast: false
|
||||||
|
steps:
|
||||||
|
- name: apt
|
||||||
|
run: |
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common libtinfo5
|
||||||
|
if [ "${{ matrix.setup-method }}" = ghcup ]; then
|
||||||
|
mkdir -p "$HOME/.ghcup/bin"
|
||||||
|
curl -sL https://downloads.haskell.org/ghcup/0.1.18.0/x86_64-linux-ghcup-0.1.18.0 > "$HOME/.ghcup/bin/ghcup"
|
||||||
|
chmod a+x "$HOME/.ghcup/bin/ghcup"
|
||||||
|
"$HOME/.ghcup/bin/ghcup" install ghc "$HCVER" || (cat "$HOME"/.ghcup/logs/*.* && false)
|
||||||
|
"$HOME/.ghcup/bin/ghcup" install cabal 3.10.1.0 || (cat "$HOME"/.ghcup/logs/*.* && false)
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y libx11-dev libxext-dev libxinerama-dev libxrandr-dev libxss-dev
|
||||||
|
else
|
||||||
|
apt-add-repository -y 'ppa:hvr/ghc'
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y "$HCNAME" libx11-dev libxext-dev libxinerama-dev libxrandr-dev libxss-dev
|
||||||
|
mkdir -p "$HOME/.ghcup/bin"
|
||||||
|
curl -sL https://downloads.haskell.org/ghcup/0.1.18.0/x86_64-linux-ghcup-0.1.18.0 > "$HOME/.ghcup/bin/ghcup"
|
||||||
|
chmod a+x "$HOME/.ghcup/bin/ghcup"
|
||||||
|
"$HOME/.ghcup/bin/ghcup" install cabal 3.10.1.0 || (cat "$HOME"/.ghcup/logs/*.* && false)
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
HCKIND: ${{ matrix.compilerKind }}
|
||||||
|
HCNAME: ${{ matrix.compiler }}
|
||||||
|
HCVER: ${{ matrix.compilerVersion }}
|
||||||
|
- name: Set PATH and environment variables
|
||||||
|
run: |
|
||||||
|
echo "$HOME/.cabal/bin" >> $GITHUB_PATH
|
||||||
|
echo "LANG=C.UTF-8" >> "$GITHUB_ENV"
|
||||||
|
echo "CABAL_DIR=$HOME/.cabal" >> "$GITHUB_ENV"
|
||||||
|
echo "CABAL_CONFIG=$HOME/.cabal/config" >> "$GITHUB_ENV"
|
||||||
|
HCDIR=/opt/$HCKIND/$HCVER
|
||||||
|
if [ "${{ matrix.setup-method }}" = ghcup ]; then
|
||||||
|
HC=$HOME/.ghcup/bin/$HCKIND-$HCVER
|
||||||
|
echo "HC=$HC" >> "$GITHUB_ENV"
|
||||||
|
echo "HCPKG=$HOME/.ghcup/bin/$HCKIND-pkg-$HCVER" >> "$GITHUB_ENV"
|
||||||
|
echo "HADDOCK=$HOME/.ghcup/bin/haddock-$HCVER" >> "$GITHUB_ENV"
|
||||||
|
echo "CABAL=$HOME/.ghcup/bin/cabal-3.10.1.0 -vnormal+nowrap" >> "$GITHUB_ENV"
|
||||||
|
else
|
||||||
|
HC=$HCDIR/bin/$HCKIND
|
||||||
|
echo "HC=$HC" >> "$GITHUB_ENV"
|
||||||
|
echo "HCPKG=$HCDIR/bin/$HCKIND-pkg" >> "$GITHUB_ENV"
|
||||||
|
echo "HADDOCK=$HCDIR/bin/haddock" >> "$GITHUB_ENV"
|
||||||
|
echo "CABAL=$HOME/.ghcup/bin/cabal-3.10.1.0 -vnormal+nowrap" >> "$GITHUB_ENV"
|
||||||
|
fi
|
||||||
|
|
||||||
|
HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\d+)\.(\d+)\.(\d+)(\.(\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')
|
||||||
|
echo "HCNUMVER=$HCNUMVER" >> "$GITHUB_ENV"
|
||||||
|
echo "ARG_TESTS=--enable-tests" >> "$GITHUB_ENV"
|
||||||
|
echo "ARG_BENCH=--enable-benchmarks" >> "$GITHUB_ENV"
|
||||||
|
echo "HEADHACKAGE=false" >> "$GITHUB_ENV"
|
||||||
|
echo "ARG_COMPILER=--$HCKIND --with-compiler=$HC" >> "$GITHUB_ENV"
|
||||||
|
echo "GHCJSARITH=0" >> "$GITHUB_ENV"
|
||||||
|
env:
|
||||||
|
HCKIND: ${{ matrix.compilerKind }}
|
||||||
|
HCNAME: ${{ matrix.compiler }}
|
||||||
|
HCVER: ${{ matrix.compilerVersion }}
|
||||||
|
- name: env
|
||||||
|
run: |
|
||||||
|
env
|
||||||
|
- name: write cabal config
|
||||||
|
run: |
|
||||||
|
mkdir -p $CABAL_DIR
|
||||||
|
cat >> $CABAL_CONFIG <<EOF
|
||||||
|
remote-build-reporting: anonymous
|
||||||
|
write-ghc-environment-files: never
|
||||||
|
remote-repo-cache: $CABAL_DIR/packages
|
||||||
|
logs-dir: $CABAL_DIR/logs
|
||||||
|
world-file: $CABAL_DIR/world
|
||||||
|
extra-prog-path: $CABAL_DIR/bin
|
||||||
|
symlink-bindir: $CABAL_DIR/bin
|
||||||
|
installdir: $CABAL_DIR/bin
|
||||||
|
build-summary: $CABAL_DIR/logs/build.log
|
||||||
|
store-dir: $CABAL_DIR/store
|
||||||
|
install-dirs user
|
||||||
|
prefix: $CABAL_DIR
|
||||||
|
repository hackage.haskell.org
|
||||||
|
url: http://hackage.haskell.org/
|
||||||
|
EOF
|
||||||
|
cat >> $CABAL_CONFIG <<EOF
|
||||||
|
program-default-options
|
||||||
|
ghc-options: $GHCJOBS +RTS -M3G -RTS
|
||||||
|
EOF
|
||||||
|
cat $CABAL_CONFIG
|
||||||
|
- name: versions
|
||||||
|
run: |
|
||||||
|
$HC --version || true
|
||||||
|
$HC --print-project-git-commit-id || true
|
||||||
|
$CABAL --version || true
|
||||||
|
- name: update cabal index
|
||||||
|
run: |
|
||||||
|
$CABAL v2-update -v
|
||||||
|
- name: install cabal-plan
|
||||||
|
run: |
|
||||||
|
mkdir -p $HOME/.cabal/bin
|
||||||
|
curl -sL https://github.com/haskell-hvr/cabal-plan/releases/download/v0.6.2.0/cabal-plan-0.6.2.0-x86_64-linux.xz > cabal-plan.xz
|
||||||
|
echo 'de73600b1836d3f55e32d80385acc055fd97f60eaa0ab68a755302685f5d81bc cabal-plan.xz' | sha256sum -c -
|
||||||
|
xz -d < cabal-plan.xz > $HOME/.cabal/bin/cabal-plan
|
||||||
|
rm -f cabal-plan.xz
|
||||||
|
chmod a+x $HOME/.cabal/bin/cabal-plan
|
||||||
|
cabal-plan --version
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
path: source
|
||||||
|
- name: initial cabal.project for sdist
|
||||||
|
run: |
|
||||||
|
touch cabal.project
|
||||||
|
echo "packages: $GITHUB_WORKSPACE/source/." >> cabal.project
|
||||||
|
cat cabal.project
|
||||||
|
- name: sdist
|
||||||
|
run: |
|
||||||
|
mkdir -p sdist
|
||||||
|
$CABAL sdist all --output-dir $GITHUB_WORKSPACE/sdist
|
||||||
|
- name: unpack
|
||||||
|
run: |
|
||||||
|
mkdir -p unpacked
|
||||||
|
find sdist -maxdepth 1 -type f -name '*.tar.gz' -exec tar -C $GITHUB_WORKSPACE/unpacked -xzvf {} \;
|
||||||
|
- name: generate cabal.project
|
||||||
|
run: |
|
||||||
|
PKGDIR_xmonad="$(find "$GITHUB_WORKSPACE/unpacked" -maxdepth 1 -type d -regex '.*/xmonad-[0-9.]*')"
|
||||||
|
echo "PKGDIR_xmonad=${PKGDIR_xmonad}" >> "$GITHUB_ENV"
|
||||||
|
rm -f cabal.project cabal.project.local
|
||||||
|
touch cabal.project
|
||||||
|
touch cabal.project.local
|
||||||
|
echo "packages: ${PKGDIR_xmonad}" >> cabal.project
|
||||||
|
echo "package xmonad" >> cabal.project
|
||||||
|
echo " ghc-options: -Werror=missing-methods" >> cabal.project
|
||||||
|
cat >> cabal.project <<EOF
|
||||||
|
optimization: False
|
||||||
|
|
||||||
|
package xmonad
|
||||||
|
flags: +pedantic
|
||||||
|
EOF
|
||||||
|
$HCPKG list --simple-output --names-only | perl -ne 'for (split /\s+/) { print "constraints: $_ installed\n" unless /^(xmonad)$/; }' >> cabal.project.local
|
||||||
|
cat cabal.project
|
||||||
|
cat cabal.project.local
|
||||||
|
- name: dump install plan
|
||||||
|
run: |
|
||||||
|
$CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dry-run all
|
||||||
|
cabal-plan
|
||||||
|
- name: restore cache
|
||||||
|
uses: actions/cache/restore@v3
|
||||||
|
with:
|
||||||
|
key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
|
||||||
|
path: ~/.cabal/store
|
||||||
|
restore-keys: ${{ runner.os }}-${{ matrix.compiler }}-
|
||||||
|
- name: install dependencies
|
||||||
|
run: |
|
||||||
|
$CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --dependencies-only -j2 all
|
||||||
|
$CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dependencies-only -j2 all
|
||||||
|
- name: build w/o tests
|
||||||
|
run: |
|
||||||
|
$CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
$CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --write-ghc-environment-files=always
|
||||||
|
- name: tests
|
||||||
|
run: |
|
||||||
|
$CABAL v2-test $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --test-show-details=direct
|
||||||
|
- name: cabal check
|
||||||
|
run: |
|
||||||
|
cd ${PKGDIR_xmonad} || false
|
||||||
|
${CABAL} -vnormal check
|
||||||
|
- name: haddock
|
||||||
|
run: |
|
||||||
|
$CABAL v2-haddock $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH --haddock-for-hackage --builddir $GITHUB_WORKSPACE/haddock all
|
||||||
|
- name: unconstrained build
|
||||||
|
run: |
|
||||||
|
rm -f cabal.project.local
|
||||||
|
$CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
|
||||||
|
- name: save cache
|
||||||
|
uses: actions/cache/save@v3
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
|
||||||
|
path: ~/.cabal/store
|
||||||
|
- name: upload artifacts (sdist)
|
||||||
|
if: matrix.upload
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
path: ${{ github.workspace }}/sdist/*.tar.gz
|
||||||
|
- name: upload artifacts (haddock)
|
||||||
|
if: matrix.upload
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
path: ${{ github.workspace }}/haddock/*-docs.tar.gz
|
||||||
|
- name: hackage upload (candidate)
|
||||||
|
if: matrix.upload && github.event_name == 'workflow_dispatch' && github.event.inputs.version != ''
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -ex
|
||||||
|
PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
||||||
|
res=$(
|
||||||
|
curl \
|
||||||
|
--silent --show-error --output /dev/stderr --write-out '%{http_code}' \
|
||||||
|
--header "Accept: text/plain" \
|
||||||
|
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||||
|
--form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
|
||||||
|
https://hackage.haskell.org/packages/candidates/
|
||||||
|
)
|
||||||
|
[[ $res == 2?? ]] # TODO: --fail-with-body once curl 7.76.0 is available
|
||||||
|
res=$(
|
||||||
|
curl \
|
||||||
|
--silent --show-error --output /dev/stderr --write-out '%{http_code}' \
|
||||||
|
-X PUT \
|
||||||
|
--header "Accept: text/plain" \
|
||||||
|
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||||
|
--header "Content-Type: application/x-tar" \
|
||||||
|
--header "Content-Encoding: gzip" \
|
||||||
|
--data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
|
||||||
|
https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/candidate/docs
|
||||||
|
)
|
||||||
|
[[ $res == 2?? ]]
|
||||||
|
env:
|
||||||
|
HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
||||||
|
PACKAGE_NAME: ${{ github.event.repository.name }}
|
||||||
|
PACKAGE_VERSION: ${{ github.event.inputs.version }}
|
||||||
|
- name: hackage upload (release)
|
||||||
|
if: matrix.upload && github.event_name == 'release'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -ex
|
||||||
|
PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
||||||
|
res=$(
|
||||||
|
curl \
|
||||||
|
--silent --show-error --output /dev/stderr --write-out '%{http_code}' \
|
||||||
|
--header "Accept: text/plain" \
|
||||||
|
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||||
|
--form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
|
||||||
|
https://hackage.haskell.org/packages/
|
||||||
|
)
|
||||||
|
[[ $res == 2?? ]] # TODO: --fail-with-body once curl 7.76.0 is available
|
||||||
|
res=$(
|
||||||
|
curl \
|
||||||
|
--silent --show-error --output /dev/stderr --write-out '%{http_code}' \
|
||||||
|
-X PUT \
|
||||||
|
--header "Accept: text/plain" \
|
||||||
|
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||||
|
--header "Content-Type: application/x-tar" \
|
||||||
|
--header "Content-Encoding: gzip" \
|
||||||
|
--data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
|
||||||
|
https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/docs
|
||||||
|
)
|
||||||
|
[[ $res == 2?? ]]
|
||||||
|
env:
|
||||||
|
HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
||||||
|
PACKAGE_NAME: ${{ github.event.repository.name }}
|
||||||
|
PACKAGE_VERSION: ${{ github.event.release.tag_name }}
|
22
.github/workflows/hlint.yaml
vendored
Normal file
22
.github/workflows/hlint.yaml
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
name: hlint
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
hlint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: 'Set up HLint'
|
||||||
|
uses: haskell/actions/hlint-setup@v2
|
||||||
|
with:
|
||||||
|
version: '3.5'
|
||||||
|
|
||||||
|
- name: 'Run HLint'
|
||||||
|
uses: haskell/actions/hlint-run@v2
|
||||||
|
with:
|
||||||
|
path: '.'
|
||||||
|
fail-on: status
|
25
.github/workflows/nix.yml
vendored
Normal file
25
.github/workflows/nix.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
name: Nix
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-20.04 # FIXME
|
||||||
|
name: Nix Flake - Linux
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Install Nix
|
||||||
|
uses: cachix/install-nix-action@v18
|
||||||
|
with:
|
||||||
|
install_url: https://nixos-nix-install-tests.cachix.org/serve/i6laym9jw3wg9mw6ncyrk6gjx4l34vvx/install
|
||||||
|
install_options: '--tarball-url-prefix https://nixos-nix-install-tests.cachix.org/serve'
|
||||||
|
extra_nix_config: |
|
||||||
|
experimental-features = nix-command flakes
|
||||||
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Clone project
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Build
|
||||||
|
run: nix build --print-build-logs
|
50
.github/workflows/packdeps.yml
vendored
Normal file
50
.github/workflows/packdeps.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
name: Packdeps
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
# Run every Saturday
|
||||||
|
- cron: '0 3 * * 6'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
packdeps:
|
||||||
|
name: Packdeps
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Clone project
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Setup Haskell
|
||||||
|
uses: haskell/actions/setup@v2
|
||||||
|
with:
|
||||||
|
# packdeps doesn't build with newer as of 2021-10
|
||||||
|
ghc-version: '8.8'
|
||||||
|
- name: Install packdeps
|
||||||
|
run: |
|
||||||
|
set -ex
|
||||||
|
echo "$HOME/.cabal/bin" >> $GITHUB_PATH
|
||||||
|
cd # go somewhere without a cabal.project
|
||||||
|
cabal install packdeps
|
||||||
|
- name: Check package bounds (all)
|
||||||
|
continue-on-error: true
|
||||||
|
run: |
|
||||||
|
set -ex
|
||||||
|
packdeps \
|
||||||
|
--exclude X11 \
|
||||||
|
*.cabal
|
||||||
|
- name: Check package bounds (preferred)
|
||||||
|
run: |
|
||||||
|
set -ex
|
||||||
|
packdeps \
|
||||||
|
--preferred \
|
||||||
|
--exclude X11 \
|
||||||
|
*.cabal
|
||||||
|
|
||||||
|
workflow-keepalive:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Re-enable workflow
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
gh api -X PUT repos/${{ github.repository }}/actions/workflows/packdeps.yml/enable
|
76
.github/workflows/stack.yml
vendored
Normal file
76
.github/workflows/stack.yml
vendored
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
name: Stack
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Stack CI - Linux - ${{ matrix.resolver }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- resolver: lts-14 # GHC 8.6
|
||||||
|
- resolver: lts-16 # GHC 8.8
|
||||||
|
- resolver: lts-18 # GHC 8.10
|
||||||
|
- resolver: lts-19 # GHC 9.0
|
||||||
|
- resolver: lts-20 # GHC 9.2
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Clone project
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install C dependencies
|
||||||
|
run: |
|
||||||
|
set -ex
|
||||||
|
sudo apt update -y
|
||||||
|
sudo apt install -y \
|
||||||
|
libx11-dev \
|
||||||
|
libxext-dev \
|
||||||
|
libxinerama-dev \
|
||||||
|
libxrandr-dev \
|
||||||
|
libxss-dev \
|
||||||
|
#
|
||||||
|
|
||||||
|
- name: Refresh caches once a month
|
||||||
|
id: cache-date
|
||||||
|
# GHA writes caches on the first miss and then never updates them again;
|
||||||
|
# force updating the cache at least once a month. Additionally, the
|
||||||
|
# date is prefixed with an epoch number to let us manually refresh the
|
||||||
|
# cache when needed. This is a workaround for https://github.com/actions/cache/issues/2
|
||||||
|
run: |
|
||||||
|
date +date=1-%Y-%m >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Cache Haskell package metadata
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ~/.stack/pantry
|
||||||
|
key: stack-pantry-${{ runner.os }}-${{ steps.cache-date.outputs.date }}
|
||||||
|
|
||||||
|
- name: Cache Haskell dependencies
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.stack/*
|
||||||
|
!~/.stack/pantry
|
||||||
|
!~/.stack/programs
|
||||||
|
key: stack-${{ runner.os }}-${{ matrix.resolver }}-${{ steps.cache-date.outputs.date }}-${{ hashFiles('stack.yaml') }}-${{ hashFiles('*.cabal') }}
|
||||||
|
restore-keys: |
|
||||||
|
stack-${{ runner.os }}-${{ matrix.resolver }}-${{ steps.cache-date.outputs.date }}-${{ hashFiles('stack.yaml') }}-
|
||||||
|
stack-${{ runner.os }}-${{ matrix.resolver }}-${{ steps.cache-date.outputs.date }}-
|
||||||
|
|
||||||
|
- name: Update hackage index
|
||||||
|
# always update index to prevent the shared ~/.stack/pantry cache from being empty
|
||||||
|
run: |
|
||||||
|
set -ex
|
||||||
|
stack update
|
||||||
|
|
||||||
|
- name: Build and test
|
||||||
|
run: |
|
||||||
|
set -ex
|
||||||
|
stack test \
|
||||||
|
--fast --no-terminal \
|
||||||
|
--resolver=${{ matrix.resolver }} --system-ghc \
|
||||||
|
--flag=xmonad:pedantic
|
2
.hlint.yaml
Normal file
2
.hlint.yaml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Ignore these warnings.
|
||||||
|
- ignore: {name: "Use camelCase"}
|
38
.mailmap
Normal file
38
.mailmap
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
Adam Plaice <plaice.adam+github@gmail.com>
|
||||||
|
Brandon S Allbery KF8NH <allbery.b@gmail.com>
|
||||||
|
Brent Yorgey <byorgey@gmail.com> <byorgey@cis.upenn.edu>
|
||||||
|
Conrad Irwin <conrad.irwin@gmail.com>
|
||||||
|
Daniel Neri <daniel.neri@sigicom.com> <daniel.neri@sigicom.se>
|
||||||
|
Daniel Schoepe <daniel.schoepe@gmail.com> <asgaroth_@gmx.de>
|
||||||
|
Daniel Wagner <me@dmwit.com> <daniel@wagner-home.com>
|
||||||
|
David Glasser <glasser@mit.edu>
|
||||||
|
Deven Lahoti <deven.lahoti@gmail.com>
|
||||||
|
Devin Mullins <devin.mullins@gmail.com> <me@twifkak.com>
|
||||||
|
Don Stewart <dons00@gmail.com> <dons@cse.unsw.edu.au>
|
||||||
|
Don Stewart <dons00@gmail.com> <dons@galois.com>
|
||||||
|
Felix Springer <felixspringer149@gmail.com> <39434424+jumper149@users.noreply.github.com>
|
||||||
|
Gwern Branwen <gwern@gwern.net> <gwern0@gmail.com>
|
||||||
|
Lukas Mai <l.mai@web.de>
|
||||||
|
Marshall Lochbaum <mwlochbaum@gmail.com>
|
||||||
|
Michael G. Sloan <mgsloan@gmail.com>
|
||||||
|
Neil Mitchell <ndmitchell@gmail.com> <http://www.cs.york.ac.uk/~ndm/>
|
||||||
|
Neil Mitchell <ndmitchell@gmail.com> Neil Mitchell <unknown>
|
||||||
|
Nick Burlett <nickburlett@mac.com>
|
||||||
|
Nicolas Pouillard <nicolas.pouillard@gmail.com>
|
||||||
|
Nik Nyby <nnyby@columbia.edu>
|
||||||
|
Peter J. Jones <pjones@devalot.com>
|
||||||
|
Peter J. Jones <pjones@devalot.com> <pjones@pmade.com>
|
||||||
|
Robert Marlow <bobstopper@bobturf.org>
|
||||||
|
Robert Marlow <bobstopper@bobturf.org> <robreim@bobturf.org>
|
||||||
|
Sam Hughes <hughes@rpi.edu>
|
||||||
|
Shae Erisson <shae@ScannedInAvian.com>
|
||||||
|
Sibi Prabakaran <sibi@psibi.in>
|
||||||
|
Sibi Prabakaran <sibi@psibi.in> <psibi2000@gmail.com>
|
||||||
|
Spencer Janssen <spencerjanssen@gmail.com> <sjanssen@cse.unl.edu>
|
||||||
|
Timothy Hobbs <tim.thelion@gmail.com>
|
||||||
|
Tomas Janousek <tomi@nomi.cz>
|
||||||
|
Valery V. Vorotyntsev <valery.vv@gmail.com>
|
||||||
|
Vanessa McHale <vamchale@gmail.com> <vanessa.mchale@reconfigure.io>
|
||||||
|
Wirt Wolff <wirtwolff@gmail.com>
|
||||||
|
|
||||||
|
slotThe <soliditsallgood@mailbox.org> <50166980+slotThe@users.noreply.github.com>
|
135
.travis.yml
135
.travis.yml
@@ -1,135 +0,0 @@
|
|||||||
# 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/.cabal/packages
|
|
||||||
- $HOME/.cabal/store
|
|
||||||
|
|
||||||
before_cache:
|
|
||||||
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
|
|
||||||
# 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:
|
|
||||||
- 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:
|
|
||||||
- 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 "$(${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
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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:
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# 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
|
|
121
CHANGES.md
121
CHANGES.md
@@ -1,6 +1,125 @@
|
|||||||
# Change Log / Release Notes
|
# Change Log / Release Notes
|
||||||
|
|
||||||
## unknown (unknown)
|
## 0.17.2 (April 2, 2023)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Fixed the build with GHC 9.6.
|
||||||
|
|
||||||
|
## 0.17.1 (September 3, 2022)
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
* Added custom cursor shapes for resizing and moving windows.
|
||||||
|
|
||||||
|
* Exported `cacheNumlockMask` and `mkGrabs` from `XMonad.Operations`.
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Fixed border color of windows with alpha channel. Now all windows have the
|
||||||
|
same opaque border color.
|
||||||
|
|
||||||
|
* Change the main loop to try to avoid [GHC bug 21708] on systems
|
||||||
|
running GHC 9.2 up to version 9.2.3. The issue has been fixed in
|
||||||
|
[GHC 9.2.4] and all later releases.
|
||||||
|
|
||||||
|
[GHC bug 21708]: https://gitlab.haskell.org/ghc/ghc/-/issues/21708
|
||||||
|
[GHC 9.2.4]: https://discourse.haskell.org/t/ghc-9-2-4-released/4851
|
||||||
|
|
||||||
|
## 0.17.0 (October 27, 2021)
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
* Migrated `X.L.LayoutCombinators.(|||)` into `XMonad.Layout`, providing the
|
||||||
|
ability to directly jump to a layout with the `JumpToLayout` message.
|
||||||
|
|
||||||
|
* Recompilation now detects `stack.yaml` (can be a symlink) alongside
|
||||||
|
`xmonad.hs` and switches to using `stack ghc`. We also updated INSTALL.md
|
||||||
|
with instructions for cabal-install that lead to correct recompilation.
|
||||||
|
|
||||||
|
Deprecation warnings during recompilation are no longer suppressed to make
|
||||||
|
it easier for us to clean up the codebase. These can still be suppressed
|
||||||
|
manually using an `OPTIONS_GHC` pragma with `-Wno-deprecations`.
|
||||||
|
|
||||||
|
* Improve handling of XDG directories.
|
||||||
|
|
||||||
|
1. If all three of xmonad's environment variables (`XMONAD_DATA_DIR,`
|
||||||
|
`XMONAD_CONFIG_DIR`, and `XMONAD_CACHE_DIR`) are set, use them.
|
||||||
|
2. If there is a build script called `build` (see [these build scripts]
|
||||||
|
for usage examples) or configuration `xmonad.hs` in `~/.xmonad`, set
|
||||||
|
all three directories to `~/.xmonad`.
|
||||||
|
3. Otherwise, use the `xmonad` directory in `XDG_DATA_HOME`,
|
||||||
|
`XDG_CONFIG_HOME`, and `XDG_CACHE_HOME` (or their respective
|
||||||
|
fallbacks). These directories are created if necessary.
|
||||||
|
|
||||||
|
In the cases of 1. and 3., the build script or executable is expected to be
|
||||||
|
in the config dir.
|
||||||
|
|
||||||
|
Additionally, the xmonad config binary and intermediate object files were
|
||||||
|
moved to the cache directory (only relevant if using XDG or
|
||||||
|
`XMONAD_CACHE_DIR`).
|
||||||
|
|
||||||
|
* Added `Foldable`, `Functor`, and `Traversable` instances for `Stack`.
|
||||||
|
|
||||||
|
* Added `Typeable layout` constraint to `LayoutClass`, making it possible to
|
||||||
|
cast `Layout` back into a concrete type and extract current layout state
|
||||||
|
from it.
|
||||||
|
|
||||||
|
* Export constructor for `Choose` and `CLR` from `Module.Layout` to allow
|
||||||
|
pattern-matching on the left and right sub-layouts of `Choose l r a`.
|
||||||
|
|
||||||
|
* Added `withUnfocused` function to `XMonad.Operations`, allowing for `X`
|
||||||
|
operations to be applied to unfocused windows.
|
||||||
|
|
||||||
|
* Added `willFloat` function to `XMonad.ManageHooks` to detect whether the
|
||||||
|
(about to be) managed window will be a floating window or not
|
||||||
|
|
||||||
|
[these build scripts]: https://github.com/xmonad/xmonad-testing/tree/master/build-scripts
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Fixed a bug when using multiple screens with different dimensions, causing
|
||||||
|
some floating windows to be smaller/larger than the size they requested.
|
||||||
|
|
||||||
|
* Compatibility with GHC 9.0
|
||||||
|
|
||||||
|
* Fixed dunst notifications being obscured when moving floats.
|
||||||
|
https://github.com/xmonad/xmonad/issues/208
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
* Made `(<&&>)` and `(<||>)` non-strict in their right operand; i.e., these
|
||||||
|
operators now implement short-circuit evaluation so the right operand is
|
||||||
|
evaluated only if the left operand does not suffice to determine the
|
||||||
|
result.
|
||||||
|
|
||||||
|
* Change `ScreenDetail` to a newtype and make `RationalRect` strict in its
|
||||||
|
contents.
|
||||||
|
|
||||||
|
* Added the `extensibleConf` field to `XConfig` which makes it easier for
|
||||||
|
contrib modules to have composable configuration (custom hooks, …).
|
||||||
|
|
||||||
|
* `util/GenerateManpage.hs` is no longer distributed in the tarball.
|
||||||
|
Instead, the manpage source is regenerated and manpage rebuilt
|
||||||
|
automatically in CI.
|
||||||
|
|
||||||
|
* `DestroyWindowEvent` is now broadcasted to layouts to let them know
|
||||||
|
window-specific resources can be discarded.
|
||||||
|
|
||||||
|
## 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)
|
## 0.14.1 (August 20, 2018)
|
||||||
|
|
||||||
|
82
CONFIG
82
CONFIG
@@ -1,82 +0,0 @@
|
|||||||
== Configuring xmonad ==
|
|
||||||
|
|
||||||
xmonad is configured by creating and editing the file:
|
|
||||||
|
|
||||||
~/.xmonad/xmonad.hs
|
|
||||||
|
|
||||||
xmonad then uses settings from this file as arguments to the window manager,
|
|
||||||
on startup. For a complete example of possible settings, see the file:
|
|
||||||
|
|
||||||
man/xmonad.hs
|
|
||||||
|
|
||||||
Further examples are on the website, wiki and extension documentation.
|
|
||||||
|
|
||||||
http://haskell.org/haskellwiki/Xmonad
|
|
||||||
|
|
||||||
== A simple example ==
|
|
||||||
|
|
||||||
Here is a basic example, which overrides the default border width,
|
|
||||||
default terminal, and some colours. This text goes in the file
|
|
||||||
$HOME/.xmonad/xmonad.hs :
|
|
||||||
|
|
||||||
import XMonad
|
|
||||||
|
|
||||||
main = xmonad $ def
|
|
||||||
{ borderWidth = 2
|
|
||||||
, terminal = "urxvt"
|
|
||||||
, normalBorderColor = "#cccccc"
|
|
||||||
, focusedBorderColor = "#cd8b00" }
|
|
||||||
|
|
||||||
You can find the defaults in the file:
|
|
||||||
|
|
||||||
XMonad/Config.hs
|
|
||||||
|
|
||||||
== Checking your xmonad.hs is correct ==
|
|
||||||
|
|
||||||
Place this text in ~/.xmonad/xmonad.hs, and then check that it is
|
|
||||||
syntactically and type correct by loading it in the Haskell
|
|
||||||
interpreter:
|
|
||||||
|
|
||||||
$ ghci ~/.xmonad/xmonad.hs
|
|
||||||
GHCi, version 6.8.1: http://www.haskell.org/ghc/ :? for help
|
|
||||||
Loading package base ... linking ... done.
|
|
||||||
Ok, modules loaded: Main.
|
|
||||||
|
|
||||||
Prelude Main> :t main
|
|
||||||
main :: IO ()
|
|
||||||
|
|
||||||
Ok, looks good.
|
|
||||||
|
|
||||||
== Loading your configuration ==
|
|
||||||
|
|
||||||
To have xmonad start using your settings, type 'mod-q'. xmonad will
|
|
||||||
then load this new file, and run it. If it is unable to, the defaults
|
|
||||||
are used.
|
|
||||||
|
|
||||||
To load successfully, both 'xmonad' and 'ghc' must be in your $PATH
|
|
||||||
environment variable. If GHC isn't in your path, for some reason, you
|
|
||||||
can compile the xmonad.hs file yourself:
|
|
||||||
|
|
||||||
$ cd ~/.xmonad
|
|
||||||
$ ghc --make xmonad.hs
|
|
||||||
$ ls
|
|
||||||
xmonad xmonad.hi xmonad.hs xmonad.o
|
|
||||||
|
|
||||||
When you hit mod-q, this newly compiled xmonad will be used.
|
|
||||||
|
|
||||||
== Where are the defaults? ==
|
|
||||||
|
|
||||||
The default configuration values are defined in the source file:
|
|
||||||
|
|
||||||
XMonad/Config.hs
|
|
||||||
|
|
||||||
the XConfig data structure itself is defined in:
|
|
||||||
|
|
||||||
XMonad/Core.hs
|
|
||||||
|
|
||||||
== Extensions ==
|
|
||||||
|
|
||||||
Since the xmonad.hs file is just another Haskell module, you may import
|
|
||||||
and use any Haskell code or libraries you wish. For example, you can use
|
|
||||||
things from the xmonad-contrib library, or other code you write
|
|
||||||
yourself.
|
|
122
CONTRIBUTING.md
122
CONTRIBUTING.md
@@ -34,9 +34,15 @@ Awesome! Here are a few things to keep in mind:
|
|||||||
nontrivial changes to xmonad. There are a couple of ways you can
|
nontrivial changes to xmonad. There are a couple of ways you can
|
||||||
chat with us:
|
chat with us:
|
||||||
|
|
||||||
|
- Join the [`#xmonad` IRC channel] on `irc.libera.chat` or the
|
||||||
|
official [matrix channel], which is linked to IRC. This is the
|
||||||
|
preferred (and fastest!) way to get into contact with us.
|
||||||
|
|
||||||
- Post a message to the [mailing list][ml].
|
- Post a message to the [mailing list][ml].
|
||||||
|
|
||||||
- Join the `#xmonad` IRC channel on `chat.freenode.org`.
|
* [XMonad.Doc.Developing][xmonad-doc-developing] is a great
|
||||||
|
resource to get an overview of xmonad. Make sure to also check
|
||||||
|
it if you want more details on the coding style.
|
||||||
|
|
||||||
* Continue reading this document!
|
* Continue reading this document!
|
||||||
|
|
||||||
@@ -55,87 +61,71 @@ Here are some tips for getting your changes merged into xmonad:
|
|||||||
* Your changes should include relevant entries in the `CHANGES.md`
|
* Your changes should include relevant entries in the `CHANGES.md`
|
||||||
file. Help us communicate changes to the community.
|
file. Help us communicate changes to the community.
|
||||||
|
|
||||||
* Make sure you test your changes using the [xmonad-testing][]
|
* Make sure you test your changes against the most recent commit of
|
||||||
repository. Include a new configuration file that shows off your
|
[xmonad][] (and [xmonad-contrib][], if you're contributing there).
|
||||||
changes if possible by creating a PR on that repository as well.
|
If you're adding a new module or functionality, make sure to add an
|
||||||
|
example in the documentation and in the PR description.
|
||||||
|
|
||||||
* Make sure you read the section on rebasing and squashing commits
|
* Make sure you run the automated tests. Both [xmonad-contrib][]
|
||||||
below.
|
and [xmonad][] have test-suites that you could run with
|
||||||
|
`stack test` for example.
|
||||||
|
|
||||||
## Rebasing and Squashing Commits
|
* When committing, try to follow existing practices. For more
|
||||||
|
information on what good commit messages look like, see [How to
|
||||||
|
Write a Git Commit Message][commit-cbeams] and the [Kernel
|
||||||
|
documentation][commit-kernel] about committing logical changes
|
||||||
|
separately.
|
||||||
|
|
||||||
Under no circumstances should you ever merge the master branch into
|
## Style Guidelines
|
||||||
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
|
Below are some common style guidelines that all of the core modules
|
||||||
branch. If a core team member asks you to "rebase your changes" this
|
follow. Before submitting a pull request, make sure that your code does
|
||||||
is what they are talking about.
|
as well!
|
||||||
|
|
||||||
It's also helpful to squash all of your commits so that your pull
|
* Comment every top level function (particularly exported functions),
|
||||||
request only contains a single commit. Again, this makes it easier to
|
and provide a type signature; use Haddock syntax in the comments.
|
||||||
review your changes and identify the changes later on in the Git
|
|
||||||
history.
|
|
||||||
|
|
||||||
### How to Rebase Your Changes
|
* Follow the coding style of the module that you are making changes to
|
||||||
|
(`n` spaces for indentation, where to break long type signatures, …).
|
||||||
|
|
||||||
The goal of rebasing is to bring recent changes from the master branch
|
* New code should not introduce any new warnings. If you want to
|
||||||
into your feature branch. This often helps resolve conflicts where
|
check this yourself before submitting a pull request, there is the
|
||||||
you have changed a file that also changed in a recently merged pull
|
`pedantic` flag, which is enforced in our CI. You can enable it by
|
||||||
request (i.e. the `CHANGES.md` file). Here is how you do that.
|
building your changes with `stack build --flag xmonad:pedantic` or
|
||||||
|
`cabal build --flag pedantic`.
|
||||||
|
|
||||||
1. Make sure that you have a `git remote` configured for the main
|
* Likewise, your code should be free of [hlint] warnings; this is also
|
||||||
repository. I like to call this remote `upstream`:
|
enforced in our GitHub CI.
|
||||||
|
|
||||||
$ git remote add upstream https://github.com/xmonad/xmonad-contrib.git
|
* Partial functions are to be avoided: the window manager should not
|
||||||
|
crash, so do not call `error` or `undefined`.
|
||||||
|
|
||||||
2. Pull from upstream and rewrite your changes on top of master. For
|
* Any pure function added to the core should have QuickCheck
|
||||||
this to work you should not have any modified files in your
|
properties precisely defining its behavior.
|
||||||
working directory. Run these commands from within your feature
|
|
||||||
branch (the branch you are asking to be merged):
|
|
||||||
|
|
||||||
$ git fetch --all
|
* New modules should identify the author, and be submitted under the
|
||||||
$ git pull --rebase upstream master
|
same license as xmonad (BSD3 license).
|
||||||
|
|
||||||
3. If the rebase was successful you can now push your feature branch
|
## Keep rocking!
|
||||||
back to GitHub. You need to force the push since your commits
|
|
||||||
have been rewritten and have new IDs:
|
|
||||||
|
|
||||||
$ git push --force-with-lease
|
xmonad is a passion project created and maintained by the community.
|
||||||
|
We'd love for you to maintain your own contributed modules (approve
|
||||||
4. Your pull request should now be conflict-free and only contain the
|
changes from other contributors, review code, etc.). However, before
|
||||||
changes that you actually made.
|
we'd be comfortable adding you to the [xmonad GitHub
|
||||||
|
organization][xmonad-gh-org] we need to trust that you have sufficient
|
||||||
### How to Squash Commits
|
knowledge of Haskell and git; and have a way of chatting with you ([IRC,
|
||||||
|
Matrix, etc.][community]).
|
||||||
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
|
|
||||||
|
|
||||||
|
[hlint]: https://github.com/ndmitchell/hlint
|
||||||
[xmonad]: https://github.com/xmonad/xmonad
|
[xmonad]: https://github.com/xmonad/xmonad
|
||||||
[xmonad-contrib]: https://github.com/xmonad/xmonad-contrib
|
[xmonad-contrib]: https://github.com/xmonad/xmonad-contrib
|
||||||
[xmonad-testing]: https://github.com/xmonad/xmonad-testing
|
[xmonad-testing]: https://github.com/xmonad/xmonad-testing
|
||||||
[x11]: https://github.com/xmonad/X11
|
[x11]: https://github.com/xmonad/X11
|
||||||
[ml]: https://mail.haskell.org/cgi-bin/mailman/listinfo/xmonad
|
[ml]: https://mail.haskell.org/cgi-bin/mailman/listinfo/xmonad
|
||||||
|
[xmonad-doc-developing]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Doc-Developing.html
|
||||||
|
[`#xmonad` IRC channel]: https://web.libera.chat/#xmonad
|
||||||
|
[matrix channel]: https://matrix.to/#/#xmonad:matrix.org
|
||||||
|
[commit-cbeams]: https://cbea.ms/git-commit/
|
||||||
|
[commit-kernel]: https://www.kernel.org/doc/html/v4.10/process/submitting-patches.html#separate-your-changes
|
||||||
|
[community]: https://xmonad.org/community.html
|
||||||
|
[xmonad-gh-org]: https://github.com/xmonad
|
||||||
|
399
INSTALL.md
Normal file
399
INSTALL.md
Normal file
@@ -0,0 +1,399 @@
|
|||||||
|
# Install XMonad
|
||||||
|
|
||||||
|
On many systems xmonad is available as a binary package in your
|
||||||
|
distribution (Debian, Ubuntu, Fedora, Arch, Gentoo, …).
|
||||||
|
It's by far the easiest way to get xmonad, although you'll miss out on the
|
||||||
|
latest features and fixes that may not have been released yet.
|
||||||
|
|
||||||
|
If you do want the latest and greatest, continue reading.
|
||||||
|
Those who install from distro can skip this and go straight to
|
||||||
|
[the XMonad Configuration Tutorial](TUTORIAL.md).
|
||||||
|
|
||||||
|
<!-- https://github.com/frnmst/md-toc -->
|
||||||
|
<!-- regenerate via: md_toc -s1 -p github INSTALL.md -->
|
||||||
|
<!--TOC-->
|
||||||
|
|
||||||
|
- [Dependencies](#dependencies)
|
||||||
|
- [Preparation](#preparation)
|
||||||
|
- [Download XMonad sources](#download-xmonad-sources)
|
||||||
|
- [Build XMonad](#build-xmonad)
|
||||||
|
- [Build using Stack](#build-using-stack)
|
||||||
|
- [Build using cabal-install](#build-using-cabal-install)
|
||||||
|
- [Make XMonad your window manager](#make-xmonad-your-window-manager)
|
||||||
|
- [Custom Build Script](#custom-build-script)
|
||||||
|
|
||||||
|
<!--TOC-->
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
#### Debian, Ubuntu
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ sudo apt install \
|
||||||
|
> git \
|
||||||
|
> libx11-dev libxft-dev libxinerama-dev libxrandr-dev libxss-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Fedora
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ sudo dnf install \
|
||||||
|
> git \
|
||||||
|
> libX11-devel libXft-devel libXinerama-devel libXrandr-devel libXScrnSaver-devel
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Arch
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ sudo pacman -S \
|
||||||
|
> git \
|
||||||
|
> xorg-server xorg-apps xorg-xinit xorg-xmessage \
|
||||||
|
> libx11 libxft libxinerama libxrandr libxss \
|
||||||
|
> pkgconf
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Void
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ sudo xbps-install \
|
||||||
|
> git \
|
||||||
|
> ncurses-libtinfo-libs ncurses-libtinfo-devel \
|
||||||
|
> libX11-devel libXft-devel libXinerama-devel libXrandr-devel libXScrnSaver-devel \
|
||||||
|
> pkg-config
|
||||||
|
```
|
||||||
|
|
||||||
|
## Preparation
|
||||||
|
|
||||||
|
We'll use the [XDG] directory specifications here, meaning our
|
||||||
|
configuration will reside within `$XDG_CONFIG_HOME`, which is
|
||||||
|
`~/.config` on most systems. Let's create this directory and move to
|
||||||
|
it:
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ mkdir -p ~/.config/xmonad && cd ~/.config/xmonad
|
||||||
|
```
|
||||||
|
|
||||||
|
If you already have an `xmonad.hs` configuration, you can copy it over
|
||||||
|
now. If not, you can use the defaults: create a file called `xmonad.hs`
|
||||||
|
with the following content:
|
||||||
|
|
||||||
|
``` haskell
|
||||||
|
import XMonad
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = xmonad def
|
||||||
|
```
|
||||||
|
|
||||||
|
Older versions of xmonad used `~/.xmonad` instead.
|
||||||
|
This is still supported, but XDG is preferred.
|
||||||
|
|
||||||
|
## Download XMonad sources
|
||||||
|
|
||||||
|
Still in `~/.config/xmonad`, clone `xmonad` and `xmonad-contrib` repositories
|
||||||
|
using [git][]:
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ git clone https://github.com/xmonad/xmonad
|
||||||
|
$ git clone https://github.com/xmonad/xmonad-contrib
|
||||||
|
```
|
||||||
|
|
||||||
|
This will give you the latest `HEAD`; if you want you can also check
|
||||||
|
out a tagged release, e.g.:
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ git clone --branch v0.17.2 https://github.com/xmonad/xmonad
|
||||||
|
$ git clone --branch v0.17.1 https://github.com/xmonad/xmonad-contrib
|
||||||
|
```
|
||||||
|
|
||||||
|
(Sources and binaries don't usually go into `~/.config`. In our case,
|
||||||
|
however, it avoids complexities related to Haskell build tools and lets us
|
||||||
|
focus on the important bits of XMonad installation.)
|
||||||
|
|
||||||
|
## Build XMonad
|
||||||
|
|
||||||
|
There are two widely used Haskell build tools:
|
||||||
|
|
||||||
|
* [Stack][stack]
|
||||||
|
* [cabal-install][cabal-install]
|
||||||
|
|
||||||
|
We include instructions for both.
|
||||||
|
Unless you already know which one you prefer, use Stack, which is easier.
|
||||||
|
|
||||||
|
### Build using Stack
|
||||||
|
|
||||||
|
#### Install Stack
|
||||||
|
|
||||||
|
The easiest way to get [stack] is probably via your system's package
|
||||||
|
manager:
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ sudo apt install haskell-stack # Debian, Ubuntu
|
||||||
|
$ sudo dnf install stack # Fedora
|
||||||
|
$ sudo pacman -S stack # Arch
|
||||||
|
```
|
||||||
|
|
||||||
|
If you install stack via this method, it is advisable that you run
|
||||||
|
`stack upgrade` after installation. This will make sure that you are on
|
||||||
|
the most recent version of the program, regardless of which version your
|
||||||
|
distribution actually packages.
|
||||||
|
|
||||||
|
If your distribution does not package stack, you can also easily install
|
||||||
|
it via the following command (this is the recommended way to install
|
||||||
|
stack via its [documentation][stack]):
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ curl -sSL https://get.haskellstack.org/ | sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Yet another way would be via [ghcup]; this is similar to installers like
|
||||||
|
`rustup`, in case you prefer that.
|
||||||
|
|
||||||
|
#### Create a New Project
|
||||||
|
|
||||||
|
Let's create a stack project. Since we're already in the correct
|
||||||
|
directory (`~/.config/xmonad`) with `xmonad` and `xmonad-contrib`
|
||||||
|
subdirectories, starting a new stack project is as simple as running `stack
|
||||||
|
init`.
|
||||||
|
|
||||||
|
Stack should now inform you that it will use the relevant `stack` and
|
||||||
|
`cabal` files from `xmonad` and `xmonad-contrib` to generate its
|
||||||
|
`stack.yaml` file. At the time of writing, this looks a little bit like
|
||||||
|
this:
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ stack init
|
||||||
|
Looking for .cabal or package.yaml files to use to init the project.
|
||||||
|
Using cabal packages:
|
||||||
|
- xmonad-contrib/
|
||||||
|
- xmonad/
|
||||||
|
|
||||||
|
Selecting the best among 19 snapshots...
|
||||||
|
|
||||||
|
* Matches https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/17/9.yaml
|
||||||
|
|
||||||
|
Selected resolver: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/17/9.yaml
|
||||||
|
Initialising configuration using resolver: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/17/9.yaml
|
||||||
|
Total number of user packages considered: 2
|
||||||
|
Writing configuration to file: stack.yaml
|
||||||
|
All done.
|
||||||
|
```
|
||||||
|
|
||||||
|
If you look into your current directory now, you should see a freshly
|
||||||
|
generated `stack.yaml` file:
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ ls
|
||||||
|
xmonad xmonad-contrib stack.yaml xmonad.hs
|
||||||
|
```
|
||||||
|
|
||||||
|
The meat of that file (comments start with `#`, we've omitted them here)
|
||||||
|
will look a little bit like
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
resolver:
|
||||||
|
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/17/9.yaml
|
||||||
|
|
||||||
|
packages:
|
||||||
|
- xmonad
|
||||||
|
- xmonad-contrib
|
||||||
|
```
|
||||||
|
|
||||||
|
With `stack.yaml` alongside `xmonad.hs`, xmonad now knows that it needs to use
|
||||||
|
`stack ghc` instead of just `ghc` when (re)compiling its configuration.
|
||||||
|
If you want to keep xmonad sources and the stack project elsewhere, but still
|
||||||
|
use `xmonad --recompile`, symlink your real `stack.yaml` into the xmonad
|
||||||
|
configuration directory, or [use a custom build script](#custom-build-script).
|
||||||
|
|
||||||
|
#### Install Everything
|
||||||
|
|
||||||
|
Installing things is as easy as typing `stack install`. This will
|
||||||
|
install the correct version of GHC, as well as build all of the required
|
||||||
|
packages (`stack build`) and then copy the relevant executables
|
||||||
|
(`xmonad`, in our case) to `~/.local/bin`. Make sure to add that
|
||||||
|
directory to your `$PATH`! The command `which xmonad` should now return
|
||||||
|
that executable. In case it does not, check if you still have xmonad
|
||||||
|
installed via your package manager and uninstall it.
|
||||||
|
|
||||||
|
If you're getting build failures while building the `X11` package it may
|
||||||
|
be that you don't have the required C libraries installed. See
|
||||||
|
[above](#dependencies).
|
||||||
|
|
||||||
|
### Build using cabal-install
|
||||||
|
|
||||||
|
#### Install cabal-install
|
||||||
|
|
||||||
|
The easiest way to get [cabal-install] is probably via your system's package
|
||||||
|
manager:
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ sudo apt install cabal-install # Debian, Ubuntu
|
||||||
|
$ sudo dnf install cabal-install # Fedora
|
||||||
|
$ sudo pacman -S cabal-install # Arch
|
||||||
|
```
|
||||||
|
|
||||||
|
If your distribution does not package cabal-install, [ghcup][] is another
|
||||||
|
option. See also <https://www.haskell.org/cabal/#install-upgrade>.
|
||||||
|
|
||||||
|
#### Create a New Project
|
||||||
|
|
||||||
|
Let's create a cabal project. Since we're already in the correct
|
||||||
|
directory (`~/.config/xmonad`) with `xmonad` and `xmonad-contrib`
|
||||||
|
subdirectories, we'll instruct cabal to use them. Create a file named
|
||||||
|
`cabal.project` containing:
|
||||||
|
|
||||||
|
```
|
||||||
|
packages: */*.cabal
|
||||||
|
```
|
||||||
|
|
||||||
|
(If you skip this step, cabal will use the latest releases from [Hackage][]
|
||||||
|
instead.)
|
||||||
|
|
||||||
|
#### Install Everything
|
||||||
|
|
||||||
|
You'll need to update the cabal package index, build xmonad and xmonad-contrib
|
||||||
|
libraries and then build the xmonad binary:
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ cabal update
|
||||||
|
$ cabal install --package-env=$HOME/.config/xmonad --lib xmonad xmonad-contrib
|
||||||
|
$ cabal install --package-env=$HOME/.config/xmonad xmonad
|
||||||
|
```
|
||||||
|
|
||||||
|
This will create a GHC environment in `~/.config/xmonad` so that the libraries
|
||||||
|
are available for recompilation of the config file, and also install the
|
||||||
|
xmonad binary to `~/.cabal/bin/xmonad`. Make sure you have that directory in
|
||||||
|
your `$PATH`!
|
||||||
|
|
||||||
|
If you're getting build failures while building the `X11` package it may
|
||||||
|
be that you don't have the required C libraries installed. See
|
||||||
|
[above](#dependencies).
|
||||||
|
|
||||||
|
## Make XMonad your window manager
|
||||||
|
|
||||||
|
This step varies depending on your distribution and X display manager (if
|
||||||
|
any).
|
||||||
|
|
||||||
|
#### Debian, Ubuntu
|
||||||
|
|
||||||
|
`/etc/X11/xinit/xinitrc` runs `/etc/X11/Xsession` which runs `~/.xsession`, so
|
||||||
|
you probably want to put `exec xmonad` there (don't forget the shebang and chmod).
|
||||||
|
|
||||||
|
(Tested with `startx`, `xdm`, `lightdm`.)
|
||||||
|
|
||||||
|
By using `~/.xsession`, the distro takes care of stuff like dbus, ssh-agent, X
|
||||||
|
resources, etc. If you want a completely manual X session, use `~/.xinitrc`
|
||||||
|
instead. Or invoke `startx`/`xinit` with an explicit path.
|
||||||
|
|
||||||
|
Some newer display managers require an entry in `/usr/share/xsessions`.
|
||||||
|
To use your custom `~/.xsession`, put these lines to
|
||||||
|
`/usr/share/xsessions/default.desktop`:
|
||||||
|
|
||||||
|
```
|
||||||
|
[Desktop Entry]
|
||||||
|
Name=Default X session
|
||||||
|
Type=Application
|
||||||
|
Exec=default
|
||||||
|
```
|
||||||
|
|
||||||
|
(Tested with `sddm`.)
|
||||||
|
|
||||||
|
#### Fedora
|
||||||
|
|
||||||
|
`/etc/X11/xinit/xinitrc` runs `~/.Xclients`, so you probably want to put `exec
|
||||||
|
xmonad` there (don't forget the shebang and chmod). Like in Debian, this can
|
||||||
|
be overridden by having a completely custom `~/.xinitrc` or passing arguments
|
||||||
|
to `startx`/`xinit`.
|
||||||
|
|
||||||
|
X display managers (e.g. `lightdm`) usually invoke `/etc/X11/xinit/Xsession`
|
||||||
|
instead, which additionally redirects output to `~/.xsession-errors` and also
|
||||||
|
tries `~/.xsession` before `~/.Xclients`.
|
||||||
|
|
||||||
|
Newer display managers require an entry in `/usr/share/xsessions`, which is
|
||||||
|
available in the `xorg-x11-xinit-session` package.
|
||||||
|
|
||||||
|
#### Arch
|
||||||
|
|
||||||
|
`/etc/X11/xinit/xinitrc` runs `twm`, `xclock` and 3 `xterm`s; users are
|
||||||
|
meant to just copy that to `~/.xinitrc` and
|
||||||
|
[customize](https://wiki.archlinux.org/title/Xinit#xinitrc) it: replace the
|
||||||
|
last few lines with `exec xmonad`.
|
||||||
|
|
||||||
|
Display managers like `lightdm` have their own `Xsession` script which invokes
|
||||||
|
`~/.xsession`. Other display managers need an entry in
|
||||||
|
`/usr/share/xsessions`, <https://aur.archlinux.org/packages/xinit-xsession/>
|
||||||
|
provides one.
|
||||||
|
|
||||||
|
#### See also
|
||||||
|
|
||||||
|
* <https://xmonad.org/documentation.html#in-your-environment>
|
||||||
|
* [FAQ: How can I use xmonad with a display manager? (xdm, kdm, gdm)](https://wiki.haskell.org/Xmonad/Frequently_asked_questions#How_can_I_use_xmonad_with_a_display_manager.3F_.28xdm.2C_kdm.2C_gdm.29)
|
||||||
|
|
||||||
|
## Custom Build Script
|
||||||
|
|
||||||
|
If you need to customize what happens during `xmonad --recompile` (bound to
|
||||||
|
`M-q` by default), perhaps because your xmonad configuration is a whole
|
||||||
|
separate Haskell package, you need to create a so-called `build` file. This
|
||||||
|
is quite literally just a shell script called `build` in your xmonad directory
|
||||||
|
(which is `~/.config/xmonad` for us) that tells xmonad how it should build its
|
||||||
|
executable.
|
||||||
|
|
||||||
|
A good starting point (this is essentially [what xmonad would do][]
|
||||||
|
without a build file, with the exception that we are invoking `stack
|
||||||
|
ghc` instead of plain `ghc`) would be
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
exec stack ghc -- \
|
||||||
|
--make xmonad.hs \
|
||||||
|
-i \
|
||||||
|
-ilib \
|
||||||
|
-fforce-recomp \
|
||||||
|
-main-is main \
|
||||||
|
-v0 \
|
||||||
|
-o "$1"
|
||||||
|
```
|
||||||
|
|
||||||
|
Don't forget to mark the file as `+x`: `chmod +x build`!
|
||||||
|
|
||||||
|
Some example build scripts for `stack` and `cabal` are provided in the
|
||||||
|
`xmonad-contrib` distribution. You can see those online in the
|
||||||
|
[scripts/build][] directory. You might wish to use these if you have
|
||||||
|
special dependencies for your `xmonad.hs`, especially with cabal as
|
||||||
|
you must use a cabal file and often a `cabal.project` to specify them;
|
||||||
|
`cabal install --lib` above generally isn't enough, and when it is
|
||||||
|
it can be difficult to keep track of when you want to replicate your
|
||||||
|
configuration on another system.
|
||||||
|
|
||||||
|
#### Don't Recompile on Every Startup
|
||||||
|
|
||||||
|
By default, xmonad always recompiles itself when a build script is used
|
||||||
|
(because the build script could contain arbitrary code, so a simple
|
||||||
|
check whether the `xmonad.hs` file changed is not enough). If you find
|
||||||
|
that too annoying, then you can use the `xmonad-ARCH` executable that
|
||||||
|
`xmonad --recompile` generates instead of `xmonad` in your startup. For
|
||||||
|
example, instead of writing
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
exec xmonad
|
||||||
|
```
|
||||||
|
|
||||||
|
in your `~/.xinitrc`, you would write
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
exec $HOME/.cache/xmonad/xmonad-x86_64-linux
|
||||||
|
```
|
||||||
|
|
||||||
|
The `~/.cache` prefix is the `$XDG_CACHE_HOME` directory. Note that
|
||||||
|
if your xmonad configuration resides within `~/.xmonad`, then the
|
||||||
|
executable will also be within that directory and not in
|
||||||
|
`$XDG_CACHE_HOME`.
|
||||||
|
|
||||||
|
[XDG]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||||
|
[git]: https://git-scm.com/
|
||||||
|
[stack]: https://docs.haskellstack.org/en/stable/README/
|
||||||
|
[cabal-install]: https://www.haskell.org/cabal/
|
||||||
|
[ghcup]: https://www.haskell.org/ghcup/
|
||||||
|
[what xmonad would do]: https://github.com/xmonad/xmonad/blob/master/src/XMonad/Core.hs#L659-L667
|
||||||
|
[Hackage]: https://hackage.haskell.org/
|
||||||
|
[scripts/build]: https://github.com/xmonad/xmonad-contrib/blob/master/scripts/build
|
45
LICENSE
45
LICENSE
@@ -1,31 +1,28 @@
|
|||||||
Copyright (c) 2007,2008 Spencer Janssen
|
Copyright (c) 2007,2008 Spencer Janssen
|
||||||
Copyright (c) 2007,2008 Don Stewart
|
Copyright (c) 2007,2008 Don Stewart
|
||||||
|
Copyright (c) The Xmonad Community. All rights reserved.
|
||||||
|
|
||||||
All rights reserved.
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
modification, are permitted provided that the following conditions
|
this list of conditions and the following disclaimer.
|
||||||
are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
notice, this list of conditions and the following disclaimer.
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
notice, this list of conditions and the following disclaimer in the
|
may be used to endorse or promote products derived from this software without
|
||||||
documentation and/or other materials provided with the distribution.
|
specific prior written permission.
|
||||||
|
|
||||||
3. Neither the name of the author nor the names of his contributors
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
may be used to endorse or promote products derived from this software
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
without specific prior written permission.
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
||||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
||||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
156
MAINTAINERS.md
156
MAINTAINERS.md
@@ -2,82 +2,140 @@
|
|||||||
|
|
||||||
## The XMonad Core Team
|
## The XMonad Core Team
|
||||||
|
|
||||||
* Adam Vogt [GitHub][aavogt]
|
* Brandon S Allbery [GitHub][geekosaur], IRC: `geekosaur`, [GPG][gpg:geekosaur]
|
||||||
|
|
||||||
* Brandon S Allbery [GitHub][geekosaur], IRC: `geekosaur`
|
|
||||||
|
|
||||||
* Brent Yorgey [GitHub][byorgey], IRC: `byorgey`
|
* Brent Yorgey [GitHub][byorgey], IRC: `byorgey`
|
||||||
|
|
||||||
* Daniel Wagner [GitHub][dmwit], IRC: `dmwit`
|
* Daniel Wagner [GitHub][dmwit], [Twitter][twitter:dmwit], IRC: `dmwit`
|
||||||
|
|
||||||
* David Lazar [GitHub][davidlazar]
|
* Sibi Prabakaran [GitHub][psibi], [Twitter][twitter:psibi], IRC: `sibi`
|
||||||
|
|
||||||
* Devin Mullins [GitHub][twifkak]
|
* Tomáš Janoušek [GitHub][liskin], [Twitter][twitter:liskin], IRC: `liskin`, [GPG][gpg:liskin]
|
||||||
|
|
||||||
* Peter J. Jones [GitHub][pjones], [Twitter][twitter:pjones], [OpenPGP Key][pgp:pjones], IRC: `pmade`
|
* Tony Zorman [GitHub][slotThe], IRC: `Solid`, [GPG][gpg:slotThe]
|
||||||
|
|
||||||
|
[geekosaur]: https://github.com/geekosaur
|
||||||
|
[byorgey]: https://github.com/byorgey
|
||||||
|
[dmwit]: https://github.com/dmwit
|
||||||
|
[psibi]: https://github.com/psibi
|
||||||
|
[liskin]: https://github.com/liskin
|
||||||
|
[slotThe]: https://github.com/slotThe
|
||||||
|
|
||||||
|
[gpg:geekosaur]: https://github.com/geekosaur.gpg
|
||||||
|
[gpg:liskin]: https://github.com/liskin.gpg
|
||||||
|
[gpg:slotThe]: https://github.com/slotThe.gpg
|
||||||
|
|
||||||
|
[twitter:dmwit]: https://twitter.com/dmwit13
|
||||||
|
[twitter:psibi]: https://twitter.com/psibi
|
||||||
|
[twitter:liskin]: https://twitter.com/Liskni_si
|
||||||
|
|
||||||
|
## Hall of Fame (past maintainers/developers)
|
||||||
|
|
||||||
|
* Adam Vogt [GitHub](https://github.com/aavogt)
|
||||||
|
|
||||||
|
* Peter Simons [GitHub](https://github.com/peti), [Twitter](https://twitter.com/OriginalPeti)
|
||||||
|
|
||||||
|
* Spencer Janssen [GitHub](https://github.com/spencerjanssen)
|
||||||
|
|
||||||
|
* Don Stewart [GitHub](https://github.com/donsbot), [Twitter](https://twitter.com/donsbot)
|
||||||
|
|
||||||
|
* Jason Creighton [GitHub](https://github.com/JasonCreighton)
|
||||||
|
|
||||||
|
* David Roundy [GitHub](https://github.com/droundy)
|
||||||
|
|
||||||
|
* Daniel Schoepe [GitHub](https://github.com/dschoepe)
|
||||||
|
|
||||||
|
* Eric Mertens [GitHub](https://github.com/glguy)
|
||||||
|
|
||||||
|
* Nicolas Pouillard [GitHub](https://github.com/np)
|
||||||
|
|
||||||
|
* Roman Cheplyaka [GitHub](https://github.com/UnkindPartition)
|
||||||
|
|
||||||
|
* Gwern Branwen [GitHub](https://github.com/gwern)
|
||||||
|
|
||||||
|
* Lukas Mai [GitHub](https://github.com/mauke)
|
||||||
|
|
||||||
|
* Braden Shepherdson [GitHub](https://github.com/shepheb)
|
||||||
|
|
||||||
|
* Devin Mullins [GitHub](https://github.com/twifkak)
|
||||||
|
|
||||||
|
* David Lazar [GitHub](https://github.com/davidlazar)
|
||||||
|
|
||||||
|
* Peter J. Jones [GitHub](https://github.com/pjones)
|
||||||
|
|
||||||
## Release Procedures
|
## Release Procedures
|
||||||
|
|
||||||
When the time comes to release another version of XMonad and Contrib...
|
When the time comes to release another version of xmonad and xmonad-contrib:
|
||||||
|
|
||||||
1. Create a release branch (e.g., `release-0.XX`).
|
1. Update the version number in all the `*.cabal` files and let the CI
|
||||||
|
verify that it all builds together.
|
||||||
|
|
||||||
This will allow you to separate the release process from main
|
2. Review documentation files and make sure they are accurate:
|
||||||
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
|
- [`README.md`](README.md)
|
||||||
dependencies and documentation. This includes the `tested-with:`
|
- [`CHANGES.md`](CHANGES.md) (bump version, set date)
|
||||||
field.
|
- [`INSTALL.md`](INSTALL.md)
|
||||||
|
- [`man/xmonad.1.markdown.in`](man/xmonad.1.markdown.in)
|
||||||
|
- [haddocks](https://xmonad.github.io/xmonad-docs/)
|
||||||
|
|
||||||
3. Use the [packdeps][] tool to ensure you have the dependency
|
If the manpage changes, wait for the CI to rebuild the rendered outputs.
|
||||||
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:
|
3. Update the website:
|
||||||
|
|
||||||
- `README.md`
|
- Draft a [new release announcement][web-announce].
|
||||||
- `CHANGES.md`
|
- Check install instructions, guided tour, keybindings cheat sheet, …
|
||||||
- and the `example-config.hs` in the `xmonad-testing` repo
|
|
||||||
|
|
||||||
5. Generate the manpage:
|
4. Make sure that `tested-with:` covers several recent releases of GHC, that
|
||||||
|
`.github/workflows/haskell-ci.yml` had been updated to test all these GHC
|
||||||
|
versions and that `.github/workflows/stack.yml` tests with several recent
|
||||||
|
revisions of [Stackage][] LTS.
|
||||||
|
|
||||||
* `cabal configure` with the `-fgeneratemanpage` flag
|
5. Trigger the Haskell-CI workflow and fill in the candidate version number.
|
||||||
* Build the project
|
This will upload a release candidate to Hackage.
|
||||||
* 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`)
|
- https://github.com/xmonad/xmonad/actions/workflows/haskell-ci.yml
|
||||||
|
- https://github.com/xmonad/xmonad-contrib/actions/workflows/haskell-ci.yml
|
||||||
|
|
||||||
7. Build the project tarballs (`cabal sdist`)
|
Check that everything looks good. If not, push fixes and do another
|
||||||
|
candidate. When everything's ready, create a release on GitHub:
|
||||||
|
|
||||||
8. Upload the packages to Hackage (`cabal upload`)
|
- https://github.com/xmonad/xmonad/releases/new
|
||||||
|
- https://github.com/xmonad/xmonad-contrib/releases/new
|
||||||
|
|
||||||
9. Merge the release branches into `master`
|
CI will automatically upload the final release to Hackage.
|
||||||
|
|
||||||
10. Update the website:
|
See [haskell-ci-hackage.patch][] for details about the Hackage automation.
|
||||||
|
|
||||||
* Generate and push haddocks with `xmonad-web/gen-docs.sh`
|
6. Post announcement to:
|
||||||
|
|
||||||
* Check that `tour.html` and `intro.html` are up to date, and
|
- [xmonad.org website](https://github.com/xmonad/xmonad-web/tree/gh-pages/news/_posts)
|
||||||
mention all core bindings
|
- [XMonad mailing list](https://mail.haskell.org/mailman/listinfo/xmonad)
|
||||||
|
- [Haskell Cafe](https://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe)
|
||||||
|
- [Haskell Discourse](https://discourse.haskell.org/)
|
||||||
|
- [Twitter](https://twitter.com/xmonad)
|
||||||
|
- [Reddit](https://www.reddit.com/r/xmonad/)
|
||||||
|
|
||||||
11. Update the topic for the IRC channel (`#xmonad`)
|
See [old announcements][old-announce] ([even older][older-announce]) for inspiration.
|
||||||
|
|
||||||
12. Send the `announce-0.XX.txt` file to:
|
7. Bump version for development (add `.9`) and prepare fresh sections in
|
||||||
|
[`CHANGES.md`](CHANGES.md).
|
||||||
|
|
||||||
- XMonad mailing list
|
[packdeps]: https://hackage.haskell.org/package/packdeps
|
||||||
- Haskell Cafe
|
[Stackage]: https://www.stackage.org/
|
||||||
|
[haskell-ci-hackage.patch]: .github/workflows/haskell-ci-hackage.patch
|
||||||
|
[web-announce]: https://github.com/xmonad/xmonad-web/tree/gh-pages/news/_posts
|
||||||
|
[old-announce]: https://github.com/xmonad/xmonad-web/blob/gh-pages/news/_posts/2021-10-27-xmonad-0-17-0.md
|
||||||
|
[older-announce]: https://github.com/xmonad/xmonad-web/tree/55614349421ebafaef4a47424fcb16efa80ff768
|
||||||
|
|
||||||
[packdeps]: http://hackage.haskell.org/package/packdeps
|
## Website and Other Accounts
|
||||||
|
|
||||||
[aavogt]: https://github.com/orgs/xmonad/people/aavogt
|
* The [xmonad twitter] is tended to by [liskin].
|
||||||
[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
|
* The [xmonad.org] domain is owned by [eyenx] and the website itself is
|
||||||
[twitter:pjones]: https://twitter.com/contextualdev
|
deployed via GitHub Pages. It can be updated by making a pull request
|
||||||
[pgp:pjones]: http://pgp.mit.edu/pks/lookup?op=get&search=0x526722D1204284CB
|
against the [xmonad-web] repository.
|
||||||
|
|
||||||
|
[eyenx]: https://github.com/eyenx
|
||||||
|
[xmonad-web]: https://github.com/xmonad/xmonad-web/
|
||||||
|
[xmonad.org]: https://xmonad.org/
|
||||||
|
[xmonad twitter]: https://twitter.com/xmonad
|
||||||
|
187
README.md
187
README.md
@@ -1,8 +1,27 @@
|
|||||||
# xmonad: A Tiling Window Manager
|
<p align="center">
|
||||||
|
<a href="https://xmonad.org/"><img alt="XMonad logo" src="https://xmonad.org/images/logo-wrapped.svg" height=150></a>
|
||||||
|
</p>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://hackage.haskell.org/package/xmonad"><img alt="Hackage" src="https://img.shields.io/hackage/v/xmonad?logo=haskell"></a>
|
||||||
|
<a href="https://github.com/xmonad/xmonad/blob/readme/LICENSE"><img alt="License" src="https://img.shields.io/github/license/xmonad/xmonad"></a>
|
||||||
|
<a href="https://haskell.org/"><img alt="Made in Haskell" src="https://img.shields.io/badge/Made%20in-Haskell-%235e5086?logo=haskell"></a>
|
||||||
|
<br>
|
||||||
|
<a href="https://github.com/xmonad/xmonad/actions/workflows/stack.yml"><img alt="Stack" src="https://img.shields.io/github/workflow/status/xmonad/xmonad/Stack?label=Stack&logo=githubactions&logoColor=white"></a>
|
||||||
|
<a href="https://github.com/xmonad/xmonad/actions/workflows/haskell-ci.yml"><img alt="Cabal" src="https://img.shields.io/github/workflow/status/xmonad/xmonad/Haskell-CI?label=Cabal&logo=githubactions&logoColor=white"></a>
|
||||||
|
<a href="https://github.com/xmonad/xmonad/actions/workflows/nix.yml"><img alt="Nix" src="https://img.shields.io/github/workflow/status/xmonad/xmonad/Nix?label=Nix&logo=githubactions&logoColor=white"></a>
|
||||||
|
<br>
|
||||||
|
<a href="https://github.com/sponsors/xmonad"><img alt="GitHub Sponsors" src="https://img.shields.io/github/sponsors/xmonad?label=GitHub%20Sponsors&logo=githubsponsors"></a>
|
||||||
|
<a href="https://opencollective.com/xmonad"><img alt="Open Collective" src="https://img.shields.io/opencollective/all/xmonad?label=Open%20Collective&logo=opencollective"></a>
|
||||||
|
<br>
|
||||||
|
<a href="https://web.libera.chat/#xmonad"><img alt="Chat on #xmonad@irc.libera.chat" src="https://img.shields.io/badge/%23%20chat-on%20libera-brightgreen"></a>
|
||||||
|
<a href="https://matrix.to/#/#xmonad:matrix.org"><img alt="Chat on #xmonad:matrix.org" src="https://img.shields.io/matrix/xmonad:matrix.org?logo=matrix"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
[](https://travis-ci.org/xmonad/xmonad)
|
# xmonad
|
||||||
|
|
||||||
[xmonad][] is a tiling window manager for X. Windows are arranged
|
**A tiling window manager for X11.**
|
||||||
|
|
||||||
|
[XMonad][web:xmonad] is a tiling window manager for X11. Windows are arranged
|
||||||
automatically to tile the screen without gaps or overlap, maximising
|
automatically to tile the screen without gaps or overlap, maximising
|
||||||
screen use. Window manager features are accessible from the keyboard:
|
screen use. Window manager features are accessible from the keyboard:
|
||||||
a mouse is optional. xmonad is written, configured and extensible in
|
a mouse is optional. xmonad is written, configured and extensible in
|
||||||
@@ -12,118 +31,74 @@ dynamically, and different layouts may be used on each
|
|||||||
workspace. Xinerama is fully supported, allowing windows to be tiled
|
workspace. Xinerama is fully supported, allowing windows to be tiled
|
||||||
on several physical screens.
|
on several physical screens.
|
||||||
|
|
||||||
## Quick Start
|
This repository contains the [xmonad][hackage:xmonad] package, a minimal,
|
||||||
|
stable, yet extensible core. It is accompanied by
|
||||||
|
[xmonad-contrib][gh:xmonad-contrib], a library of hundreds of additional
|
||||||
|
community-maintained tiling algorithms and extension modules. The two combined
|
||||||
|
make for a powerful X11 window-manager with endless customization
|
||||||
|
possibilities. They are, quite literally, libraries for creating your own
|
||||||
|
window manager.
|
||||||
|
|
||||||
* From hackage:
|
## Installation
|
||||||
|
|
||||||
cabal update
|
For installation and configuration instructions, please see:
|
||||||
cabal install xmonad xmonad-contrib
|
|
||||||
|
|
||||||
* Alternatively, build from source using the following repositories:
|
* [downloading and installing xmonad][web:download]
|
||||||
|
* [installing latest xmonad snapshot from git][web:install]
|
||||||
|
* [configuring xmonad][web:tutorial]
|
||||||
|
|
||||||
- <https://github.com/xmonad/xmonad>
|
If you run into any trouble, consult our [documentation][web:documentation] or
|
||||||
|
ask the [community][web:community] for help.
|
||||||
|
|
||||||
- <https://github.com/xmonad/xmonad-contrib>
|
## Contributing
|
||||||
|
|
||||||
For the full story, read on.
|
We welcome all forms of contributions:
|
||||||
|
|
||||||
## Building
|
* [bug reports and feature ideas][gh:xmonad:issues]
|
||||||
|
(also to [xmonad-contrib][gh:xmonad-contrib:issues])
|
||||||
|
* [bug fixes, new features, new extensions][gh:xmonad:pulls]
|
||||||
|
(usually to [xmonad-contrib][gh:xmonad-contrib:pulls])
|
||||||
|
* documentation fixes and improvements: [xmonad][gh:xmonad],
|
||||||
|
[xmonad-contrib][gh:xmonad-contrib], [xmonad-web][gh:xmonad-web]
|
||||||
|
* helping others in the [community][web:community]
|
||||||
|
* financial support: [GitHub Sponsors][gh:xmonad:sponsors],
|
||||||
|
[Open Collective][opencollective:xmonad]
|
||||||
|
|
||||||
Building is quite straightforward, and requires a basic Haskell toolchain.
|
Please do read the [CONTRIBUTING][gh:xmonad:contributing] document for more
|
||||||
On many systems xmonad is available as a binary package in your
|
information about bug reporting and code contributions. For a brief overview
|
||||||
package system (e.g. on Debian or Gentoo). If at all possible, use this
|
of the architecture and code conventions, see the [documentation for the
|
||||||
in preference to a source build, as the dependency resolution will be
|
`XMonad.Doc.Developing` module][doc:developing]. If in doubt, [talk to
|
||||||
simpler.
|
us][web:community].
|
||||||
|
|
||||||
We'll now walk through the complete list of toolchain dependencies.
|
|
||||||
|
|
||||||
* GHC: the Glasgow Haskell Compiler
|
|
||||||
|
|
||||||
You first need a Haskell compiler. Your distribution's package
|
|
||||||
system will have binaries of GHC (the Glasgow Haskell Compiler),
|
|
||||||
the compiler we use, so install that first. If your operating
|
|
||||||
system's package system doesn't provide a binary version of GHC
|
|
||||||
and the `cabal-install` tool, you can install both using the
|
|
||||||
[Haskell Platform][platform].
|
|
||||||
|
|
||||||
It shouldn't be necessary to compile GHC from source -- every common
|
|
||||||
system has a pre-build binary version. However, if you want to
|
|
||||||
build from source, the following links will be helpful:
|
|
||||||
|
|
||||||
- GHC: <http://haskell.org/ghc/>
|
|
||||||
|
|
||||||
- Cabal: <http://haskell.org/cabal/download.html>
|
|
||||||
|
|
||||||
* X11 libraries:
|
|
||||||
|
|
||||||
Since you're building an X application, you'll need the C X11
|
|
||||||
library headers. On many platforms, these come pre-installed. For
|
|
||||||
others, such as Debian, you can get them from your package manager:
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
If you built XMonad using `cabal` then add:
|
|
||||||
|
|
||||||
exec $HOME/.cabal/bin/xmonad
|
|
||||||
|
|
||||||
to the last line of your `.xsession` or `.xinitrc` file.
|
|
||||||
|
|
||||||
## Configuring
|
|
||||||
|
|
||||||
See the [CONFIG][] document and the [example configuration file][example-config].
|
|
||||||
|
|
||||||
## XMonadContrib
|
|
||||||
|
|
||||||
There are many extensions to xmonad available in the XMonadContrib
|
|
||||||
(xmc) library. Examples include an ion3-like tabbed layout, a
|
|
||||||
prompt/program launcher, and various other useful modules.
|
|
||||||
XMonadContrib is available at:
|
|
||||||
|
|
||||||
* Latest release: <http://hackage.haskell.org/package/xmonad-contrib>
|
|
||||||
|
|
||||||
* Git version: <https://github.com/xmonad/xmonad-contrib>
|
|
||||||
|
|
||||||
## Other Useful Programs
|
|
||||||
|
|
||||||
A nicer xterm replacement, that supports resizing better:
|
|
||||||
|
|
||||||
* urxvt: <http://software.schmorp.de/pkg/rxvt-unicode.html>
|
|
||||||
|
|
||||||
For custom status bars:
|
|
||||||
|
|
||||||
* xmobar: <http://hackage.haskell.org/package/xmobar>
|
|
||||||
|
|
||||||
* taffybar: <https://github.com/travitch/taffybar>
|
|
||||||
|
|
||||||
* dzen: <http://gotmor.googlepages.com/dzen>
|
|
||||||
|
|
||||||
For a program dispatch menu:
|
|
||||||
|
|
||||||
* [XMonad.Prompt.Shell][xmc-prompt-shell]: (from [XMonadContrib][])
|
|
||||||
|
|
||||||
* dmenu: <http://www.suckless.org/download/>
|
|
||||||
|
|
||||||
* gmrun: (in your package system)
|
|
||||||
|
|
||||||
## Authors
|
## Authors
|
||||||
|
|
||||||
* Spencer Janssen
|
Started in 2007 by [Spencer Janssen][gh:spencerjanssen], [Don
|
||||||
* Don Stewart
|
Stewart][gh:donsbot] and [Jason Creighton][gh:JasonCreighton], the
|
||||||
* Jason Creighton
|
[XMonad][web:xmonad] project lives on thanks to [new generations of
|
||||||
|
maintainers][gh:xmonad:maintainers] and [dozens of
|
||||||
|
contributors][gh:xmonad:contributors].
|
||||||
|
|
||||||
[xmonad]: http://xmonad.org
|
[gh:spencerjanssen]: https://github.com/spencerjanssen
|
||||||
[xmonadcontrib]: https://hackage.haskell.org/package/xmonad-contrib
|
[gh:donsbot]: https://github.com/donsbot
|
||||||
[xmc-prompt-shell]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Prompt-Shell.html
|
[gh:JasonCreighton]: https://github.com/JasonCreighton
|
||||||
[platform]: http://haskell.org/platform/
|
|
||||||
[example-config]: https://github.com/xmonad/xmonad-testing/blob/master/example-config.hs
|
[doc:developing]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Doc-Developing.html
|
||||||
[config]: https://github.com/xmonad/xmonad/blob/master/CONFIG
|
[gh:xmonad-contrib:issues]: https://github.com/xmonad/xmonad-contrib/issues
|
||||||
|
[gh:xmonad-contrib:pulls]: https://github.com/xmonad/xmonad-contrib/pulls
|
||||||
|
[gh:xmonad-contrib]: https://github.com/xmonad/xmonad-contrib
|
||||||
|
[gh:xmonad-web]: https://github.com/xmonad/xmonad-web
|
||||||
|
[gh:xmonad:contributing]: https://github.com/xmonad/xmonad/blob/master/CONTRIBUTING.md
|
||||||
|
[gh:xmonad:contributors]: https://github.com/xmonad/xmonad/graphs/contributors
|
||||||
|
[gh:xmonad:issues]: https://github.com/xmonad/xmonad/issues
|
||||||
|
[gh:xmonad:maintainers]: https://github.com/xmonad/xmonad/blob/master/MAINTAINERS.md
|
||||||
|
[gh:xmonad:pulls]: https://github.com/xmonad/xmonad/pulls
|
||||||
|
[gh:xmonad:sponsors]: https://github.com/sponsors/xmonad
|
||||||
|
[gh:xmonad]: https://github.com/xmonad/xmonad
|
||||||
|
[hackage:xmonad]: https://hackage.haskell.org/package/xmonad
|
||||||
|
[opencollective:xmonad]: https://opencollective.com/xmonad
|
||||||
|
[web:community]: https://xmonad.org/community.html
|
||||||
|
[web:documentation]: https://xmonad.org/documentation.html
|
||||||
|
[web:download]: https://xmonad.org/download.html
|
||||||
|
[web:install]: https://xmonad.org/INSTALL.html
|
||||||
|
[web:tutorial]: https://xmonad.org/TUTORIAL.html
|
||||||
|
[web:xmonad]: https://xmonad.org/
|
||||||
|
22
STYLE
22
STYLE
@@ -1,22 +0,0 @@
|
|||||||
|
|
||||||
== Coding guidelines for contributing to
|
|
||||||
== xmonad and the xmonad contributed extensions
|
|
||||||
|
|
||||||
* Comment every top level function (particularly exported functions), and
|
|
||||||
provide a type signature; use Haddock syntax in the comments.
|
|
||||||
|
|
||||||
* Follow the coding style of the other modules.
|
|
||||||
|
|
||||||
* Code should be compilable with -Wall -Werror -fno-warn-unused-do-bind -fwarn-tabs.
|
|
||||||
There should be no warnings.
|
|
||||||
|
|
||||||
* Partial functions should be avoided: the window manager should not
|
|
||||||
crash, so do not call `error` or `undefined`
|
|
||||||
|
|
||||||
* Use 4 spaces for indenting.
|
|
||||||
|
|
||||||
* Any pure function added to the core should have QuickCheck properties
|
|
||||||
precisely defining its behavior.
|
|
||||||
|
|
||||||
* New modules should identify the author, and be submitted under
|
|
||||||
the same license as xmonad (BSD3 license or freer).
|
|
1268
TUTORIAL.md
Normal file
1268
TUTORIAL.md
Normal file
File diff suppressed because it is too large
Load Diff
17
cabal.haskell-ci
Normal file
17
cabal.haskell-ci
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
apt:
|
||||||
|
libx11-dev
|
||||||
|
libxext-dev
|
||||||
|
libxinerama-dev
|
||||||
|
libxrandr-dev
|
||||||
|
libxss-dev
|
||||||
|
|
||||||
|
github-patches:
|
||||||
|
.github/workflows/haskell-ci-hackage.patch
|
||||||
|
|
||||||
|
raw-project
|
||||||
|
optimization: False
|
||||||
|
package xmonad
|
||||||
|
flags: +pedantic
|
||||||
|
|
||||||
|
-- avoid --haddock-all which overwrites *-docs.tar.gz with tests docs
|
||||||
|
haddock-components: libs
|
@@ -1 +1,4 @@
|
|||||||
packages: ./
|
-- cabal.project
|
||||||
|
|
||||||
|
packages:
|
||||||
|
xmonad.cabal
|
||||||
|
77
flake.nix
Normal file
77
flake.nix
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# This file is maintained by @IvanMalison and @LSLeary (github)
|
||||||
|
# See xmonad-contrib/NIX.md for an overview of module usage.
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
flake-utils.url = github:numtide/flake-utils;
|
||||||
|
git-ignore-nix.url = github:hercules-ci/gitignore.nix/master;
|
||||||
|
unstable.url = github:NixOS/nixpkgs/nixos-unstable;
|
||||||
|
};
|
||||||
|
outputs = { self, flake-utils, nixpkgs, unstable, git-ignore-nix }:
|
||||||
|
let
|
||||||
|
hpath = { prefix ? null, compiler ? null }:
|
||||||
|
(if prefix == null then [] else [ prefix ]) ++
|
||||||
|
(if compiler == null
|
||||||
|
then [ "haskellPackages" ]
|
||||||
|
else [ "haskell" "packages" compiler ]
|
||||||
|
);
|
||||||
|
fromHOL = hol: comp: final: prev: with prev.lib; with attrsets;
|
||||||
|
setAttrByPath (hpath comp)
|
||||||
|
((getAttrFromPath (hpath comp) prev).override (old: {
|
||||||
|
overrides = composeExtensions (old.overrides or (_: _: {}))
|
||||||
|
(hol final prev);
|
||||||
|
}));
|
||||||
|
hoverlay = final: prev: hself: hsuper:
|
||||||
|
with prev.haskell.lib.compose; {
|
||||||
|
xmonad = hself.callCabal2nix "xmonad"
|
||||||
|
(git-ignore-nix.lib.gitignoreSource ./.) { };
|
||||||
|
};
|
||||||
|
overlay = fromHOL hoverlay { };
|
||||||
|
overlays = [ overlay ];
|
||||||
|
nixosModule = { config, pkgs, lib, ... }: with lib; with attrsets;
|
||||||
|
let
|
||||||
|
cfg = config.services.xserver.windowManager.xmonad.flake;
|
||||||
|
comp = { inherit (cfg) prefix compiler; };
|
||||||
|
in {
|
||||||
|
options = {
|
||||||
|
services.xserver.windowManager.xmonad.flake = with types; {
|
||||||
|
enable = mkEnableOption "flake";
|
||||||
|
prefix = mkOption {
|
||||||
|
default = null;
|
||||||
|
type = nullOr string;
|
||||||
|
example = literalExpression "\"unstable\"";
|
||||||
|
description = ''
|
||||||
|
Specify a nested alternative <literal>pkgs</literal> by attrName.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
compiler = mkOption {
|
||||||
|
default = null;
|
||||||
|
type = nullOr string;
|
||||||
|
example = literalExpression "\"ghc922\"";
|
||||||
|
description = ''
|
||||||
|
Which compiler to build xmonad with.
|
||||||
|
Must be an attribute of <literal>pkgs.haskell.packages</literal>.
|
||||||
|
Sets <option>xmonad.haskellPackages</option> to match.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
nixpkgs.overlays = [ (fromHOL hoverlay comp) ];
|
||||||
|
services.xserver.windowManager.xmonad.haskellPackages =
|
||||||
|
getAttrFromPath (hpath comp) pkgs;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
nixosModules = [ nixosModule ];
|
||||||
|
in flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let pkgs = import nixpkgs { inherit system overlays; };
|
||||||
|
in
|
||||||
|
rec {
|
||||||
|
devShell = pkgs.haskellPackages.shellFor {
|
||||||
|
packages = p: [ p.xmonad ];
|
||||||
|
};
|
||||||
|
defaultPackage = pkgs.haskellPackages.xmonad;
|
||||||
|
}) // {
|
||||||
|
inherit hoverlay overlay overlays nixosModule nixosModules;
|
||||||
|
lib = { inherit hpath fromHOL; };
|
||||||
|
};
|
||||||
|
}
|
11
man/Makefile
Normal file
11
man/Makefile
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
.PHONY: all
|
||||||
|
all: xmonad.1 xmonad.1.html
|
||||||
|
|
||||||
|
xmonad.1.markdown: xmonad.1.markdown.in
|
||||||
|
(cd .. && util/GenerateManpage.hs) <$< >$@
|
||||||
|
|
||||||
|
xmonad.1: xmonad.1.markdown
|
||||||
|
pandoc --from=markdown --to=man --standalone --output=$@ $<
|
||||||
|
|
||||||
|
xmonad.1.html: xmonad.1.markdown
|
||||||
|
pandoc --from=markdown --to=html --standalone --table-of-contents --output=$@ $<
|
232
man/xmonad.1
232
man/xmonad.1
@@ -1,13 +1,13 @@
|
|||||||
.\" Automatically generated by Pandoc 2.2.1
|
.\" Automatically generated by Pandoc 2.9.2.1
|
||||||
.\"
|
.\"
|
||||||
.TH "XMONAD" "1" "20 August 2018" "Tiling Window Manager" ""
|
.TH "XMONAD" "1" "27 October 2021" "Tiling Window Manager" ""
|
||||||
.hy
|
.hy
|
||||||
.SH Name
|
.SH Name
|
||||||
.PP
|
.PP
|
||||||
xmonad \- Tiling Window Manager
|
xmonad - Tiling Window Manager
|
||||||
.SH Description
|
.SH Description
|
||||||
.PP
|
.PP
|
||||||
\f[I]xmonad\f[] is a minimalist tiling window manager for X, written in
|
\f[I]xmonad\f[R] is a minimalist tiling window manager for X, written in
|
||||||
Haskell.
|
Haskell.
|
||||||
Windows are managed using automatic layout algorithms, which can be
|
Windows are managed using automatic layout algorithms, which can be
|
||||||
dynamically reconfigured.
|
dynamically reconfigured.
|
||||||
@@ -15,14 +15,14 @@ At any time windows are arranged so as to maximize the use of screen
|
|||||||
real estate.
|
real estate.
|
||||||
All features of the window manager are accessible purely from the
|
All features of the window manager are accessible purely from the
|
||||||
keyboard: a mouse is entirely optional.
|
keyboard: a mouse is entirely optional.
|
||||||
\f[I]xmonad\f[] is configured in Haskell, and custom layout algorithms
|
\f[I]xmonad\f[R] is configured in Haskell, and custom layout algorithms
|
||||||
may be implemented by the user in config files.
|
may be implemented by the user in config files.
|
||||||
A principle of \f[I]xmonad\f[] is predictability: the user should know
|
A principle of \f[I]xmonad\f[R] is predictability: the user should know
|
||||||
in advance precisely the window arrangement that will result from any
|
in advance precisely the window arrangement that will result from any
|
||||||
action.
|
action.
|
||||||
.PP
|
.PP
|
||||||
By default, \f[I]xmonad\f[] provides three layout algorithms: tall, wide
|
By default, \f[I]xmonad\f[R] provides three layout algorithms: tall,
|
||||||
and fullscreen.
|
wide and fullscreen.
|
||||||
In tall or wide mode, windows are tiled and arranged to prevent overlap
|
In tall or wide mode, windows are tiled and arranged to prevent overlap
|
||||||
and maximize screen use.
|
and maximize screen use.
|
||||||
Sets of windows are grouped together on virtual screens, and each screen
|
Sets of windows are grouped together on virtual screens, and each screen
|
||||||
@@ -31,34 +31,34 @@ Multiple physical monitors are supported via Xinerama, allowing
|
|||||||
simultaneous display of a number of screens.
|
simultaneous display of a number of screens.
|
||||||
.PP
|
.PP
|
||||||
By utilizing the expressivity of a modern functional language with a
|
By utilizing the expressivity of a modern functional language with a
|
||||||
rich static type system, \f[I]xmonad\f[] provides a complete, featureful
|
rich static type system, \f[I]xmonad\f[R] provides a complete,
|
||||||
window manager in less than 1200 lines of code, with an emphasis on
|
featureful window manager in less than 1200 lines of code, with an
|
||||||
correctness and robustness.
|
emphasis on correctness and robustness.
|
||||||
Internal properties of the window manager are checked using a
|
Internal properties of the window manager are checked using a
|
||||||
combination of static guarantees provided by the type system, and
|
combination of static guarantees provided by the type system, and
|
||||||
type\-based automated testing.
|
type-based automated testing.
|
||||||
A benefit of this is that the code is simple to understand, and easy to
|
A benefit of this is that the code is simple to understand, and easy to
|
||||||
modify.
|
modify.
|
||||||
.SH Usage
|
.SH Usage
|
||||||
.PP
|
.PP
|
||||||
\f[I]xmonad\f[] places each window into a \[lq]workspace\[rq].
|
\f[I]xmonad\f[R] places each window into a \[lq]workspace\[rq].
|
||||||
Each workspace can have any number of windows, which you can cycle
|
Each workspace can have any number of windows, which you can cycle
|
||||||
though with mod\-j and mod\-k.
|
though with mod-j and mod-k.
|
||||||
Windows are either displayed full screen, tiled horizontally, or tiled
|
Windows are either displayed full screen, tiled horizontally, or tiled
|
||||||
vertically.
|
vertically.
|
||||||
You can toggle the layout mode with mod\-space, which will cycle through
|
You can toggle the layout mode with mod-space, which will cycle through
|
||||||
the available modes.
|
the available modes.
|
||||||
.PP
|
.PP
|
||||||
You can switch to workspace N with mod\-N.
|
You can switch to workspace N with mod-N.
|
||||||
For example, to switch to workspace 5, you would press mod\-5.
|
For example, to switch to workspace 5, you would press mod-5.
|
||||||
Similarly, you can move the current window to another workspace with
|
Similarly, you can move the current window to another workspace with
|
||||||
mod\-shift\-N.
|
mod-shift-N.
|
||||||
.PP
|
.PP
|
||||||
When running with multiple monitors (Xinerama), each screen has exactly
|
When running with multiple monitors (Xinerama), each screen has exactly
|
||||||
1 workspace visible.
|
1 workspace visible.
|
||||||
mod\-{w,e,r} switch the focus between screens, while shift\-mod\-{w,e,r}
|
mod-{w,e,r} switch the focus between screens, while shift-mod-{w,e,r}
|
||||||
move the current window to that screen.
|
move the current window to that screen.
|
||||||
When \f[I]xmonad\f[] starts, workspace 1 is on screen 1, workspace 2 is
|
When \f[I]xmonad\f[R] starts, workspace 1 is on screen 1, workspace 2 is
|
||||||
on screen 2, etc.
|
on screen 2, etc.
|
||||||
When switching workspaces to one that is already visible, the current
|
When switching workspaces to one that is already visible, the current
|
||||||
and visible workspaces are swapped.
|
and visible workspaces are swapped.
|
||||||
@@ -67,221 +67,159 @@ and visible workspaces are swapped.
|
|||||||
xmonad has several flags which you may pass to the executable.
|
xmonad has several flags which you may pass to the executable.
|
||||||
These flags are:
|
These flags are:
|
||||||
.TP
|
.TP
|
||||||
.B \[en]recompile
|
\[en]recompile
|
||||||
Recompiles your configuration in \f[I]~/.xmonad/xmonad.hs\f[]
|
Recompiles your \f[I]xmonad.hs\f[R] configuration
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B \[en]restart
|
\[en]restart
|
||||||
Causes the currently running \f[I]xmonad\f[] process to restart
|
Causes the currently running \f[I]xmonad\f[R] process to restart
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B \[en]replace
|
\[en]replace
|
||||||
Replace the current window manager with xmonad
|
Replace the current window manager with xmonad
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B \[en]version
|
\[en]version
|
||||||
Display version of \f[I]xmonad\f[]
|
Display version of \f[I]xmonad\f[R]
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B \[en]verbose\-version
|
\[en]verbose-version
|
||||||
Display detailed version of \f[I]xmonad\f[]
|
Display detailed version of \f[I]xmonad\f[R]
|
||||||
.RS
|
.SS Default keyboard bindings
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
##Default keyboard bindings
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-shift\-return
|
mod-shift-return
|
||||||
Launch terminal
|
Launch terminal
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-p
|
mod-p
|
||||||
Launch dmenu
|
Launch dmenu
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-shift\-p
|
mod-shift-p
|
||||||
Launch gmrun
|
Launch gmrun
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-shift\-c
|
mod-shift-c
|
||||||
Close the focused window
|
Close the focused window
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-space
|
mod-space
|
||||||
Rotate through the available layout algorithms
|
Rotate through the available layout algorithms
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-shift\-space
|
mod-shift-space
|
||||||
Reset the layouts on the current workspace to default
|
Reset the layouts on the current workspace to default
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-n
|
mod-n
|
||||||
Resize viewed windows to the correct size
|
Resize viewed windows to the correct size
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-tab
|
mod-tab
|
||||||
Move focus to the next window
|
Move focus to the next window
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-shift\-tab
|
mod-shift-tab
|
||||||
Move focus to the previous window
|
Move focus to the previous window
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-j
|
mod-j
|
||||||
Move focus to the next window
|
Move focus to the next window
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-k
|
mod-k
|
||||||
Move focus to the previous window
|
Move focus to the previous window
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-m
|
mod-m
|
||||||
Move focus to the master window
|
Move focus to the master window
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-return
|
mod-return
|
||||||
Swap the focused window and the master window
|
Swap the focused window and the master window
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-shift\-j
|
mod-shift-j
|
||||||
Swap the focused window with the next window
|
Swap the focused window with the next window
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-shift\-k
|
mod-shift-k
|
||||||
Swap the focused window with the previous window
|
Swap the focused window with the previous window
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-h
|
mod-h
|
||||||
Shrink the master area
|
Shrink the master area
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-l
|
mod-l
|
||||||
Expand the master area
|
Expand the master area
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-t
|
mod-t
|
||||||
Push window back into tiling
|
Push window back into tiling
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-comma
|
mod-comma
|
||||||
Increment the number of windows in the master area
|
Increment the number of windows in the master area
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-period
|
mod-period
|
||||||
Deincrement the number of windows in the master area
|
Deincrement the number of windows in the master area
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-shift\-q
|
mod-shift-q
|
||||||
Quit xmonad
|
Quit xmonad
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-q
|
mod-q
|
||||||
Restart xmonad
|
Restart xmonad
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-shift\-slash
|
mod-shift-slash
|
||||||
Run xmessage with a summary of the default keybindings (useful for
|
Run xmessage with a summary of the default keybindings (useful for
|
||||||
beginners)
|
beginners)
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-question
|
mod-question
|
||||||
Run xmessage with a summary of the default keybindings (useful for
|
Run xmessage with a summary of the default keybindings (useful for
|
||||||
beginners)
|
beginners)
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-[1..9]
|
mod-[1..9]
|
||||||
Switch to workspace N
|
Switch to workspace N
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-shift\-[1..9]
|
mod-shift-[1..9]
|
||||||
Move client to workspace N
|
Move client to workspace N
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-{w,e,r}
|
mod-{w,e,r}
|
||||||
Switch to physical/Xinerama screens 1, 2, or 3
|
Switch to physical/Xinerama screens 1, 2, or 3
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-shift\-{w,e,r}
|
mod-shift-{w,e,r}
|
||||||
Move client to screen 1, 2, or 3
|
Move client to screen 1, 2, or 3
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-button1
|
mod-button1
|
||||||
Set the window to floating mode and move by dragging
|
Set the window to floating mode and move by dragging
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-button2
|
mod-button2
|
||||||
Raise the window to the top of the stack
|
Raise the window to the top of the stack
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.TP
|
.TP
|
||||||
.B mod\-button3
|
mod-button3
|
||||||
Set the window to floating mode and resize by dragging
|
Set the window to floating mode and resize by dragging
|
||||||
.RS
|
|
||||||
.RE
|
|
||||||
.SH Examples
|
.SH Examples
|
||||||
.PP
|
.PP
|
||||||
To use xmonad as your window manager add to your \f[I]~/.xinitrc\f[]
|
To use xmonad as your window manager add to your
|
||||||
file:
|
\f[I]\[ti]/.xinitrc\f[R] file:
|
||||||
.RS
|
.RS
|
||||||
.PP
|
.PP
|
||||||
exec xmonad
|
exec xmonad
|
||||||
.RE
|
.RE
|
||||||
.SH Customization
|
.SH Customization
|
||||||
.PP
|
.PP
|
||||||
xmonad is customized in ~/.xmonad/xmonad.hs, and then restarted with
|
xmonad is customized in your \f[I]xmonad.hs\f[R], and then restarted
|
||||||
mod\-q.
|
with mod-q.
|
||||||
|
You can choose where your configuration file lives by
|
||||||
|
.IP "1." 3
|
||||||
|
Setting \f[C]XMONAD_DATA_DIR,\f[R] \f[C]XMONAD_CONFIG_DIR\f[R], and
|
||||||
|
\f[C]XMONAD_CACHE_DIR\f[R]; \f[I]xmonad.hs\f[R] is then expected to be
|
||||||
|
in \f[C]XMONAD_CONFIG_DIR\f[R].
|
||||||
|
.IP "2." 3
|
||||||
|
Creating \f[I]xmonad.hs\f[R] in \f[I]\[ti]/.xmonad\f[R].
|
||||||
|
.IP "3." 3
|
||||||
|
Creating \f[I]xmonad.hs\f[R] in \f[C]XDG_CONFIG_HOME\f[R].
|
||||||
|
Note that, in this case, xmonad will use \f[C]XDG_DATA_HOME\f[R] and
|
||||||
|
\f[C]XDG_CACHE_HOME\f[R] for its data and cache directory respectively.
|
||||||
.PP
|
.PP
|
||||||
You can find many extensions to the core feature set in the xmonad\-
|
You can find many extensions to the core feature set in the xmonad-
|
||||||
contrib package, available through your package manager or from
|
contrib package, available through your package manager or from
|
||||||
xmonad.org (http://xmonad.org).
|
xmonad.org (https://xmonad.org).
|
||||||
.SS Modular Configuration
|
.SS Modular Configuration
|
||||||
.PP
|
.PP
|
||||||
As of \f[I]xmonad\-0.9\f[], any additional Haskell modules may be placed
|
As of \f[I]xmonad-0.9\f[R], any additional Haskell modules may be placed
|
||||||
in \f[I]~/.xmonad/lib/\f[] are available in GHC's searchpath.
|
in \f[I]\[ti]/.xmonad/lib/\f[R] are available in GHC\[cq]s searchpath.
|
||||||
Hierarchical modules are supported: for example, the file
|
Hierarchical modules are supported: for example, the file
|
||||||
\f[I]~/.xmonad/lib/XMonad/Stack/MyAdditions.hs\f[] could contain:
|
\f[I]\[ti]/.xmonad/lib/XMonad/Stack/MyAdditions.hs\f[R] could contain:
|
||||||
.IP
|
.IP
|
||||||
.nf
|
.nf
|
||||||
\f[C]
|
\f[C]
|
||||||
module\ XMonad.Stack.MyAdditions\ (function1)\ where
|
module XMonad.Stack.MyAdditions (function1) where
|
||||||
\ \ function1\ =\ error\ "function1:\ Not\ implemented\ yet!"
|
function1 = error \[dq]function1: Not implemented yet!\[dq]
|
||||||
\f[]
|
\f[R]
|
||||||
.fi
|
.fi
|
||||||
.PP
|
.PP
|
||||||
Your xmonad.hs may then import XMonad.Stack.MyAdditions as if that
|
Your xmonad.hs may then import XMonad.Stack.MyAdditions as if that
|
||||||
module was contained within xmonad or xmonad\-contrib.
|
module was contained within xmonad or xmonad-contrib.
|
||||||
.SH Bugs
|
.SH Bugs
|
||||||
.PP
|
.PP
|
||||||
Probably.
|
Probably.
|
||||||
|
@@ -5,96 +5,97 @@
|
|||||||
<meta name="generator" content="pandoc" />
|
<meta name="generator" content="pandoc" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||||
<meta name="author" content="" />
|
<meta name="author" content="" />
|
||||||
<meta name="dcterms.date" content="2018-08-20" />
|
<meta name="dcterms.date" content="2021-10-27" />
|
||||||
<title>XMONAD(1) Tiling Window Manager</title>
|
<title>XMONAD(1) Tiling Window Manager</title>
|
||||||
<style type="text/css">
|
<style>
|
||||||
code{white-space: pre-wrap;}
|
code{white-space: pre-wrap;}
|
||||||
span.smallcaps{font-variant: small-caps;}
|
span.smallcaps{font-variant: small-caps;}
|
||||||
span.underline{text-decoration: underline;}
|
span.underline{text-decoration: underline;}
|
||||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||||
|
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||||
|
ul.task-list{list-style: none;}
|
||||||
|
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||||
|
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||||
|
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||||
|
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||||
|
div.sourceCode { margin: 1em 0; }
|
||||||
|
pre.sourceCode { margin: 0; }
|
||||||
|
@media screen {
|
||||||
|
div.sourceCode { overflow: auto; }
|
||||||
|
}
|
||||||
|
@media print {
|
||||||
|
pre > code.sourceCode { white-space: pre-wrap; }
|
||||||
|
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||||
|
}
|
||||||
|
pre.numberSource code
|
||||||
|
{ counter-reset: source-line 0; }
|
||||||
|
pre.numberSource code > span
|
||||||
|
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||||
|
pre.numberSource code > span > a:first-child::before
|
||||||
|
{ content: counter(source-line);
|
||||||
|
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||||
|
border: none; 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 {
|
||||||
|
pre > code.sourceCode > span > a:first-child::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>
|
</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>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header id="title-block-header">
|
||||||
<h1 class="title">XMONAD(1) Tiling Window Manager</h1>
|
<h1 class="title">XMONAD(1) Tiling Window Manager</h1>
|
||||||
<p class="author"></p>
|
<p class="author"></p>
|
||||||
<p class="date">20 August 2018</p>
|
<p class="date">27 October 2021</p>
|
||||||
</header>
|
</header>
|
||||||
<nav id="TOC">
|
<nav id="TOC" role="doc-toc">
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="#name">Name</a></li>
|
<li><a href="#name">Name</a></li>
|
||||||
<li><a href="#description">Description</a></li>
|
<li><a href="#description">Description</a></li>
|
||||||
<li><a href="#usage">Usage</a><ul>
|
<li><a href="#usage">Usage</a>
|
||||||
|
<ul>
|
||||||
<li><a href="#flags">Flags</a></li>
|
<li><a href="#flags">Flags</a></li>
|
||||||
|
<li><a href="#default-keyboard-bindings">Default keyboard bindings</a></li>
|
||||||
</ul></li>
|
</ul></li>
|
||||||
<li><a href="#examples">Examples</a></li>
|
<li><a href="#examples">Examples</a></li>
|
||||||
<li><a href="#customization">Customization</a><ul>
|
<li><a href="#customization">Customization</a>
|
||||||
|
<ul>
|
||||||
<li><a href="#modular-configuration">Modular Configuration</a></li>
|
<li><a href="#modular-configuration">Modular Configuration</a></li>
|
||||||
</ul></li>
|
</ul></li>
|
||||||
<li><a href="#bugs">Bugs</a></li>
|
<li><a href="#bugs">Bugs</a></li>
|
||||||
@@ -114,7 +115,7 @@ code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warni
|
|||||||
<p>xmonad has several flags which you may pass to the executable. These flags are:</p>
|
<p>xmonad has several flags which you may pass to the executable. These flags are:</p>
|
||||||
<dl>
|
<dl>
|
||||||
<dt>–recompile</dt>
|
<dt>–recompile</dt>
|
||||||
<dd>Recompiles your configuration in <em>~/.xmonad/xmonad.hs</em>
|
<dd>Recompiles your <em>xmonad.hs</em> configuration
|
||||||
</dd>
|
</dd>
|
||||||
<dt>–restart</dt>
|
<dt>–restart</dt>
|
||||||
<dd>Causes the currently running <em>xmonad</em> process to restart
|
<dd>Causes the currently running <em>xmonad</em> process to restart
|
||||||
@@ -129,7 +130,7 @@ code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warni
|
|||||||
<dd>Display detailed version of <em>xmonad</em>
|
<dd>Display detailed version of <em>xmonad</em>
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<p>##Default keyboard bindings</p>
|
<h2 id="default-keyboard-bindings">Default keyboard bindings</h2>
|
||||||
<dl>
|
<dl>
|
||||||
<dt>mod-shift-return</dt>
|
<dt>mod-shift-return</dt>
|
||||||
<dd>Launch terminal
|
<dd>Launch terminal
|
||||||
@@ -231,12 +232,17 @@ code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warni
|
|||||||
<p>exec xmonad</p>
|
<p>exec xmonad</p>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<h1 id="customization">Customization</h1>
|
<h1 id="customization">Customization</h1>
|
||||||
<p>xmonad is customized in ~/.xmonad/xmonad.hs, and then restarted with mod-q.</p>
|
<p>xmonad is customized in your <em>xmonad.hs</em>, and then restarted with mod-q. You can choose where your configuration file lives by</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>
|
<ol type="1">
|
||||||
|
<li>Setting <code>XMONAD_DATA_DIR,</code> <code>XMONAD_CONFIG_DIR</code>, and <code>XMONAD_CACHE_DIR</code>; <em>xmonad.hs</em> is then expected to be in <code>XMONAD_CONFIG_DIR</code>.</li>
|
||||||
|
<li>Creating <em>xmonad.hs</em> in <em>~/.xmonad</em>.</li>
|
||||||
|
<li>Creating <em>xmonad.hs</em> in <code>XDG_CONFIG_HOME</code>. Note that, in this case, xmonad will use <code>XDG_DATA_HOME</code> and <code>XDG_CACHE_HOME</code> for its data and cache directory respectively.</li>
|
||||||
|
</ol>
|
||||||
|
<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="https://xmonad.org">xmonad.org</a>.</p>
|
||||||
<h2 id="modular-configuration">Modular Configuration</h2>
|
<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>
|
<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>
|
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="kw">module</span> <span class="dt">XMonad.Stack.MyAdditions</span> (function1) <span class="kw">where</span></span>
|
||||||
<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>
|
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a> function1 <span class="ot">=</span> <span class="fu">error</span> <span class="st">"function1: Not implemented yet!"</span></span></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>
|
<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>
|
<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>
|
<p>Probably. If you find any, please report them to the <a href="https://github.com/xmonad/xmonad/issues">bugtracker</a></p>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
% XMONAD(1) Tiling Window Manager
|
% XMONAD(1) Tiling Window Manager
|
||||||
%
|
%
|
||||||
% 20 August 2018
|
% 27 October 2021
|
||||||
|
|
||||||
# Name
|
# Name
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ xmonad has several flags which you may pass to the executable.
|
|||||||
These flags are:
|
These flags are:
|
||||||
|
|
||||||
--recompile
|
--recompile
|
||||||
: Recompiles your configuration in _~/.xmonad/xmonad.hs_
|
: Recompiles your _xmonad.hs_ configuration
|
||||||
|
|
||||||
--restart
|
--restart
|
||||||
: Causes the currently running _xmonad_ process to restart
|
: Causes the currently running _xmonad_ process to restart
|
||||||
@@ -72,9 +72,100 @@ These flags are:
|
|||||||
--verbose-version
|
--verbose-version
|
||||||
: Display detailed version of _xmonad_
|
: Display detailed version of _xmonad_
|
||||||
|
|
||||||
##Default keyboard bindings
|
## Default keyboard bindings
|
||||||
|
|
||||||
___KEYBINDINGS___
|
mod-shift-return
|
||||||
|
: Launch terminal
|
||||||
|
|
||||||
|
mod-p
|
||||||
|
: Launch dmenu
|
||||||
|
|
||||||
|
mod-shift-p
|
||||||
|
: Launch gmrun
|
||||||
|
|
||||||
|
mod-shift-c
|
||||||
|
: Close the focused window
|
||||||
|
|
||||||
|
mod-space
|
||||||
|
: Rotate through the available layout algorithms
|
||||||
|
|
||||||
|
mod-shift-space
|
||||||
|
: Reset the layouts on the current workspace to default
|
||||||
|
|
||||||
|
mod-n
|
||||||
|
: Resize viewed windows to the correct size
|
||||||
|
|
||||||
|
mod-tab
|
||||||
|
: Move focus to the next window
|
||||||
|
|
||||||
|
mod-shift-tab
|
||||||
|
: Move focus to the previous window
|
||||||
|
|
||||||
|
mod-j
|
||||||
|
: Move focus to the next window
|
||||||
|
|
||||||
|
mod-k
|
||||||
|
: Move focus to the previous window
|
||||||
|
|
||||||
|
mod-m
|
||||||
|
: Move focus to the master window
|
||||||
|
|
||||||
|
mod-return
|
||||||
|
: Swap the focused window and the master window
|
||||||
|
|
||||||
|
mod-shift-j
|
||||||
|
: Swap the focused window with the next window
|
||||||
|
|
||||||
|
mod-shift-k
|
||||||
|
: Swap the focused window with the previous window
|
||||||
|
|
||||||
|
mod-h
|
||||||
|
: Shrink the master area
|
||||||
|
|
||||||
|
mod-l
|
||||||
|
: Expand the master area
|
||||||
|
|
||||||
|
mod-t
|
||||||
|
: Push window back into tiling
|
||||||
|
|
||||||
|
mod-comma
|
||||||
|
: Increment the number of windows in the master area
|
||||||
|
|
||||||
|
mod-period
|
||||||
|
: Deincrement the number of windows in the master area
|
||||||
|
|
||||||
|
mod-shift-q
|
||||||
|
: Quit xmonad
|
||||||
|
|
||||||
|
mod-q
|
||||||
|
: Restart xmonad
|
||||||
|
|
||||||
|
mod-shift-slash
|
||||||
|
: Run xmessage with a summary of the default keybindings (useful for beginners)
|
||||||
|
|
||||||
|
mod-question
|
||||||
|
: Run xmessage with a summary of the default keybindings (useful for beginners)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
|
|
||||||
@@ -83,8 +174,16 @@ To use xmonad as your window manager add to your _~/.xinitrc_ file:
|
|||||||
> exec xmonad
|
> exec xmonad
|
||||||
|
|
||||||
# Customization
|
# Customization
|
||||||
xmonad is customized in ~/.xmonad/xmonad.hs, and then restarted
|
xmonad is customized in your _xmonad.hs_, and then restarted with mod-q.
|
||||||
with mod-q.
|
You can choose where your configuration file lives by
|
||||||
|
|
||||||
|
1. Setting `XMONAD_DATA_DIR,` `XMONAD_CONFIG_DIR`, and
|
||||||
|
`XMONAD_CACHE_DIR`; _xmonad.hs_ is then expected to be in
|
||||||
|
`XMONAD_CONFIG_DIR`.
|
||||||
|
2. Creating _xmonad.hs_ in _~/.xmonad_.
|
||||||
|
3. Creating _xmonad.hs_ in `XDG_CONFIG_HOME`. Note that, in this
|
||||||
|
case, xmonad will use `XDG_DATA_HOME` and `XDG_CACHE_HOME` for its
|
||||||
|
data and cache directory respectively.
|
||||||
|
|
||||||
You can find many extensions to the core feature set in the xmonad-
|
You can find many extensions to the core feature set in the xmonad-
|
||||||
contrib package, available through your package manager or from
|
contrib package, available through your package manager or from
|
||||||
@@ -107,5 +206,5 @@ module was contained within xmonad or xmonad-contrib.
|
|||||||
# Bugs
|
# Bugs
|
||||||
Probably. If you find any, please report them to the [bugtracker]
|
Probably. If you find any, please report them to the [bugtracker]
|
||||||
|
|
||||||
[xmonad.org]: http://xmonad.org
|
[xmonad.org]: https://xmonad.org
|
||||||
[bugtracker]: https://github.com/xmonad/xmonad/issues
|
[bugtracker]: https://github.com/xmonad/xmonad/issues
|
||||||
|
119
man/xmonad.1.markdown.in
Normal file
119
man/xmonad.1.markdown.in
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
% XMONAD(1) Tiling Window Manager
|
||||||
|
%
|
||||||
|
% 27 October 2021
|
||||||
|
|
||||||
|
# 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
|
||||||
|
dynamically reconfigured. At any time windows are arranged so as to
|
||||||
|
maximize the use of screen real estate. All features of the window manager
|
||||||
|
are accessible purely from the keyboard: a mouse is entirely optional.
|
||||||
|
_xmonad_ is configured in Haskell, and custom layout algorithms may be
|
||||||
|
implemented by the user in config files. A principle of _xmonad_ is
|
||||||
|
predictability: the user should know in advance precisely the window
|
||||||
|
arrangement that will result from any action.
|
||||||
|
|
||||||
|
By default, _xmonad_ provides three layout algorithms: tall, wide and
|
||||||
|
fullscreen. In tall or wide mode, windows are tiled and arranged to prevent
|
||||||
|
overlap and maximize screen use. Sets of windows are grouped together on
|
||||||
|
virtual screens, and each screen retains its own layout, which may be
|
||||||
|
reconfigured dynamically. Multiple physical monitors are supported via
|
||||||
|
Xinerama, allowing simultaneous display of a number of screens.
|
||||||
|
|
||||||
|
By utilizing the expressivity of a modern functional language with a rich
|
||||||
|
static type system, _xmonad_ provides a complete, featureful window manager
|
||||||
|
in less than 1200 lines of code, with an emphasis on correctness and
|
||||||
|
robustness. Internal properties of the window manager are checked using a
|
||||||
|
combination of static guarantees provided by the type system, and
|
||||||
|
type-based automated testing. A benefit of this is that the code is simple
|
||||||
|
to understand, and easy to modify.
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
_xmonad_ places each window into a "workspace". Each workspace can have
|
||||||
|
any number of windows, which you can cycle though with mod-j and mod-k.
|
||||||
|
Windows are either displayed full screen, tiled horizontally, or tiled
|
||||||
|
vertically. You can toggle the layout mode with mod-space, which will cycle
|
||||||
|
through the available modes.
|
||||||
|
|
||||||
|
You can switch to workspace N with mod-N. For example, to switch to
|
||||||
|
workspace 5, you would press mod-5. Similarly, you can move the current
|
||||||
|
window to another workspace with mod-shift-N.
|
||||||
|
|
||||||
|
When running with multiple monitors (Xinerama), each screen has exactly 1
|
||||||
|
workspace visible. mod-{w,e,r} switch the focus between screens, while
|
||||||
|
shift-mod-{w,e,r} move the current window to that screen. When _xmonad_
|
||||||
|
starts, workspace 1 is on screen 1, workspace 2 is on screen 2, etc. When
|
||||||
|
switching workspaces to one that is already visible, the current and
|
||||||
|
visible workspaces are swapped.
|
||||||
|
|
||||||
|
## Flags
|
||||||
|
|
||||||
|
xmonad has several flags which you may pass to the executable.
|
||||||
|
These flags are:
|
||||||
|
|
||||||
|
--recompile
|
||||||
|
: Recompiles your _xmonad.hs_ configuration
|
||||||
|
|
||||||
|
--restart
|
||||||
|
: Causes the currently running _xmonad_ process to restart
|
||||||
|
|
||||||
|
--replace
|
||||||
|
: Replace the current window manager with xmonad
|
||||||
|
|
||||||
|
--version
|
||||||
|
: Display version of _xmonad_
|
||||||
|
|
||||||
|
--verbose-version
|
||||||
|
: Display detailed version of _xmonad_
|
||||||
|
|
||||||
|
## Default keyboard bindings
|
||||||
|
|
||||||
|
___KEYBINDINGS___
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
To use xmonad as your window manager add to your _~/.xinitrc_ file:
|
||||||
|
|
||||||
|
> exec xmonad
|
||||||
|
|
||||||
|
# Customization
|
||||||
|
xmonad is customized in your _xmonad.hs_, and then restarted with mod-q.
|
||||||
|
You can choose where your configuration file lives by
|
||||||
|
|
||||||
|
1. Setting `XMONAD_DATA_DIR,` `XMONAD_CONFIG_DIR`, and
|
||||||
|
`XMONAD_CACHE_DIR`; _xmonad.hs_ is then expected to be in
|
||||||
|
`XMONAD_CONFIG_DIR`.
|
||||||
|
2. Creating _xmonad.hs_ in _~/.xmonad_.
|
||||||
|
3. Creating _xmonad.hs_ in `XDG_CONFIG_HOME`. Note that, in this
|
||||||
|
case, xmonad will use `XDG_DATA_HOME` and `XDG_CACHE_HOME` for its
|
||||||
|
data and cache directory respectively.
|
||||||
|
|
||||||
|
You can find many extensions to the core feature set in the xmonad-
|
||||||
|
contrib package, available through your package manager or from
|
||||||
|
[xmonad.org].
|
||||||
|
|
||||||
|
## Modular Configuration
|
||||||
|
As of _xmonad-0.9_, any additional Haskell modules may be placed in
|
||||||
|
_~/.xmonad/lib/_ are available in GHC's searchpath. Hierarchical modules
|
||||||
|
are supported: for example, the file
|
||||||
|
_~/.xmonad/lib/XMonad/Stack/MyAdditions.hs_ could contain:
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
module XMonad.Stack.MyAdditions (function1) where
|
||||||
|
function1 = error "function1: Not implemented yet!"
|
||||||
|
```
|
||||||
|
|
||||||
|
Your xmonad.hs may then import XMonad.Stack.MyAdditions as if that
|
||||||
|
module was contained within xmonad or xmonad-contrib.
|
||||||
|
|
||||||
|
# Bugs
|
||||||
|
Probably. If you find any, please report them to the [bugtracker]
|
||||||
|
|
||||||
|
[xmonad.org]: https://xmonad.org
|
||||||
|
[bugtracker]: https://github.com/xmonad/xmonad/issues
|
@@ -123,13 +123,13 @@ myKeys conf@(XConfig {XMonad.modMask = modm}) = M.fromList $
|
|||||||
-- , ((modm , xK_b ), sendMessage ToggleStruts)
|
-- , ((modm , xK_b ), sendMessage ToggleStruts)
|
||||||
|
|
||||||
-- Quit xmonad
|
-- Quit xmonad
|
||||||
, ((modm .|. shiftMask, xK_q ), io (exitWith ExitSuccess))
|
, ((modm .|. shiftMask, xK_q ), io exitSuccess)
|
||||||
|
|
||||||
-- Restart xmonad
|
-- Restart xmonad
|
||||||
, ((modm , xK_q ), spawn "xmonad --recompile; xmonad --restart")
|
, ((modm , xK_q ), spawn "xmonad --recompile; xmonad --restart")
|
||||||
|
|
||||||
-- Run xmessage with a summary of the default keybindings (useful for beginners)
|
-- Run xmessage with a summary of the default keybindings (useful for beginners)
|
||||||
, ((modm .|. shiftMask, xK_slash ), spawn ("echo \"" ++ help ++ "\" | xmessage -file -"))
|
, ((modm .|. shiftMask, xK_slash ), xmessage help)
|
||||||
]
|
]
|
||||||
++
|
++
|
||||||
|
|
||||||
@@ -154,18 +154,18 @@ myKeys conf@(XConfig {XMonad.modMask = modm}) = M.fromList $
|
|||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
-- Mouse bindings: default actions bound to mouse events
|
-- Mouse bindings: default actions bound to mouse events
|
||||||
--
|
--
|
||||||
myMouseBindings (XConfig {XMonad.modMask = modm}) = M.fromList $
|
myMouseBindings (XConfig {XMonad.modMask = modm}) = M.fromList
|
||||||
|
|
||||||
-- mod-button1, Set the window to floating mode and move by dragging
|
-- mod-button1, Set the window to floating mode and move by dragging
|
||||||
[ ((modm, button1), (\w -> focus w >> mouseMoveWindow w
|
[ ((modm, button1), \w -> focus w >> mouseMoveWindow w
|
||||||
>> windows W.shiftMaster))
|
>> windows W.shiftMaster)
|
||||||
|
|
||||||
-- mod-button2, Raise the window to the top of the stack
|
-- mod-button2, Raise the window to the top of the stack
|
||||||
, ((modm, button2), (\w -> focus w >> windows W.shiftMaster))
|
, ((modm, button2), \w -> focus w >> windows W.shiftMaster)
|
||||||
|
|
||||||
-- mod-button3, Set the window to floating mode and resize by dragging
|
-- mod-button3, Set the window to floating mode and resize by dragging
|
||||||
, ((modm, button3), (\w -> focus w >> mouseResizeWindow w
|
, ((modm, button3), \w -> focus w >> mouseResizeWindow w
|
||||||
>> windows W.shiftMaster))
|
>> windows W.shiftMaster)
|
||||||
|
|
||||||
-- you may also bind events to the mouse scroll wheel (button4 and button5)
|
-- you may also bind events to the mouse scroll wheel (button4 and button5)
|
||||||
]
|
]
|
||||||
@@ -293,6 +293,7 @@ help = unlines ["The default modifier key is 'alt'. Default keybindings:",
|
|||||||
"mod-Space Rotate through the available layout algorithms",
|
"mod-Space Rotate through the available layout algorithms",
|
||||||
"mod-Shift-Space Reset the layouts on the current workSpace to default",
|
"mod-Shift-Space Reset the layouts on the current workSpace to default",
|
||||||
"mod-n Resize/refresh viewed windows to the correct size",
|
"mod-n Resize/refresh viewed windows to the correct size",
|
||||||
|
"mod-Shift-/ Show this help message with the default keybindings",
|
||||||
"",
|
"",
|
||||||
"-- move focus up or down the window stack",
|
"-- move focus up or down the window stack",
|
||||||
"mod-Tab Move focus to the next window",
|
"mod-Tab Move focus to the next window",
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-orphans #-}
|
{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-orphans #-}
|
||||||
{-# LANGUAGE TypeFamilies #-}
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Config
|
-- Module : XMonad.Config
|
||||||
@@ -39,7 +40,7 @@ import XMonad.Operations
|
|||||||
import XMonad.ManageHook
|
import XMonad.ManageHook
|
||||||
import qualified XMonad.StackSet as W
|
import qualified XMonad.StackSet as W
|
||||||
import Data.Bits ((.|.))
|
import Data.Bits ((.|.))
|
||||||
import Data.Default
|
import Data.Default.Class
|
||||||
import Data.Monoid
|
import Data.Monoid
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import System.Exit
|
import System.Exit
|
||||||
@@ -218,7 +219,7 @@ keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
|||||||
, ((modMask , xK_period), sendMessage (IncMasterN (-1))) -- %! Deincrement the number of windows in the master area
|
, ((modMask , xK_period), sendMessage (IncMasterN (-1))) -- %! Deincrement the number of windows in the master area
|
||||||
|
|
||||||
-- quit, or restart
|
-- quit, or restart
|
||||||
, ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess)) -- %! Quit xmonad
|
, ((modMask .|. shiftMask, xK_q ), io 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 , 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 ), helpCommand) -- %! 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)
|
||||||
@@ -239,7 +240,7 @@ keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
|||||||
, (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
|
, (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
|
||||||
where
|
where
|
||||||
helpCommand :: X ()
|
helpCommand :: X ()
|
||||||
helpCommand = spawn ("echo " ++ show help ++ " | xmessage -file -")
|
helpCommand = xmessage help
|
||||||
|
|
||||||
-- | Mouse bindings: default actions bound to mouse events
|
-- | Mouse bindings: default actions bound to mouse events
|
||||||
mouseBindings :: XConfig Layout -> M.Map (KeyMask, Button) (Window -> X ())
|
mouseBindings :: XConfig Layout -> M.Map (KeyMask, Button) (Window -> X ())
|
||||||
@@ -277,6 +278,7 @@ instance (a ~ Choose Tall (Choose (Mirror Tall) Full)) => Default (XConfig a) wh
|
|||||||
, XMonad.handleExtraArgs = \ xs theConf -> case xs of
|
, XMonad.handleExtraArgs = \ xs theConf -> case xs of
|
||||||
[] -> return theConf
|
[] -> return theConf
|
||||||
_ -> fail ("unrecognized flags:" ++ show xs)
|
_ -> fail ("unrecognized flags:" ++ show xs)
|
||||||
|
, XMonad.extensibleConf = M.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
-- | The default set of configuration values itself
|
-- | The default set of configuration values itself
|
||||||
@@ -296,6 +298,7 @@ help = unlines ["The default modifier key is 'alt'. Default keybindings:",
|
|||||||
"mod-Space Rotate through the available layout algorithms",
|
"mod-Space Rotate through the available layout algorithms",
|
||||||
"mod-Shift-Space Reset the layouts on the current workSpace to default",
|
"mod-Shift-Space Reset the layouts on the current workSpace to default",
|
||||||
"mod-n Resize/refresh viewed windows to the correct size",
|
"mod-n Resize/refresh viewed windows to the correct size",
|
||||||
|
"mod-Shift-/ Show this help message with the default keybindings",
|
||||||
"",
|
"",
|
||||||
"-- move focus up or down the window stack",
|
"-- move focus up or down the window stack",
|
||||||
"mod-Tab Move focus to the next window",
|
"mod-Tab Move focus to the next window",
|
||||||
|
@@ -1,5 +1,12 @@
|
|||||||
{-# LANGUAGE ExistentialQuantification, FlexibleInstances, GeneralizedNewtypeDeriving,
|
{-# LANGUAGE CPP #-}
|
||||||
MultiParamTypeClasses, TypeSynonymInstances, DeriveDataTypeable #-}
|
{-# LANGUAGE DeriveTraversable #-}
|
||||||
|
{-# LANGUAGE ExistentialQuantification #-}
|
||||||
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||||
|
{-# LANGUAGE LambdaCase #-}
|
||||||
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
@@ -22,26 +29,31 @@ module XMonad.Core (
|
|||||||
XConf(..), XConfig(..), LayoutClass(..),
|
XConf(..), XConfig(..), LayoutClass(..),
|
||||||
Layout(..), readsLayout, Typeable, Message,
|
Layout(..), readsLayout, Typeable, Message,
|
||||||
SomeMessage(..), fromMessage, LayoutMessages(..),
|
SomeMessage(..), fromMessage, LayoutMessages(..),
|
||||||
StateExtension(..), ExtensionClass(..),
|
StateExtension(..), ExtensionClass(..), ConfExtension(..),
|
||||||
runX, catchX, userCode, userCodeDef, io, catchIO, installSignalHandlers, uninstallSignalHandlers,
|
runX, catchX, userCode, userCodeDef, io, catchIO, installSignalHandlers, uninstallSignalHandlers,
|
||||||
withDisplay, withWindowSet, isRoot, runOnWorkspaces,
|
withDisplay, withWindowSet, isRoot, runOnWorkspaces,
|
||||||
getAtom, spawn, spawnPID, xfork, recompile, trace, whenJust, whenX,
|
getAtom, spawn, spawnPID, xfork, xmessage, recompile, trace, whenJust, whenX,
|
||||||
getXMonadDir, getXMonadCacheDir, getXMonadDataDir, stateFileName,
|
getXMonadDir, getXMonadCacheDir, getXMonadDataDir, stateFileName, binFileName,
|
||||||
atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, atom_WM_TAKE_FOCUS, withWindowAttributes,
|
atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, atom_WM_TAKE_FOCUS, withWindowAttributes,
|
||||||
ManageHook, Query(..), runQuery
|
ManageHook, Query(..), runQuery, Directories'(..), Directories, getDirectories,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import XMonad.StackSet hiding (modify)
|
import XMonad.StackSet hiding (modify)
|
||||||
|
|
||||||
import Prelude
|
import Prelude
|
||||||
import Control.Exception.Extensible (fromException, try, bracket, throw, finally, SomeException(..))
|
import Control.Exception (fromException, try, bracket_, throw, finally, SomeException(..))
|
||||||
import qualified Control.Exception.Extensible as E
|
import qualified Control.Exception as E
|
||||||
import Control.Applicative(Applicative, pure, (<$>), (<*>))
|
import Control.Applicative ((<|>), empty)
|
||||||
import Control.Monad.Fail
|
import Control.Monad.Fail
|
||||||
|
import Control.Monad.Fix (fix)
|
||||||
import Control.Monad.State
|
import Control.Monad.State
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
|
import Control.Monad (filterM, guard, liftM2, void, when)
|
||||||
import Data.Semigroup
|
import Data.Semigroup
|
||||||
import Data.Default
|
import Data.Traversable (for)
|
||||||
|
import Data.Time.Clock (UTCTime)
|
||||||
|
import Data.Default.Class
|
||||||
|
import System.Environment (lookupEnv)
|
||||||
import System.FilePath
|
import System.FilePath
|
||||||
import System.IO
|
import System.IO
|
||||||
import System.Info
|
import System.Info
|
||||||
@@ -56,10 +68,8 @@ import System.Exit
|
|||||||
import Graphics.X11.Xlib
|
import Graphics.X11.Xlib
|
||||||
import Graphics.X11.Xlib.Extras (getWindowAttributes, WindowAttributes, Event)
|
import Graphics.X11.Xlib.Extras (getWindowAttributes, WindowAttributes, Event)
|
||||||
import Data.Typeable
|
import Data.Typeable
|
||||||
import Data.List ((\\))
|
import Data.List (isInfixOf, (\\))
|
||||||
import Data.Maybe (isJust,fromMaybe)
|
import Data.Maybe (isJust,fromMaybe)
|
||||||
import Data.Monoid hiding ((<>))
|
|
||||||
import System.Environment (lookupEnv)
|
|
||||||
|
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
@@ -93,8 +103,8 @@ data XConf = XConf
|
|||||||
, mousePosition :: !(Maybe (Position, Position))
|
, mousePosition :: !(Maybe (Position, Position))
|
||||||
-- ^ position of the mouse according to
|
-- ^ position of the mouse according to
|
||||||
-- the event currently being processed
|
-- the event currently being processed
|
||||||
, currentEvent :: !(Maybe Event)
|
, currentEvent :: !(Maybe Event) -- ^ event currently being processed
|
||||||
-- ^ event currently being processed
|
, directories :: !Directories -- ^ directories to use
|
||||||
}
|
}
|
||||||
|
|
||||||
-- todo, better name
|
-- todo, better name
|
||||||
@@ -122,6 +132,11 @@ data XConfig l = XConfig
|
|||||||
, rootMask :: !EventMask -- ^ The root events that xmonad is interested in
|
, rootMask :: !EventMask -- ^ The root events that xmonad is interested in
|
||||||
, handleExtraArgs :: !([String] -> XConfig Layout -> IO (XConfig Layout))
|
, handleExtraArgs :: !([String] -> XConfig Layout -> IO (XConfig Layout))
|
||||||
-- ^ Modify the configuration, complain about extra arguments etc. with arguments that are not handled by default
|
-- ^ Modify the configuration, complain about extra arguments etc. with arguments that are not handled by default
|
||||||
|
, extensibleConf :: !(M.Map TypeRep ConfExtension)
|
||||||
|
-- ^ Stores custom config information.
|
||||||
|
--
|
||||||
|
-- The module "XMonad.Util.ExtensibleConf" in xmonad-contrib
|
||||||
|
-- provides additional information and a simple interface for using this.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -135,7 +150,8 @@ type WorkspaceId = String
|
|||||||
newtype ScreenId = S Int deriving (Eq,Ord,Show,Read,Enum,Num,Integral,Real)
|
newtype ScreenId = S Int deriving (Eq,Ord,Show,Read,Enum,Num,Integral,Real)
|
||||||
|
|
||||||
-- | The 'Rectangle' with screen dimensions
|
-- | The 'Rectangle' with screen dimensions
|
||||||
data ScreenDetail = SD { screenRect :: !Rectangle } deriving (Eq,Show, Read)
|
newtype ScreenDetail = SD { screenRect :: Rectangle }
|
||||||
|
deriving (Eq,Show, Read)
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -148,18 +164,13 @@ data ScreenDetail = SD { screenRect :: !Rectangle } deriving (Eq,Show, Read)
|
|||||||
-- instantiated on 'XConf' and 'XState' automatically.
|
-- instantiated on 'XConf' and 'XState' automatically.
|
||||||
--
|
--
|
||||||
newtype X a = X (ReaderT XConf (StateT XState IO) a)
|
newtype X a = X (ReaderT XConf (StateT XState IO) a)
|
||||||
deriving (Functor, Monad, MonadFail, MonadIO, MonadState XState, MonadReader XConf, Typeable)
|
deriving (Functor, Applicative, Monad, MonadFail, MonadIO, MonadState XState, MonadReader XConf)
|
||||||
|
|
||||||
instance Applicative X where
|
|
||||||
pure = return
|
|
||||||
(<*>) = ap
|
|
||||||
|
|
||||||
instance Semigroup a => Semigroup (X a) where
|
instance Semigroup a => Semigroup (X a) where
|
||||||
(<>) = liftM2 (<>)
|
(<>) = liftM2 (<>)
|
||||||
|
|
||||||
instance (Monoid a) => Monoid (X a) where
|
instance (Monoid a) => Monoid (X a) where
|
||||||
mempty = return mempty
|
mempty = pure mempty
|
||||||
mappend = liftM2 mappend
|
|
||||||
|
|
||||||
instance Default a => Default (X a) where
|
instance Default a => Default (X a) where
|
||||||
def = return def
|
def = return def
|
||||||
@@ -169,14 +180,13 @@ newtype Query a = Query (ReaderT Window X a)
|
|||||||
deriving (Functor, Applicative, Monad, MonadReader Window, MonadIO)
|
deriving (Functor, Applicative, Monad, MonadReader Window, MonadIO)
|
||||||
|
|
||||||
runQuery :: Query a -> Window -> X a
|
runQuery :: Query a -> Window -> X a
|
||||||
runQuery (Query m) w = runReaderT m w
|
runQuery (Query m) = runReaderT m
|
||||||
|
|
||||||
instance Semigroup a => Semigroup (Query a) where
|
instance Semigroup a => Semigroup (Query a) where
|
||||||
(<>) = liftM2 (<>)
|
(<>) = liftM2 (<>)
|
||||||
|
|
||||||
instance Monoid a => Monoid (Query a) where
|
instance Monoid a => Monoid (Query a) where
|
||||||
mempty = return mempty
|
mempty = pure mempty
|
||||||
mappend = liftM2 mappend
|
|
||||||
|
|
||||||
instance Default a => Default (Query a) where
|
instance Default a => Default (Query a) where
|
||||||
def = return def
|
def = return def
|
||||||
@@ -193,7 +203,7 @@ catchX job errcase = do
|
|||||||
st <- get
|
st <- get
|
||||||
c <- ask
|
c <- ask
|
||||||
(a, s') <- io $ runX c st job `E.catch` \e -> case fromException e of
|
(a, s') <- io $ runX c st job `E.catch` \e -> case fromException e of
|
||||||
Just x -> throw e `const` (x `asTypeOf` ExitSuccess)
|
Just (_ :: ExitCode) -> throw e
|
||||||
_ -> do hPrint stderr e; runX c st errcase
|
_ -> do hPrint stderr e; runX c st errcase
|
||||||
put s'
|
put s'
|
||||||
return a
|
return a
|
||||||
@@ -201,12 +211,12 @@ catchX job errcase = do
|
|||||||
-- | Execute the argument, catching all exceptions. Either this function or
|
-- | Execute the argument, catching all exceptions. Either this function or
|
||||||
-- 'catchX' should be used at all callsites of user customized code.
|
-- 'catchX' should be used at all callsites of user customized code.
|
||||||
userCode :: X a -> X (Maybe a)
|
userCode :: X a -> X (Maybe a)
|
||||||
userCode a = catchX (Just `liftM` a) (return Nothing)
|
userCode a = catchX (Just <$> a) (return Nothing)
|
||||||
|
|
||||||
-- | Same as userCode but with a default argument to return instead of using
|
-- | Same as userCode but with a default argument to return instead of using
|
||||||
-- Maybe, provided for convenience.
|
-- Maybe, provided for convenience.
|
||||||
userCodeDef :: a -> X a -> X a
|
userCodeDef :: a -> X a -> X a
|
||||||
userCodeDef defValue a = fromMaybe defValue `liftM` userCode a
|
userCodeDef defValue a = fromMaybe defValue <$> userCode a
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
-- Convenient wrappers to state
|
-- Convenient wrappers to state
|
||||||
@@ -227,7 +237,7 @@ withWindowAttributes dpy win f = do
|
|||||||
|
|
||||||
-- | True if the given window is the root window
|
-- | True if the given window is the root window
|
||||||
isRoot :: Window -> X Bool
|
isRoot :: Window -> X Bool
|
||||||
isRoot w = (w==) <$> asks theRoot
|
isRoot w = asks $ (w ==) . theRoot
|
||||||
|
|
||||||
-- | Wrapper for the common case of atom internment
|
-- | Wrapper for the common case of atom internment
|
||||||
getAtom :: String -> X Atom
|
getAtom :: String -> X Atom
|
||||||
@@ -255,14 +265,18 @@ readsLayout (Layout l) s = [(Layout (asTypeOf x l), rs) | (x, rs) <- reads s]
|
|||||||
-- | Every layout must be an instance of 'LayoutClass', which defines
|
-- | Every layout must be an instance of 'LayoutClass', which defines
|
||||||
-- the basic layout operations along with a sensible default for each.
|
-- the basic layout operations along with a sensible default for each.
|
||||||
--
|
--
|
||||||
-- Minimal complete definition:
|
-- All of the methods have default implementations, so there is no
|
||||||
|
-- minimal complete definition. They do, however, have a dependency
|
||||||
|
-- structure by default; this is something to be aware of should you
|
||||||
|
-- choose to implement one of these methods. Here is how a minimal
|
||||||
|
-- complete definition would look like if we did not provide any default
|
||||||
|
-- implementations:
|
||||||
--
|
--
|
||||||
-- * 'runLayout' || (('doLayout' || 'pureLayout') && 'emptyLayout'), and
|
-- * 'runLayout' || (('doLayout' || 'pureLayout') && 'emptyLayout')
|
||||||
--
|
--
|
||||||
-- * 'handleMessage' || 'pureMessage'
|
-- * 'handleMessage' || 'pureMessage'
|
||||||
--
|
--
|
||||||
-- You should also strongly consider implementing 'description',
|
-- * 'description'
|
||||||
-- although it is not required.
|
|
||||||
--
|
--
|
||||||
-- Note that any code which /uses/ 'LayoutClass' methods should only
|
-- Note that any code which /uses/ 'LayoutClass' methods should only
|
||||||
-- ever call 'runLayout', 'handleMessage', and 'description'! In
|
-- ever call 'runLayout', 'handleMessage', and 'description'! In
|
||||||
@@ -271,7 +285,7 @@ readsLayout (Layout l) s = [(Layout (asTypeOf x l), rs) | (x, rs) <- reads s]
|
|||||||
-- 'runLayout', 'handleMessage', and so on. This ensures that the
|
-- 'runLayout', 'handleMessage', and so on. This ensures that the
|
||||||
-- proper methods will be used, regardless of the particular methods
|
-- proper methods will be used, regardless of the particular methods
|
||||||
-- that any 'LayoutClass' instance chooses to define.
|
-- that any 'LayoutClass' instance chooses to define.
|
||||||
class Show (layout a) => LayoutClass layout a where
|
class (Show (layout a), Typeable layout) => LayoutClass layout a where
|
||||||
|
|
||||||
-- | By default, 'runLayout' calls 'doLayout' if there are any
|
-- | By default, 'runLayout' calls 'doLayout' if there are any
|
||||||
-- windows to be laid out, and 'emptyLayout' otherwise. Most
|
-- windows to be laid out, and 'emptyLayout' otherwise. Most
|
||||||
@@ -373,12 +387,12 @@ instance Message Event
|
|||||||
-- layouts) should consider handling.
|
-- layouts) should consider handling.
|
||||||
data LayoutMessages = Hide -- ^ sent when a layout becomes non-visible
|
data LayoutMessages = Hide -- ^ sent when a layout becomes non-visible
|
||||||
| ReleaseResources -- ^ sent when xmonad is exiting or restarting
|
| ReleaseResources -- ^ sent when xmonad is exiting or restarting
|
||||||
deriving (Typeable, Eq)
|
deriving Eq
|
||||||
|
|
||||||
instance Message LayoutMessages
|
instance Message LayoutMessages
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
-- Extensible state
|
-- Extensible state/config
|
||||||
--
|
--
|
||||||
|
|
||||||
-- | Every module must make the data it wants to store
|
-- | Every module must make the data it wants to store
|
||||||
@@ -386,6 +400,7 @@ instance Message LayoutMessages
|
|||||||
--
|
--
|
||||||
-- Minimal complete definition: initialValue
|
-- Minimal complete definition: initialValue
|
||||||
class Typeable a => ExtensionClass a where
|
class Typeable a => ExtensionClass a where
|
||||||
|
{-# MINIMAL initialValue #-}
|
||||||
-- | Defines an initial value for the state extension
|
-- | Defines an initial value for the state extension
|
||||||
initialValue :: a
|
initialValue :: a
|
||||||
-- | Specifies whether the state extension should be
|
-- | Specifies whether the state extension should be
|
||||||
@@ -404,6 +419,9 @@ data StateExtension =
|
|||||||
| forall a. (Read a, Show a, ExtensionClass a) => PersistentExtension a
|
| forall a. (Read a, Show a, ExtensionClass a) => PersistentExtension a
|
||||||
-- ^ Persistent extension
|
-- ^ Persistent extension
|
||||||
|
|
||||||
|
-- | Existential type to store a config extension.
|
||||||
|
data ConfExtension = forall a. Typeable a => ConfExtension a
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
-- | General utilities
|
-- | General utilities
|
||||||
--
|
--
|
||||||
@@ -421,7 +439,7 @@ catchIO f = io (f `E.catch` \(SomeException e) -> hPrint stderr e >> hFlush stde
|
|||||||
--
|
--
|
||||||
-- Note this function assumes your locale uses utf8.
|
-- Note this function assumes your locale uses utf8.
|
||||||
spawn :: MonadIO m => String -> m ()
|
spawn :: MonadIO m => String -> m ()
|
||||||
spawn x = spawnPID x >> return ()
|
spawn x = void $ spawnPID x
|
||||||
|
|
||||||
-- | Like 'spawn', but returns the 'ProcessID' of the launched application
|
-- | Like 'spawn', but returns the 'ProcessID' of the launched application
|
||||||
spawnPID :: MonadIO m => String -> m ProcessID
|
spawnPID :: MonadIO m => String -> m ProcessID
|
||||||
@@ -435,10 +453,25 @@ xfork x = io . forkProcess . finally nullStdin $ do
|
|||||||
x
|
x
|
||||||
where
|
where
|
||||||
nullStdin = do
|
nullStdin = do
|
||||||
|
#if MIN_VERSION_unix(2,8,0)
|
||||||
|
fd <- openFd "/dev/null" ReadOnly defaultFileFlags
|
||||||
|
#else
|
||||||
fd <- openFd "/dev/null" ReadOnly Nothing defaultFileFlags
|
fd <- openFd "/dev/null" ReadOnly Nothing defaultFileFlags
|
||||||
|
#endif
|
||||||
dupTo fd stdInput
|
dupTo fd stdInput
|
||||||
closeFd fd
|
closeFd fd
|
||||||
|
|
||||||
|
-- | Use @xmessage@ to show information to the user.
|
||||||
|
xmessage :: MonadIO m => String -> m ()
|
||||||
|
xmessage msg = void . xfork $ do
|
||||||
|
xmessageBin <- fromMaybe "xmessage" <$> liftIO (lookupEnv "XMONAD_XMESSAGE")
|
||||||
|
executeFile xmessageBin True
|
||||||
|
[ "-default", "okay"
|
||||||
|
, "-xrm", "*international:true"
|
||||||
|
, "-xrm", "*fontSet:-*-fixed-medium-r-normal-*-18-*-*-*-*-*-*-*,-*-fixed-*-*-*-*-18-*-*-*-*-*-*-*,-*-*-*-*-*-*-18-*-*-*-*-*-*-*"
|
||||||
|
, msg
|
||||||
|
] Nothing
|
||||||
|
|
||||||
-- | This is basically a map function, running a function in the 'X' monad on
|
-- | This is basically a map function, running a function in the 'X' monad on
|
||||||
-- each workspace with the output of that function being the modified workspace.
|
-- each workspace with the output of that function being the modified workspace.
|
||||||
runOnWorkspaces :: (WindowSpace -> X WindowSpace) -> X ()
|
runOnWorkspaces :: (WindowSpace -> X WindowSpace) -> X ()
|
||||||
@@ -449,137 +482,255 @@ runOnWorkspaces job = do
|
|||||||
$ current ws : visible ws
|
$ current ws : visible ws
|
||||||
modify $ \s -> s { windowset = ws { current = c, visible = v, hidden = h } }
|
modify $ \s -> s { windowset = ws { current = c, visible = v, hidden = h } }
|
||||||
|
|
||||||
-- | Return the path to the xmonad configuration directory. This
|
-- | All the directories that xmonad will use. They will be used for
|
||||||
-- directory is where user configuration files are stored (e.g, the
|
-- the following purposes:
|
||||||
-- 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
|
-- * @dataDir@: This directory is used by XMonad to store data files
|
||||||
-- preference:
|
-- such as the run-time state file.
|
||||||
--
|
--
|
||||||
-- 1. The directory specified in the @XMONAD_CONFIG_DIR@ environment variable.
|
-- * @cfgDir@: This directory is where user configuration files are
|
||||||
-- 2. The @~\/.xmonad@ directory.
|
-- stored (e.g, the xmonad.hs file). You may also create a @lib@
|
||||||
-- 3. The @XDG_CONFIG_HOME/xmonad@ directory.
|
-- subdirectory in the configuration directory and the default recompile
|
||||||
|
-- command will add it to the GHC include path.
|
||||||
--
|
--
|
||||||
-- The first directory that exists will be used. If none of the
|
-- * @cacheDir@: This directory is used to store temporary files that
|
||||||
-- directories exist then (1) will be used if it is set, otherwise (2)
|
-- can easily be recreated such as the configuration binary and any
|
||||||
-- will be used. Either way, a directory will be created if necessary.
|
-- intermediate object files generated by GHC.
|
||||||
getXMonadDir :: MonadIO m => m String
|
-- Also, the XPrompt history file goes here.
|
||||||
getXMonadDir =
|
--
|
||||||
findFirstDirWithEnv "XMONAD_CONFIG_DIR"
|
-- For how these directories are chosen, see 'getDirectories'.
|
||||||
[ getAppUserDataDirectory "xmonad"
|
--
|
||||||
, getXDGDirectory XDGConfig "xmonad"
|
data Directories' a = Directories
|
||||||
]
|
{ dataDir :: !a
|
||||||
|
, cfgDir :: !a
|
||||||
|
, cacheDir :: !a
|
||||||
|
}
|
||||||
|
deriving (Show, Functor, Foldable, Traversable)
|
||||||
|
|
||||||
-- | Return the path to the xmonad cache directory. This directory is
|
-- | Convenient type alias for the most common case in which one might
|
||||||
-- used to store temporary files that can easily be recreated. For
|
-- want to use the 'Directories' type.
|
||||||
-- example, the XPrompt history file.
|
type Directories = Directories' FilePath
|
||||||
--
|
|
||||||
-- 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
|
-- | Build up the 'Dirs' that xmonad will use. They are chosen as
|
||||||
-- used by XMonad to store data files such as the run-time state file
|
-- follows:
|
||||||
-- and the configuration binary generated by GHC.
|
|
||||||
--
|
--
|
||||||
-- Several directories are considered. In order of preference:
|
-- 1. If all three of xmonad's environment variables (@XMONAD_DATA_DIR@,
|
||||||
|
-- @XMONAD_CONFIG_DIR@, and @XMONAD_CACHE_DIR@) are set, use them.
|
||||||
|
-- 2. If there is a build script called @build@ or configuration
|
||||||
|
-- @xmonad.hs@ in @~\/.xmonad@, set all three directories to
|
||||||
|
-- @~\/.xmonad@.
|
||||||
|
-- 3. Otherwise, use the @xmonad@ directory in @XDG_DATA_HOME@,
|
||||||
|
-- @XDG_CONFIG_HOME@, and @XDG_CACHE_HOME@ (or their respective
|
||||||
|
-- fallbacks). These directories are created if necessary.
|
||||||
--
|
--
|
||||||
-- 1. The directory specified in the @XMONAD_DATA_DIR@ environment variable.
|
-- The xmonad configuration file (or the build script, if present) is
|
||||||
-- 2. The @~\/.xmonad@ directory.
|
-- always assumed to be in @cfgDir@.
|
||||||
-- 3. The @XDG_DATA_HOME/xmonad@ directory.
|
|
||||||
--
|
--
|
||||||
-- The first directory that exists will be used. If none of the
|
getDirectories :: IO Directories
|
||||||
-- directories exist then (1) will be used if it is set, otherwise (2)
|
getDirectories = xmEnvDirs <|> xmDirs <|> xdgDirs
|
||||||
-- 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
|
where
|
||||||
go [] = return Nothing
|
-- | Check for xmonad's environment variables first
|
||||||
go (x:xs) = do
|
xmEnvDirs :: IO Directories
|
||||||
dir <- io x
|
xmEnvDirs = do
|
||||||
exists <- io (doesDirectoryExist dir)
|
let xmEnvs = Directories{ dataDir = "XMONAD_DATA_DIR"
|
||||||
if exists then return (Just dir) else go xs
|
, cfgDir = "XMONAD_CONFIG_DIR"
|
||||||
|
, cacheDir = "XMONAD_CACHE_DIR"
|
||||||
|
}
|
||||||
|
maybe empty pure . sequenceA =<< traverse getEnv xmEnvs
|
||||||
|
|
||||||
-- | Simple wrapper around @findFirstDirOf@ that allows the primary
|
-- | Check whether the config file or a build script is in the
|
||||||
-- path to be specified by an environment variable.
|
-- @~\/.xmonad@ directory
|
||||||
findFirstDirWithEnv :: MonadIO m => String -> [IO FilePath] -> m FilePath
|
xmDirs :: IO Directories
|
||||||
findFirstDirWithEnv envName paths = do
|
xmDirs = do
|
||||||
envPath' <- io (getEnv envName)
|
xmDir <- getAppUserDataDirectory "xmonad"
|
||||||
|
conf <- doesFileExist $ xmDir </> "xmonad.hs"
|
||||||
|
build <- doesFileExist $ xmDir </> "build"
|
||||||
|
|
||||||
case envPath' of
|
-- Place *everything* in ~/.xmonad if yes
|
||||||
Nothing -> findFirstDirOf paths
|
guard $ conf || build
|
||||||
Just envPath -> findFirstDirOf (return envPath:paths)
|
pure Directories{ dataDir = xmDir, cfgDir = xmDir, cacheDir = xmDir }
|
||||||
|
|
||||||
-- | Helper function to retrieve the various XDG directories.
|
-- | Use XDG directories as a fallback
|
||||||
-- This has been based on the implementation shipped with GHC version 8.0.1 or
|
xdgDirs :: IO Directories
|
||||||
-- higher. Put here to preserve compatibility with older GHC versions.
|
xdgDirs =
|
||||||
getXDGDirectory :: XDGDirectory -> FilePath -> IO FilePath
|
for Directories{ dataDir = XdgData, cfgDir = XdgConfig, cacheDir = XdgCache }
|
||||||
getXDGDirectory xdgDir suffix =
|
$ \dir -> do d <- getXdgDirectory dir "xmonad"
|
||||||
normalise . (</> suffix) <$>
|
d <$ createDirectoryIfMissing True d
|
||||||
case xdgDir of
|
|
||||||
XDGData -> get "XDG_DATA_HOME" ".local/share"
|
-- | Return the path to the xmonad configuration directory.
|
||||||
XDGConfig -> get "XDG_CONFIG_HOME" ".config"
|
getXMonadDir :: X String
|
||||||
XDGCache -> get "XDG_CACHE_HOME" ".cache"
|
getXMonadDir = asks (cfgDir . directories)
|
||||||
|
{-# DEPRECATED getXMonadDir "Use `asks (cfgDir . directories)' instead." #-}
|
||||||
|
|
||||||
|
-- | Return the path to the xmonad cache directory.
|
||||||
|
getXMonadCacheDir :: X String
|
||||||
|
getXMonadCacheDir = asks (cacheDir . directories)
|
||||||
|
{-# DEPRECATED getXMonadCacheDir "Use `asks (cacheDir . directories)' instead." #-}
|
||||||
|
|
||||||
|
-- | Return the path to the xmonad data directory.
|
||||||
|
getXMonadDataDir :: X String
|
||||||
|
getXMonadDataDir = asks (dataDir . directories)
|
||||||
|
{-# DEPRECATED getXMonadDataDir "Use `asks (dataDir . directories)' instead." #-}
|
||||||
|
|
||||||
|
binFileName, buildDirName :: Directories -> FilePath
|
||||||
|
binFileName Directories{ cacheDir } = cacheDir </> "xmonad-" <> arch <> "-" <> os
|
||||||
|
buildDirName Directories{ cacheDir } = cacheDir </> "build-" <> arch <> "-" <> os
|
||||||
|
|
||||||
|
errFileName, stateFileName :: Directories -> FilePath
|
||||||
|
errFileName Directories{ dataDir } = dataDir </> "xmonad.errors"
|
||||||
|
stateFileName Directories{ dataDir } = dataDir </> "xmonad.state"
|
||||||
|
|
||||||
|
srcFileName, libFileName :: Directories -> FilePath
|
||||||
|
srcFileName Directories{ cfgDir } = cfgDir </> "xmonad.hs"
|
||||||
|
libFileName Directories{ cfgDir } = cfgDir </> "lib"
|
||||||
|
|
||||||
|
buildScriptFileName, stackYamlFileName :: Directories -> FilePath
|
||||||
|
buildScriptFileName Directories{ cfgDir } = cfgDir </> "build"
|
||||||
|
stackYamlFileName Directories{ cfgDir } = cfgDir </> "stack.yaml"
|
||||||
|
|
||||||
|
-- | Compilation method for xmonad configuration.
|
||||||
|
data Compile = CompileGhc | CompileStackGhc FilePath | CompileScript FilePath
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
|
-- | Detect compilation method by looking for known file names in xmonad
|
||||||
|
-- configuration directory.
|
||||||
|
detectCompile :: Directories -> IO Compile
|
||||||
|
detectCompile dirs = tryScript <|> tryStack <|> useGhc
|
||||||
where
|
where
|
||||||
get name fallback = do
|
buildScript = buildScriptFileName dirs
|
||||||
env <- lookupEnv name
|
stackYaml = stackYamlFileName dirs
|
||||||
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.
|
tryScript = do
|
||||||
stateFileName :: (Functor m, MonadIO m) => m FilePath
|
guard =<< doesFileExist buildScript
|
||||||
stateFileName = (</> "xmonad.state") <$> getXMonadDataDir
|
isExe <- isExecutable buildScript
|
||||||
|
if isExe
|
||||||
|
then do
|
||||||
|
trace $ "XMonad will use build script at " <> show buildScript <> " to recompile."
|
||||||
|
pure $ CompileScript buildScript
|
||||||
|
else do
|
||||||
|
trace $ "XMonad will not use build script, because " <> show buildScript <> " is not executable."
|
||||||
|
trace $ "Suggested resolution to use it: chmod u+x " <> show buildScript
|
||||||
|
empty
|
||||||
|
|
||||||
-- | 'recompile force', recompile the xmonad configuration file when
|
tryStack = do
|
||||||
-- any of the following apply:
|
guard =<< doesFileExist stackYaml
|
||||||
|
canonStackYaml <- canonicalizePath stackYaml
|
||||||
|
trace $ "XMonad will use stack ghc --stack-yaml " <> show canonStackYaml <> " to recompile."
|
||||||
|
pure $ CompileStackGhc canonStackYaml
|
||||||
|
|
||||||
|
useGhc = do
|
||||||
|
trace $ "XMonad will use ghc to recompile, because neither "
|
||||||
|
<> show buildScript <> " nor " <> show stackYaml <> " exists."
|
||||||
|
pure CompileGhc
|
||||||
|
|
||||||
|
isExecutable f = E.catch (executable <$> getPermissions f) (\(SomeException _) -> return False)
|
||||||
|
|
||||||
|
-- | Should we recompile xmonad configuration? Is it newer than the compiled
|
||||||
|
-- binary?
|
||||||
|
shouldCompile :: Directories -> Compile -> IO Bool
|
||||||
|
shouldCompile dirs CompileGhc = do
|
||||||
|
libTs <- mapM getModTime . Prelude.filter isSource =<< allFiles (libFileName dirs)
|
||||||
|
srcT <- getModTime (srcFileName dirs)
|
||||||
|
binT <- getModTime (binFileName dirs)
|
||||||
|
if any (binT <) (srcT : libTs)
|
||||||
|
then True <$ trace "XMonad recompiling because some files have changed."
|
||||||
|
else False <$ 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."
|
||||||
|
where
|
||||||
|
isSource = flip elem [".hs",".lhs",".hsc"] . takeExtension
|
||||||
|
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
|
||||||
|
shouldCompile dirs CompileStackGhc{} = do
|
||||||
|
stackYamlT <- getModTime (stackYamlFileName dirs)
|
||||||
|
binT <- getModTime (binFileName dirs)
|
||||||
|
if binT < stackYamlT
|
||||||
|
then True <$ trace "XMonad recompiling because some files have changed."
|
||||||
|
else shouldCompile dirs CompileGhc
|
||||||
|
shouldCompile _dirs CompileScript{} =
|
||||||
|
True <$ trace "XMonad recompiling because a custom build script is being used."
|
||||||
|
|
||||||
|
getModTime :: FilePath -> IO (Maybe UTCTime)
|
||||||
|
getModTime f = E.catch (Just <$> getModificationTime f) (\(SomeException _) -> return Nothing)
|
||||||
|
|
||||||
|
-- | Compile the configuration.
|
||||||
|
compile :: Directories -> Compile -> IO ExitCode
|
||||||
|
compile dirs method =
|
||||||
|
bracket_ uninstallSignalHandlers installSignalHandlers $
|
||||||
|
withFile (errFileName dirs) WriteMode $ \err -> do
|
||||||
|
let run = runProc (cfgDir dirs) err
|
||||||
|
case method of
|
||||||
|
CompileGhc -> do
|
||||||
|
ghc <- fromMaybe "ghc" <$> lookupEnv "XMONAD_GHC"
|
||||||
|
run ghc ghcArgs
|
||||||
|
CompileStackGhc stackYaml ->
|
||||||
|
run "stack" ["build", "--silent", "--stack-yaml", stackYaml] .&&.
|
||||||
|
run "stack" ("ghc" : "--stack-yaml" : stackYaml : "--" : ghcArgs)
|
||||||
|
CompileScript script ->
|
||||||
|
run script [binFileName dirs]
|
||||||
|
where
|
||||||
|
ghcArgs = [ "--make"
|
||||||
|
, "xmonad.hs"
|
||||||
|
, "-i" -- only look in @lib@
|
||||||
|
, "-ilib"
|
||||||
|
, "-fforce-recomp"
|
||||||
|
, "-main-is", "main"
|
||||||
|
, "-v0"
|
||||||
|
, "-outputdir", buildDirName dirs
|
||||||
|
, "-o", binFileName dirs
|
||||||
|
]
|
||||||
|
|
||||||
|
-- waitForProcess =<< System.Process.runProcess, but without closing the err handle
|
||||||
|
runProc cwd err exe args = do
|
||||||
|
hPutStrLn err $ unwords $ "$" : exe : args
|
||||||
|
hFlush err
|
||||||
|
(_, _, _, h) <- createProcess_ "runProc" (proc exe args){ cwd = Just cwd, std_err = UseHandle err }
|
||||||
|
waitForProcess h
|
||||||
|
|
||||||
|
cmd1 .&&. cmd2 = cmd1 >>= \case
|
||||||
|
ExitSuccess -> cmd2
|
||||||
|
e -> pure e
|
||||||
|
|
||||||
|
-- | Check GHC output for deprecation warnings and notify the user if there
|
||||||
|
-- were any. Report success otherwise.
|
||||||
|
checkCompileWarnings :: Directories -> IO ()
|
||||||
|
checkCompileWarnings dirs = do
|
||||||
|
ghcErr <- readFile (errFileName dirs)
|
||||||
|
if "-Wdeprecations" `isInfixOf` ghcErr
|
||||||
|
then do
|
||||||
|
let msg = unlines $
|
||||||
|
["Deprecations detected while compiling xmonad config: " <> srcFileName dirs]
|
||||||
|
++ lines ghcErr
|
||||||
|
++ ["","Please correct them or silence using {-# OPTIONS_GHC -Wno-deprecations #-}."]
|
||||||
|
trace msg
|
||||||
|
xmessage msg
|
||||||
|
else
|
||||||
|
trace "XMonad recompilation process exited with success!"
|
||||||
|
|
||||||
|
-- | Notify the user that compilation failed and what was wrong.
|
||||||
|
compileFailed :: Directories -> ExitCode -> IO ()
|
||||||
|
compileFailed dirs status = do
|
||||||
|
ghcErr <- readFile (errFileName dirs)
|
||||||
|
let msg = unlines $
|
||||||
|
["Errors detected while compiling xmonad config: " <> srcFileName dirs]
|
||||||
|
++ 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
|
||||||
|
trace msg
|
||||||
|
xmessage msg
|
||||||
|
|
||||||
|
-- | Recompile the xmonad configuration file when any of the following apply:
|
||||||
--
|
--
|
||||||
-- * force is 'True'
|
-- * force is 'True'
|
||||||
--
|
--
|
||||||
-- * the xmonad executable does not exist
|
-- * the xmonad executable does not exist
|
||||||
--
|
--
|
||||||
-- * the xmonad executable is older than xmonad.hs or any file in
|
-- * the xmonad executable is older than @xmonad.hs@ or any file in
|
||||||
-- the @lib@ directory (under the configuration directory).
|
-- the @lib@ directory (under the configuration directory)
|
||||||
|
--
|
||||||
|
-- * custom @build@ script is being used
|
||||||
--
|
--
|
||||||
-- The -i flag is used to restrict recompilation to the xmonad.hs file only,
|
-- The -i flag is used to restrict recompilation to the xmonad.hs file only,
|
||||||
-- and any files in the aforementioned @lib@ directory.
|
-- and any files in the aforementioned @lib@ directory.
|
||||||
@@ -590,106 +741,21 @@ stateFileName = (</> "xmonad.state") <$> getXMonadDataDir
|
|||||||
--
|
--
|
||||||
-- 'False' is returned if there are compilation errors.
|
-- 'False' is returned if there are compilation errors.
|
||||||
--
|
--
|
||||||
recompile :: MonadIO m => Bool -> m Bool
|
recompile :: MonadIO m => Directories -> Bool -> m Bool
|
||||||
recompile force = io $ do
|
recompile dirs force = io $ do
|
||||||
cfgdir <- getXMonadDir
|
method <- detectCompile dirs
|
||||||
datadir <- getXMonadDataDir
|
willCompile <- if force
|
||||||
let binn = "xmonad-"++arch++"-"++os
|
then True <$ trace "XMonad recompiling (forced)."
|
||||||
bin = datadir </> binn
|
else shouldCompile dirs method
|
||||||
err = datadir </> "xmonad.errors"
|
if willCompile
|
||||||
src = cfgdir </> "xmonad.hs"
|
|
||||||
lib = cfgdir </> "lib"
|
|
||||||
buildscript = cfgdir </> "build"
|
|
||||||
|
|
||||||
libTs <- mapM getModTime . Prelude.filter isSource =<< allFiles lib
|
|
||||||
srcT <- getModTime src
|
|
||||||
binT <- getModTime bin
|
|
||||||
|
|
||||||
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
|
then do
|
||||||
-- temporarily disable SIGCHLD ignoring:
|
status <- compile dirs method
|
||||||
uninstallSignalHandlers
|
|
||||||
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:
|
|
||||||
if status == ExitSuccess
|
if status == ExitSuccess
|
||||||
then trace "XMonad recompilation process exited with success!"
|
then checkCompileWarnings dirs
|
||||||
else do
|
else compileFailed dirs status
|
||||||
ghcErr <- readFile err
|
pure $ status == ExitSuccess
|
||||||
let msg = unlines $
|
else
|
||||||
["Error detected while loading xmonad configuration file: " ++ src]
|
pure True
|
||||||
++ 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.
|
-- | Conditionally run an action, using a @Maybe a@ to decide.
|
||||||
whenJust :: Monad m => Maybe a -> (a -> m ()) -> m ()
|
whenJust :: Monad m => Maybe a -> (a -> m ()) -> m ()
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances, DeriveDataTypeable #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
|
{-# LANGUAGE PatternGuards #-}
|
||||||
|
|
||||||
-- --------------------------------------------------------------------------
|
-- --------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
@@ -8,7 +10,7 @@
|
|||||||
--
|
--
|
||||||
-- Maintainer : spencerjanssen@gmail.com
|
-- Maintainer : spencerjanssen@gmail.com
|
||||||
-- Stability : unstable
|
-- Stability : unstable
|
||||||
-- Portability : not portable, Typeable deriving, mtl, posix
|
-- Portability : not portable, mtl, posix
|
||||||
--
|
--
|
||||||
-- The collection of core layouts.
|
-- The collection of core layouts.
|
||||||
--
|
--
|
||||||
@@ -16,7 +18,7 @@
|
|||||||
|
|
||||||
module XMonad.Layout (
|
module XMonad.Layout (
|
||||||
Full(..), Tall(..), Mirror(..),
|
Full(..), Tall(..), Mirror(..),
|
||||||
Resize(..), IncMasterN(..), Choose, (|||), ChangeLayout(..),
|
Resize(..), IncMasterN(..), Choose(..), (|||), CLR(..), ChangeLayout(..), JumpToLayout(..),
|
||||||
mirrorRect, splitVertically,
|
mirrorRect, splitVertically,
|
||||||
splitHorizontally, splitHorizontallyBy, splitVerticallyBy,
|
splitHorizontally, splitHorizontallyBy, splitVerticallyBy,
|
||||||
|
|
||||||
@@ -27,6 +29,7 @@ module XMonad.Layout (
|
|||||||
import XMonad.Core
|
import XMonad.Core
|
||||||
|
|
||||||
import Graphics.X11 (Rectangle(..))
|
import Graphics.X11 (Rectangle(..))
|
||||||
|
import Graphics.X11.Xlib.Extras ( Event(DestroyWindowEvent) )
|
||||||
import qualified XMonad.StackSet as W
|
import qualified XMonad.StackSet as W
|
||||||
import Control.Arrow ((***), second)
|
import Control.Arrow ((***), second)
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
@@ -35,10 +38,10 @@ import Data.Maybe (fromMaybe)
|
|||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
-- | Change the size of the master pane.
|
-- | Change the size of the master pane.
|
||||||
data Resize = Shrink | Expand deriving Typeable
|
data Resize = Shrink | Expand
|
||||||
|
|
||||||
-- | Increase the number of clients in the master pane.
|
-- | Increase the number of clients in the master pane.
|
||||||
data IncMasterN = IncMasterN !Int deriving Typeable
|
newtype IncMasterN = IncMasterN Int
|
||||||
|
|
||||||
instance Message Resize
|
instance Message Resize
|
||||||
instance Message IncMasterN
|
instance Message IncMasterN
|
||||||
@@ -77,7 +80,7 @@ instance LayoutClass Tall a where
|
|||||||
-- algorithm.
|
-- algorithm.
|
||||||
--
|
--
|
||||||
-- The screen is divided into two panes. All clients are
|
-- The screen is divided into two panes. All clients are
|
||||||
-- then partioned between these two panes. One pane, the master, by
|
-- then partitioned between these two panes. One pane, the master, by
|
||||||
-- convention has the least number of windows in it.
|
-- convention has the least number of windows in it.
|
||||||
tile
|
tile
|
||||||
:: Rational -- ^ @frac@, what proportion of the screen to devote to the master area
|
:: Rational -- ^ @frac@, what proportion of the screen to devote to the master area
|
||||||
@@ -131,23 +134,53 @@ mirrorRect (Rectangle rx ry rw rh) = Rectangle ry rx rh rw
|
|||||||
-- LayoutClass selection manager
|
-- LayoutClass selection manager
|
||||||
-- Layouts that transition between other layouts
|
-- Layouts that transition between other layouts
|
||||||
|
|
||||||
-- | Messages to change the current layout.
|
-- | Messages to change the current layout. Also see 'JumpToLayout'.
|
||||||
data ChangeLayout = FirstLayout | NextLayout deriving (Eq, Show, Typeable)
|
data ChangeLayout = FirstLayout | NextLayout deriving (Eq, Show)
|
||||||
|
|
||||||
instance Message ChangeLayout
|
instance Message ChangeLayout
|
||||||
|
|
||||||
|
-- | A message to jump to a particular layout, specified by its
|
||||||
|
-- description string.
|
||||||
|
--
|
||||||
|
-- The argument given to a 'JumpToLayout' message should be the
|
||||||
|
-- @description@ of the layout to be selected. If you use
|
||||||
|
-- "XMonad.Hooks.DynamicLog" from @xmonad-contrib@, this is the name of
|
||||||
|
-- the layout displayed in your status bar. Alternatively, you can use
|
||||||
|
-- GHCi to determine the proper name to use. For example:
|
||||||
|
--
|
||||||
|
-- > $ ghci
|
||||||
|
-- > GHCi, version 6.8.2: http://www.haskell.org/ghc/ :? for help
|
||||||
|
-- > Loading package base ... linking ... done.
|
||||||
|
-- > :set prompt "> " -- don't show loaded module names
|
||||||
|
-- > > :m +XMonad.Core -- load the xmonad core
|
||||||
|
-- > > :m +XMonad.Layout.Grid -- load whatever module you want to use
|
||||||
|
-- > > description Grid -- find out what it's called
|
||||||
|
-- > "Grid"
|
||||||
|
--
|
||||||
|
-- As yet another (possibly easier) alternative, you can use the
|
||||||
|
-- "XMonad.Layout.Renamed" module (also in @xmonad-contrib@) to give
|
||||||
|
-- custom names to your layouts, and use those.
|
||||||
|
--
|
||||||
|
-- For example, if you want to jump directly to the 'Full' layout you
|
||||||
|
-- can do
|
||||||
|
--
|
||||||
|
-- > , ((modm .|. controlMask, xK_f), sendMessage $ JumpToLayout "Full")
|
||||||
|
--
|
||||||
|
newtype JumpToLayout = JumpToLayout String
|
||||||
|
instance Message JumpToLayout
|
||||||
|
|
||||||
-- | The layout choice combinator
|
-- | The layout choice combinator
|
||||||
(|||) :: l a -> r a -> Choose l r a
|
(|||) :: l a -> r a -> Choose l r a
|
||||||
(|||) = Choose L
|
(|||) = Choose CL
|
||||||
infixr 5 |||
|
infixr 5 |||
|
||||||
|
|
||||||
-- | A layout that allows users to switch between various layout options.
|
-- | A layout that allows users to switch between various layout options.
|
||||||
data Choose l r a = Choose LR (l a) (r a) deriving (Read, Show)
|
data Choose l r a = Choose CLR (l a) (r a) deriving (Read, Show)
|
||||||
|
|
||||||
-- | Are we on the left or right sub-layout?
|
-- | Choose the current sub-layout (left or right) in 'Choose'.
|
||||||
data LR = L | R deriving (Read, Show, Eq)
|
data CLR = CL | CR deriving (Read, Show, Eq)
|
||||||
|
|
||||||
data NextNoWrap = NextNoWrap deriving (Eq, Show, Typeable)
|
data NextNoWrap = NextNoWrap deriving (Eq, Show)
|
||||||
instance Message NextNoWrap
|
instance Message NextNoWrap
|
||||||
|
|
||||||
-- | A small wrapper around handleMessage, as it is tedious to write
|
-- | A small wrapper around handleMessage, as it is tedious to write
|
||||||
@@ -159,26 +192,26 @@ handle l m = handleMessage l (SomeMessage m)
|
|||||||
-- new structure if any fields have changed, and performs any necessary cleanup
|
-- new structure if any fields have changed, and performs any necessary cleanup
|
||||||
-- on newly non-visible layouts.
|
-- on newly non-visible layouts.
|
||||||
choose :: (LayoutClass l a, LayoutClass r a)
|
choose :: (LayoutClass l a, LayoutClass r a)
|
||||||
=> Choose l r a-> LR -> Maybe (l a) -> Maybe (r a) -> X (Maybe (Choose l r a))
|
=> Choose l r a -> CLR -> Maybe (l a) -> Maybe (r a) -> X (Maybe (Choose l r a))
|
||||||
choose (Choose d _ _) d' Nothing Nothing | d == d' = return Nothing
|
choose (Choose d _ _) d' Nothing Nothing | d == d' = return Nothing
|
||||||
choose (Choose d l r) d' ml mr = f lr
|
choose (Choose d l r) d' ml mr = f lr
|
||||||
where
|
where
|
||||||
(l', r') = (fromMaybe l ml, fromMaybe r mr)
|
(l', r') = (fromMaybe l ml, fromMaybe r mr)
|
||||||
lr = case (d, d') of
|
lr = case (d, d') of
|
||||||
(L, R) -> (hide l' , return r')
|
(CL, CR) -> (hide l' , return r')
|
||||||
(R, L) -> (return l', hide r' )
|
(CR, CL) -> (return l', hide r' )
|
||||||
(_, _) -> (return l', return r')
|
(_ , _ ) -> (return l', return r')
|
||||||
f (x,y) = fmap Just $ liftM2 (Choose d') x y
|
f (x,y) = Just <$> liftM2 (Choose d') x y
|
||||||
hide x = fmap (fromMaybe x) $ handle x Hide
|
hide x = fromMaybe x <$> handle x Hide
|
||||||
|
|
||||||
instance (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a where
|
instance (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a where
|
||||||
runLayout (W.Workspace i (Choose L l r) ms) =
|
runLayout (W.Workspace i (Choose CL l r) ms) =
|
||||||
fmap (second . fmap $ flip (Choose L) r) . runLayout (W.Workspace i l ms)
|
fmap (second . fmap $ flip (Choose CL) r) . runLayout (W.Workspace i l ms)
|
||||||
runLayout (W.Workspace i (Choose R l r) ms) =
|
runLayout (W.Workspace i (Choose CR l r) ms) =
|
||||||
fmap (second . fmap $ Choose R l) . runLayout (W.Workspace i r ms)
|
fmap (second . fmap $ Choose CR l) . runLayout (W.Workspace i r ms)
|
||||||
|
|
||||||
description (Choose L l _) = description l
|
description (Choose CL l _) = description l
|
||||||
description (Choose R _ r) = description r
|
description (Choose CR _ r) = description r
|
||||||
|
|
||||||
handleMessage lr m | Just NextLayout <- fromMessage m = do
|
handleMessage lr m | Just NextLayout <- fromMessage m = do
|
||||||
mlr' <- handle lr NextNoWrap
|
mlr' <- handle lr NextNoWrap
|
||||||
@@ -186,25 +219,36 @@ instance (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a where
|
|||||||
|
|
||||||
handleMessage c@(Choose d l r) m | Just NextNoWrap <- fromMessage m =
|
handleMessage c@(Choose d l r) m | Just NextNoWrap <- fromMessage m =
|
||||||
case d of
|
case d of
|
||||||
L -> do
|
CL -> do
|
||||||
ml <- handle l NextNoWrap
|
ml <- handle l NextNoWrap
|
||||||
case ml of
|
case ml of
|
||||||
Just _ -> choose c L ml Nothing
|
Just _ -> choose c CL ml Nothing
|
||||||
Nothing -> choose c R Nothing =<< handle r FirstLayout
|
Nothing -> choose c CR Nothing =<< handle r FirstLayout
|
||||||
|
|
||||||
R -> choose c R Nothing =<< handle r NextNoWrap
|
CR -> choose c CR Nothing =<< handle r NextNoWrap
|
||||||
|
|
||||||
handleMessage c@(Choose _ l _) m | Just FirstLayout <- fromMessage m =
|
handleMessage c@(Choose _ l _) m | Just FirstLayout <- fromMessage m =
|
||||||
flip (choose c L) Nothing =<< handle l FirstLayout
|
flip (choose c CL) Nothing =<< handle l FirstLayout
|
||||||
|
|
||||||
handleMessage c@(Choose d l r) m | Just ReleaseResources <- fromMessage m =
|
handleMessage c@(Choose d l r) m | Just ReleaseResources <- fromMessage m =
|
||||||
join $ liftM2 (choose c d) (handle l ReleaseResources) (handle r ReleaseResources)
|
join $ liftM2 (choose c d) (handle l ReleaseResources) (handle r ReleaseResources)
|
||||||
|
|
||||||
|
handleMessage c@(Choose d l r) m | Just e@DestroyWindowEvent{} <- fromMessage m =
|
||||||
|
join $ liftM2 (choose c d) (handle l e) (handle r e)
|
||||||
|
|
||||||
|
handleMessage c@(Choose d l r) m | Just (JumpToLayout desc) <- fromMessage m = do
|
||||||
|
ml <- handleMessage l m
|
||||||
|
mr <- handleMessage r m
|
||||||
|
let md | desc == description (fromMaybe l ml) = CL
|
||||||
|
| desc == description (fromMaybe r mr) = CR
|
||||||
|
| otherwise = d
|
||||||
|
choose c md ml mr
|
||||||
|
|
||||||
handleMessage c@(Choose d l r) m = do
|
handleMessage c@(Choose d l r) m = do
|
||||||
ml' <- case d of
|
ml' <- case d of
|
||||||
L -> handleMessage l m
|
CL -> handleMessage l m
|
||||||
R -> return Nothing
|
CR -> return Nothing
|
||||||
mr' <- case d of
|
mr' <- case d of
|
||||||
L -> return Nothing
|
CL -> return Nothing
|
||||||
R -> handleMessage r m
|
CR -> handleMessage r m
|
||||||
choose c d ml' mr'
|
choose c d ml' mr'
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
|
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Main
|
-- Module : XMonad.Main
|
||||||
@@ -16,15 +18,17 @@
|
|||||||
module XMonad.Main (xmonad, launch) where
|
module XMonad.Main (xmonad, launch) where
|
||||||
|
|
||||||
import System.Locale.SetLocale
|
import System.Locale.SetLocale
|
||||||
import qualified Control.Exception.Extensible as E
|
import qualified Control.Exception as E
|
||||||
import Data.Bits
|
import Data.Bits
|
||||||
import Data.List ((\\))
|
import Data.List ((\\))
|
||||||
|
import Data.Foldable (traverse_)
|
||||||
import Data.Function
|
import Data.Function
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Control.Monad.State
|
import Control.Monad.State
|
||||||
import Data.Maybe (fromMaybe)
|
import Control.Monad (filterM, guard, unless, void, when)
|
||||||
|
import Data.Maybe (fromMaybe, isJust)
|
||||||
import Data.Monoid (getAll)
|
import Data.Monoid (getAll)
|
||||||
|
|
||||||
import Graphics.X11.Xlib hiding (refreshKeyboardMapping)
|
import Graphics.X11.Xlib hiding (refreshKeyboardMapping)
|
||||||
@@ -39,7 +43,7 @@ import XMonad.Operations
|
|||||||
import System.IO
|
import System.IO
|
||||||
import System.Directory
|
import System.Directory
|
||||||
import System.Info
|
import System.Info
|
||||||
import System.Environment
|
import System.Environment (getArgs, getProgName, withArgs)
|
||||||
import System.Posix.Process (executeFile)
|
import System.Posix.Process (executeFile)
|
||||||
import System.Exit (exitFailure)
|
import System.Exit (exitFailure)
|
||||||
import System.FilePath
|
import System.FilePath
|
||||||
@@ -48,6 +52,7 @@ import Paths_xmonad (version)
|
|||||||
import Data.Version (showVersion)
|
import Data.Version (showVersion)
|
||||||
|
|
||||||
import Graphics.X11.Xinerama (compiledWithXinerama)
|
import Graphics.X11.Xinerama (compiledWithXinerama)
|
||||||
|
import Graphics.X11.Xrandr (xrrQueryExtension, xrrUpdateConfiguration)
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -59,17 +64,17 @@ xmonad :: (LayoutClass l Window, Read (l Window)) => XConfig l -> IO ()
|
|||||||
xmonad conf = do
|
xmonad conf = do
|
||||||
installSignalHandlers -- important to ignore SIGCHLD to avoid zombies
|
installSignalHandlers -- important to ignore SIGCHLD to avoid zombies
|
||||||
|
|
||||||
|
dirs <- getDirectories
|
||||||
let launch' args = do
|
let launch' args = do
|
||||||
catchIO buildLaunch
|
catchIO (buildLaunch dirs)
|
||||||
conf' @ XConfig { layoutHook = Layout l }
|
conf'@XConfig { layoutHook = Layout l }
|
||||||
<- handleExtraArgs conf args conf{ layoutHook = Layout (layoutHook conf) }
|
<- handleExtraArgs conf args conf{ layoutHook = Layout (layoutHook conf) }
|
||||||
withArgs [] $ launch (conf' { layoutHook = l })
|
withArgs [] $ launch (conf' { layoutHook = l }) dirs
|
||||||
|
|
||||||
args <- getArgs
|
args <- getArgs
|
||||||
case args of
|
case args of
|
||||||
("--resume": ws : xs : args') -> migrateState ws xs >> launch' args'
|
|
||||||
["--help"] -> usage
|
["--help"] -> usage
|
||||||
["--recompile"] -> recompile True >>= flip unless exitFailure
|
["--recompile"] -> recompile dirs True >>= flip unless exitFailure
|
||||||
["--restart"] -> sendRestart
|
["--restart"] -> sendRestart
|
||||||
["--version"] -> putStrLn $ unwords shortVersion
|
["--version"] -> putStrLn $ unwords shortVersion
|
||||||
["--verbose-version"] -> putStrLn . unwords $ shortVersion ++ longVersion
|
["--verbose-version"] -> putStrLn . unwords $ shortVersion ++ longVersion
|
||||||
@@ -86,14 +91,14 @@ usage :: IO ()
|
|||||||
usage = do
|
usage = do
|
||||||
self <- getProgName
|
self <- getProgName
|
||||||
putStr . unlines $
|
putStr . unlines $
|
||||||
concat ["Usage: ", self, " [OPTION]"] :
|
[ "Usage: " <> self <> " [OPTION]"
|
||||||
"Options:" :
|
, "Options:"
|
||||||
" --help Print this message" :
|
, " --help Print this message"
|
||||||
" --version Print the version number" :
|
, " --version Print the version number"
|
||||||
" --recompile Recompile your ~/.xmonad/xmonad.hs" :
|
, " --recompile Recompile your xmonad.hs"
|
||||||
" --replace Replace the running window manager with xmonad" :
|
, " --replace Replace the running window manager with xmonad"
|
||||||
" --restart Request a running xmonad process to restart" :
|
, " --restart Request a running xmonad process to restart"
|
||||||
[]
|
]
|
||||||
|
|
||||||
-- | Build the xmonad configuration file with ghc, then execute it.
|
-- | Build the xmonad configuration file with ghc, then execute it.
|
||||||
-- If there are no errors, this function does not return. An
|
-- If there are no errors, this function does not return. An
|
||||||
@@ -111,21 +116,21 @@ usage = do
|
|||||||
--
|
--
|
||||||
-- * Missing XMonad\/XMonadContrib modules due to ghc upgrade
|
-- * Missing XMonad\/XMonadContrib modules due to ghc upgrade
|
||||||
--
|
--
|
||||||
buildLaunch :: IO ()
|
buildLaunch :: Directories -> IO ()
|
||||||
buildLaunch = do
|
buildLaunch dirs = do
|
||||||
whoami <- getProgName
|
whoami <- getProgName
|
||||||
let compiledConfig = "xmonad-"++arch++"-"++os
|
let bin = binFileName dirs
|
||||||
|
let compiledConfig = takeFileName bin
|
||||||
unless (whoami == compiledConfig) $ do
|
unless (whoami == compiledConfig) $ do
|
||||||
trace $ concat
|
trace $ concat
|
||||||
[ "XMonad is recompiling and replacing itself another XMonad process because the current process is called "
|
[ "XMonad is recompiling and replacing itself with another XMonad process because the current process is called "
|
||||||
, show whoami
|
, show whoami
|
||||||
, " but the compiled configuration should be called "
|
, " but the compiled configuration should be called "
|
||||||
, show compiledConfig
|
, show compiledConfig
|
||||||
]
|
]
|
||||||
recompile False
|
recompile dirs False
|
||||||
dir <- getXMonadDataDir
|
|
||||||
args <- getArgs
|
args <- getArgs
|
||||||
executeFile (dir </> compiledConfig) False args Nothing
|
executeFile bin False args Nothing
|
||||||
|
|
||||||
sendRestart :: IO ()
|
sendRestart :: IO ()
|
||||||
sendRestart = do
|
sendRestart = do
|
||||||
@@ -134,7 +139,7 @@ sendRestart = do
|
|||||||
xmonad_restart <- internAtom dpy "XMONAD_RESTART" False
|
xmonad_restart <- internAtom dpy "XMONAD_RESTART" False
|
||||||
allocaXEvent $ \e -> do
|
allocaXEvent $ \e -> do
|
||||||
setEventType e clientMessage
|
setEventType e clientMessage
|
||||||
setClientMessageEvent e rw xmonad_restart 32 0 currentTime
|
setClientMessageEvent' e rw xmonad_restart 32 []
|
||||||
sendEvent dpy rw False structureNotifyMask e
|
sendEvent dpy rw False structureNotifyMask e
|
||||||
sync dpy False
|
sync dpy False
|
||||||
|
|
||||||
@@ -166,8 +171,8 @@ sendReplace = do
|
|||||||
-- function instead of 'xmonad'. You probably also want to have a key
|
-- function instead of 'xmonad'. You probably also want to have a key
|
||||||
-- binding to the 'XMonad.Operations.restart` function that restarts
|
-- binding to the 'XMonad.Operations.restart` function that restarts
|
||||||
-- your custom binary with the resume flag set to @True@.
|
-- your custom binary with the resume flag set to @True@.
|
||||||
launch :: (LayoutClass l Window, Read (l Window)) => XConfig l -> IO ()
|
launch :: (LayoutClass l Window, Read (l Window)) => XConfig l -> Directories -> IO ()
|
||||||
launch initxmc = do
|
launch initxmc drs = do
|
||||||
-- setup locale information from environment
|
-- setup locale information from environment
|
||||||
setLocale LC_ALL (Just "")
|
setLocale LC_ALL (Just "")
|
||||||
-- ignore SIGPIPE and SIGCHLD
|
-- ignore SIGPIPE and SIGCHLD
|
||||||
@@ -192,12 +197,12 @@ launch initxmc = do
|
|||||||
|
|
||||||
xinesc <- getCleanedScreenInfo dpy
|
xinesc <- getCleanedScreenInfo dpy
|
||||||
|
|
||||||
nbc <- do v <- initColor dpy $ normalBorderColor xmc
|
nbc <- do v <- initColor dpy $ normalBorderColor xmc
|
||||||
~(Just nbc_) <- initColor dpy $ normalBorderColor Default.def
|
Just nbc_ <- initColor dpy $ normalBorderColor Default.def
|
||||||
return (fromMaybe nbc_ v)
|
return (fromMaybe nbc_ v)
|
||||||
|
|
||||||
fbc <- do v <- initColor dpy $ focusedBorderColor xmc
|
fbc <- do v <- initColor dpy $ focusedBorderColor xmc
|
||||||
~(Just fbc_) <- initColor dpy $ focusedBorderColor Default.def
|
Just fbc_ <- initColor dpy $ focusedBorderColor Default.def
|
||||||
return (fromMaybe fbc_ v)
|
return (fromMaybe fbc_ v)
|
||||||
|
|
||||||
hSetBuffering stdout NoBuffering
|
hSetBuffering stdout NoBuffering
|
||||||
@@ -216,7 +221,9 @@ launch initxmc = do
|
|||||||
, buttonActions = mouseBindings xmc xmc
|
, buttonActions = mouseBindings xmc xmc
|
||||||
, mouseFocused = False
|
, mouseFocused = False
|
||||||
, mousePosition = Nothing
|
, mousePosition = Nothing
|
||||||
, currentEvent = Nothing }
|
, currentEvent = Nothing
|
||||||
|
, directories = drs
|
||||||
|
}
|
||||||
|
|
||||||
st = XState
|
st = XState
|
||||||
{ windowset = initialWinset
|
{ windowset = initialWinset
|
||||||
@@ -231,7 +238,7 @@ launch initxmc = do
|
|||||||
runX cf st $ do
|
runX cf st $ do
|
||||||
-- check for serialized state in a file.
|
-- check for serialized state in a file.
|
||||||
serializedSt <- do
|
serializedSt <- do
|
||||||
path <- stateFileName
|
path <- asks $ stateFileName . directories
|
||||||
exists <- io (doesFileExist path)
|
exists <- io (doesFileExist path)
|
||||||
if exists then readStateFile initxmc else return Nothing
|
if exists then readStateFile initxmc else return Nothing
|
||||||
|
|
||||||
@@ -239,7 +246,7 @@ launch initxmc = do
|
|||||||
let extst = maybe M.empty extensibleState serializedSt
|
let extst = maybe M.empty extensibleState serializedSt
|
||||||
modify (\s -> s {extensibleState = extst})
|
modify (\s -> s {extensibleState = extst})
|
||||||
|
|
||||||
setNumlockMask
|
cacheNumlockMask
|
||||||
grabKeys
|
grabKeys
|
||||||
grabButtons
|
grabButtons
|
||||||
|
|
||||||
@@ -260,8 +267,12 @@ launch initxmc = do
|
|||||||
|
|
||||||
userCode $ startupHook initxmc
|
userCode $ startupHook initxmc
|
||||||
|
|
||||||
|
rrData <- io $ xrrQueryExtension dpy
|
||||||
|
|
||||||
-- main loop, for all you HOF/recursion fans out there.
|
-- main loop, for all you HOF/recursion fans out there.
|
||||||
forever $ prehandle =<< io (nextEvent dpy e >> getEvent e)
|
-- forever $ prehandle =<< io (nextEvent dpy e >> rrUpdate e >> getEvent e)
|
||||||
|
-- sadly, 9.2.{1,2,3} join points mishandle the above and trash the heap (see #389)
|
||||||
|
mainLoop dpy e rrData
|
||||||
|
|
||||||
return ()
|
return ()
|
||||||
where
|
where
|
||||||
@@ -272,6 +283,8 @@ launch initxmc = do
|
|||||||
in local (\c -> c { mousePosition = mouse, currentEvent = Just e }) (handleWithHook e)
|
in local (\c -> c { mousePosition = mouse, currentEvent = Just e }) (handleWithHook e)
|
||||||
evs = [ keyPress, keyRelease, enterNotify, leaveNotify
|
evs = [ keyPress, keyRelease, enterNotify, leaveNotify
|
||||||
, buttonPress, buttonRelease]
|
, buttonPress, buttonRelease]
|
||||||
|
rrUpdate e r = when (isJust r) (void (xrrUpdateConfiguration e))
|
||||||
|
mainLoop d e r = io (nextEvent d e >> rrUpdate e r >> getEvent e) >>= prehandle >> mainLoop d e r
|
||||||
|
|
||||||
|
|
||||||
-- | Runs handleEventHook from the configuration and runs the default handler
|
-- | Runs handleEventHook from the configuration and runs the default handler
|
||||||
@@ -310,16 +323,21 @@ handle (MapRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
|
|||||||
|
|
||||||
-- window destroyed, unmanage it
|
-- window destroyed, unmanage it
|
||||||
-- window gone, unmanage it
|
-- window gone, unmanage it
|
||||||
handle (DestroyWindowEvent {ev_window = w}) = whenX (isClient w) $ do
|
-- broadcast to layouts
|
||||||
|
handle e@(DestroyWindowEvent {ev_window = w}) = do
|
||||||
|
whenX (isClient w) $ do
|
||||||
unmanage w
|
unmanage w
|
||||||
modify (\s -> s { mapped = S.delete w (mapped s)
|
modify (\s -> s { mapped = S.delete w (mapped s)
|
||||||
, waitingUnmap = M.delete w (waitingUnmap s)})
|
, waitingUnmap = M.delete w (waitingUnmap s)})
|
||||||
|
-- the window is already unmanged, but we broadcast the event to all layouts
|
||||||
|
-- to trigger garbage-collection in case they hold window-specific resources
|
||||||
|
broadcastMessage e
|
||||||
|
|
||||||
-- We track expected unmap events in waitingUnmap. We ignore this event unless
|
-- We track expected unmap events in waitingUnmap. We ignore this event unless
|
||||||
-- it is synthetic or we are not expecting an unmap notification from a window.
|
-- it is synthetic or we are not expecting an unmap notification from a window.
|
||||||
handle (UnmapEvent {ev_window = w, ev_send_event = synthetic}) = whenX (isClient w) $ do
|
handle (UnmapEvent {ev_window = w, ev_send_event = synthetic}) = whenX (isClient w) $ do
|
||||||
e <- gets (fromMaybe 0 . M.lookup w . waitingUnmap)
|
e <- gets (fromMaybe 0 . M.lookup w . waitingUnmap)
|
||||||
if (synthetic || e == 0)
|
if synthetic || e == 0
|
||||||
then unmanage w
|
then unmanage w
|
||||||
else modify (\s -> s { waitingUnmap = M.update mpred w (waitingUnmap s) })
|
else modify (\s -> s { waitingUnmap = M.update mpred w (waitingUnmap s) })
|
||||||
where mpred 1 = Nothing
|
where mpred 1 = Nothing
|
||||||
@@ -329,7 +347,7 @@ handle (UnmapEvent {ev_window = w, ev_send_event = synthetic}) = whenX (isClient
|
|||||||
handle e@(MappingNotifyEvent {}) = do
|
handle e@(MappingNotifyEvent {}) = do
|
||||||
io $ refreshKeyboardMapping e
|
io $ refreshKeyboardMapping e
|
||||||
when (ev_request e `elem` [mappingKeyboard, mappingModifier]) $ do
|
when (ev_request e `elem` [mappingKeyboard, mappingModifier]) $ do
|
||||||
setNumlockMask
|
cacheNumlockMask
|
||||||
grabKeys
|
grabKeys
|
||||||
|
|
||||||
-- handle button release, which may finish dragging.
|
-- handle button release, which may finish dragging.
|
||||||
@@ -417,7 +435,7 @@ handle event@(PropertyEvent { ev_event_type = t, ev_atom = a })
|
|||||||
|
|
||||||
handle e@ClientMessageEvent { ev_message_type = mt } = do
|
handle e@ClientMessageEvent { ev_message_type = mt } = do
|
||||||
a <- getAtom "XMONAD_RESTART"
|
a <- getAtom "XMONAD_RESTART"
|
||||||
if (mt == a)
|
if mt == a
|
||||||
then restart "xmonad" True
|
then restart "xmonad" True
|
||||||
else broadcastMessage e
|
else broadcastMessage e
|
||||||
|
|
||||||
@@ -448,35 +466,14 @@ scan dpy rootw = do
|
|||||||
skip :: E.SomeException -> IO Bool
|
skip :: E.SomeException -> IO Bool
|
||||||
skip _ = return False
|
skip _ = return False
|
||||||
|
|
||||||
setNumlockMask :: X ()
|
|
||||||
setNumlockMask = do
|
|
||||||
dpy <- asks display
|
|
||||||
ms <- io $ getModifierMapping dpy
|
|
||||||
xs <- sequence [ do
|
|
||||||
ks <- io $ keycodeToKeysym dpy kc 0
|
|
||||||
if ks == xK_Num_Lock
|
|
||||||
then return (setBit 0 (fromIntegral m))
|
|
||||||
else return (0 :: KeyMask)
|
|
||||||
| (m, kcs) <- ms, kc <- kcs, kc /= 0]
|
|
||||||
modify (\s -> s { numberlockMask = foldr (.|.) 0 xs })
|
|
||||||
|
|
||||||
-- | Grab the keys back
|
-- | Grab the keys back
|
||||||
grabKeys :: X ()
|
grabKeys :: X ()
|
||||||
grabKeys = do
|
grabKeys = do
|
||||||
XConf { display = dpy, theRoot = rootw } <- ask
|
XConf { display = dpy, theRoot = rootw } <- ask
|
||||||
let grab kc m = io $ grabKey dpy kc m rootw True grabModeAsync grabModeAsync
|
|
||||||
(minCode, maxCode) = displayKeycodes dpy
|
|
||||||
allCodes = [fromIntegral minCode .. fromIntegral maxCode]
|
|
||||||
io $ ungrabKey dpy anyKey anyModifier rootw
|
io $ ungrabKey dpy anyKey anyModifier rootw
|
||||||
ks <- asks keyActions
|
let grab :: (KeyMask, KeyCode) -> X ()
|
||||||
-- build a map from keysyms to lists of keysyms (doing what
|
grab (km, kc) = io $ grabKey dpy kc km rootw True grabModeAsync grabModeAsync
|
||||||
-- XGetKeyboardMapping would do if the X11 package bound it)
|
traverse_ grab =<< mkGrabs =<< asks (M.keys . keyActions)
|
||||||
syms <- forM allCodes $ \code -> io (keycodeToKeysym dpy code 0)
|
|
||||||
let keysymMap = M.fromListWith (++) (zip syms [[code] | code <- allCodes])
|
|
||||||
keysymToKeycodes sym = M.findWithDefault [] sym keysymMap
|
|
||||||
forM_ (M.keys ks) $ \(mask,sym) ->
|
|
||||||
forM_ (keysymToKeycodes sym) $ \kc ->
|
|
||||||
mapM_ (grab kc . (mask .|.)) =<< extraModifiers
|
|
||||||
|
|
||||||
-- | Grab the buttons
|
-- | Grab the buttons
|
||||||
grabButtons :: X ()
|
grabButtons :: X ()
|
||||||
@@ -487,7 +484,7 @@ grabButtons = do
|
|||||||
io $ ungrabButton dpy anyButton anyModifier rootw
|
io $ ungrabButton dpy anyButton anyModifier rootw
|
||||||
ems <- extraModifiers
|
ems <- extraModifiers
|
||||||
ba <- asks buttonActions
|
ba <- asks buttonActions
|
||||||
mapM_ (\(m,b) -> mapM_ (grab b . (m .|.)) ems) (M.keys $ ba)
|
mapM_ (\(m,b) -> mapM_ (grab b . (m .|.)) ems) (M.keys ba)
|
||||||
|
|
||||||
-- | @replace@ to signals compliant window managers to exit.
|
-- | @replace@ to signals compliant window managers to exit.
|
||||||
replace :: Display -> ScreenNumber -> Window -> IO ()
|
replace :: Display -> ScreenNumber -> Window -> IO ()
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
|
||||||
|
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.ManageHook
|
-- Module : XMonad.ManageHook
|
||||||
@@ -8,7 +6,6 @@
|
|||||||
--
|
--
|
||||||
-- Maintainer : spencerjanssen@gmail.com
|
-- Maintainer : spencerjanssen@gmail.com
|
||||||
-- Stability : unstable
|
-- Stability : unstable
|
||||||
-- Portability : not portable, uses cunning newtype deriving
|
|
||||||
--
|
--
|
||||||
-- An EDSL for ManageHooks
|
-- An EDSL for ManageHooks
|
||||||
--
|
--
|
||||||
@@ -21,13 +18,13 @@ module XMonad.ManageHook where
|
|||||||
import XMonad.Core
|
import XMonad.Core
|
||||||
import Graphics.X11.Xlib.Extras
|
import Graphics.X11.Xlib.Extras
|
||||||
import Graphics.X11.Xlib (Display, Window, internAtom, wM_NAME)
|
import Graphics.X11.Xlib (Display, Window, internAtom, wM_NAME)
|
||||||
import Control.Exception.Extensible (bracket, SomeException(..))
|
import Control.Exception (bracket, SomeException(..))
|
||||||
import qualified Control.Exception.Extensible as E
|
import qualified Control.Exception as E
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.Monoid
|
import Data.Monoid
|
||||||
import qualified XMonad.StackSet as W
|
import qualified XMonad.StackSet as W
|
||||||
import XMonad.Operations (floatLocation, reveal)
|
import XMonad.Operations (floatLocation, reveal, isFixedSizeOrTransient)
|
||||||
|
|
||||||
-- | Lift an 'X' action to a 'Query'.
|
-- | Lift an 'X' action to a 'Query'.
|
||||||
liftX :: X a -> Query a
|
liftX :: X a -> Query a
|
||||||
@@ -61,11 +58,15 @@ infixr 3 <&&>, <||>
|
|||||||
|
|
||||||
-- | '&&' lifted to a 'Monad'.
|
-- | '&&' lifted to a 'Monad'.
|
||||||
(<&&>) :: Monad m => m Bool -> m Bool -> m Bool
|
(<&&>) :: Monad m => m Bool -> m Bool -> m Bool
|
||||||
(<&&>) = liftM2 (&&)
|
x <&&> y = ifM x y (pure False)
|
||||||
|
|
||||||
-- | '||' lifted to a 'Monad'.
|
-- | '||' lifted to a 'Monad'.
|
||||||
(<||>) :: Monad m => m Bool -> m Bool -> m Bool
|
(<||>) :: Monad m => m Bool -> m Bool -> m Bool
|
||||||
(<||>) = liftM2 (||)
|
x <||> y = ifM x (pure True) y
|
||||||
|
|
||||||
|
-- | If-then-else lifted to a 'Monad'.
|
||||||
|
ifM :: Monad m => m Bool -> m a -> m a -> m a
|
||||||
|
ifM mb t f = mb >>= \b -> if b then t else f
|
||||||
|
|
||||||
-- | Return the window title.
|
-- | Return the window title.
|
||||||
title :: Query String
|
title :: Query String
|
||||||
@@ -79,7 +80,8 @@ title = ask >>= \w -> liftX $ do
|
|||||||
return $ if null l then "" else head l
|
return $ if null l then "" else head l
|
||||||
io $ bracket getProp (xFree . tp_value) extract `E.catch` \(SomeException _) -> return ""
|
io $ bracket getProp (xFree . tp_value) extract `E.catch` \(SomeException _) -> return ""
|
||||||
|
|
||||||
-- | Return the application name.
|
-- | Return the application name; i.e., the /first/ string returned by
|
||||||
|
-- @WM_CLASS@.
|
||||||
appName :: Query String
|
appName :: Query String
|
||||||
appName = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resName $ io $ getClassHint d w)
|
appName = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resName $ io $ getClassHint d w)
|
||||||
|
|
||||||
@@ -87,14 +89,15 @@ appName = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resName $ io $ getClas
|
|||||||
resource :: Query String
|
resource :: Query String
|
||||||
resource = appName
|
resource = appName
|
||||||
|
|
||||||
-- | Return the resource class.
|
-- | Return the resource class; i.e., the /second/ string returned by
|
||||||
|
-- @WM_CLASS@.
|
||||||
className :: Query String
|
className :: Query String
|
||||||
className = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resClass $ io $ getClassHint d w)
|
className = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resClass $ io $ getClassHint d w)
|
||||||
|
|
||||||
-- | A query that can return an arbitrary X property of type 'String',
|
-- | A query that can return an arbitrary X property of type 'String',
|
||||||
-- identified by name.
|
-- identified by name.
|
||||||
stringProperty :: String -> Query String
|
stringProperty :: String -> Query String
|
||||||
stringProperty p = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap (fromMaybe "") $ getStringProperty d w p)
|
stringProperty p = ask >>= (\w -> liftX $ withDisplay $ \d -> fromMaybe "" <$> getStringProperty d w p)
|
||||||
|
|
||||||
getStringProperty :: Display -> Window -> String -> X (Maybe String)
|
getStringProperty :: Display -> Window -> String -> X (Maybe String)
|
||||||
getStringProperty d w p = do
|
getStringProperty d w p = do
|
||||||
@@ -102,6 +105,10 @@ getStringProperty d w p = do
|
|||||||
md <- io $ getWindowProperty8 d a w
|
md <- io $ getWindowProperty8 d a w
|
||||||
return $ fmap (map (toEnum . fromIntegral)) md
|
return $ fmap (map (toEnum . fromIntegral)) md
|
||||||
|
|
||||||
|
-- | Return whether the window will be a floating window or not
|
||||||
|
willFloat :: Query Bool
|
||||||
|
willFloat = ask >>= \w -> liftX $ withDisplay $ \d -> isFixedSizeOrTransient d w
|
||||||
|
|
||||||
-- | Modify the 'WindowSet' with a pure function.
|
-- | Modify the 'WindowSet' with a pure function.
|
||||||
doF :: (s -> s) -> Query (Endo s)
|
doF :: (s -> s) -> Query (Endo s)
|
||||||
doF = return . Endo
|
doF = return . Endo
|
||||||
|
@@ -1,5 +1,10 @@
|
|||||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
{-# LANGUAGE LambdaCase #-}
|
||||||
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
|
{-# LANGUAGE PatternGuards #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
|
||||||
-- --------------------------------------------------------------------------
|
-- --------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Operations
|
-- Module : XMonad.Operations
|
||||||
@@ -8,31 +13,68 @@
|
|||||||
--
|
--
|
||||||
-- Maintainer : dons@cse.unsw.edu.au
|
-- Maintainer : dons@cse.unsw.edu.au
|
||||||
-- Stability : unstable
|
-- Stability : unstable
|
||||||
-- Portability : not portable, Typeable deriving, mtl, posix
|
-- Portability : not portable, mtl, posix
|
||||||
--
|
--
|
||||||
-- Operations.
|
-- Operations. A module for functions that don't cleanly fit anywhere else.
|
||||||
--
|
--
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
module XMonad.Operations where
|
module XMonad.Operations (
|
||||||
|
-- * Manage One Window
|
||||||
|
manage, unmanage, killWindow, kill, isClient,
|
||||||
|
setInitialProperties, setWMState, setWindowBorderWithFallback,
|
||||||
|
hide, reveal, tileWindow,
|
||||||
|
setTopFocus, focus, isFixedSizeOrTransient,
|
||||||
|
|
||||||
|
-- * Manage Windows
|
||||||
|
windows, refresh, rescreen, modifyWindowSet, windowBracket, windowBracket_, clearEvents, getCleanedScreenInfo,
|
||||||
|
withFocused, withUnfocused,
|
||||||
|
|
||||||
|
-- * Keyboard and Mouse
|
||||||
|
cleanMask, extraModifiers,
|
||||||
|
mouseDrag, mouseMoveWindow, mouseResizeWindow,
|
||||||
|
setButtonGrab, setFocusX, cacheNumlockMask, mkGrabs,
|
||||||
|
|
||||||
|
-- * Messages
|
||||||
|
sendMessage, broadcastMessage, sendMessageWithNoRefresh,
|
||||||
|
|
||||||
|
-- * Save and Restore State
|
||||||
|
StateFile (..), writeStateToFile, readStateFile, restart,
|
||||||
|
|
||||||
|
-- * Floating Layer
|
||||||
|
float, floatLocation,
|
||||||
|
|
||||||
|
-- * Window Size Hints
|
||||||
|
D, mkAdjust, applySizeHints, applySizeHints', applySizeHintsContents,
|
||||||
|
applyAspectHint, applyResizeIncHint, applyMaxSizeHint,
|
||||||
|
|
||||||
|
-- * Rectangles
|
||||||
|
containedIn, nubScreens, pointWithin, scaleRationalRect,
|
||||||
|
|
||||||
|
-- * Other Utilities
|
||||||
|
initColor, pointScreen, screenWorkspace,
|
||||||
|
setLayout, updateLayout,
|
||||||
|
) where
|
||||||
|
|
||||||
import XMonad.Core
|
import XMonad.Core
|
||||||
import XMonad.Layout (Full(..))
|
import XMonad.Layout (Full(..))
|
||||||
import qualified XMonad.StackSet as W
|
import qualified XMonad.StackSet as W
|
||||||
|
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.Monoid (Endo(..))
|
import Data.Monoid (Endo(..),Any(..))
|
||||||
import Data.List (nub, (\\), find)
|
import Data.List (nub, (\\), find)
|
||||||
import Data.Bits ((.|.), (.&.), complement, testBit)
|
import Data.Bits ((.|.), (.&.), complement, setBit, testBit)
|
||||||
|
import Data.Function (on)
|
||||||
import Data.Ratio
|
import Data.Ratio
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
|
|
||||||
import Control.Applicative((<$>), (<*>))
|
|
||||||
import Control.Arrow (second)
|
import Control.Arrow (second)
|
||||||
|
import Control.Monad.Fix (fix)
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Control.Monad.State
|
import Control.Monad.State
|
||||||
import qualified Control.Exception.Extensible as C
|
import Control.Monad (forM, forM_, guard, join, unless, void, when)
|
||||||
|
import qualified Control.Exception as C
|
||||||
|
|
||||||
import System.IO
|
import System.IO
|
||||||
import System.Directory
|
import System.Directory
|
||||||
@@ -42,9 +84,20 @@ import Graphics.X11.Xinerama (getScreenInfo)
|
|||||||
import Graphics.X11.Xlib.Extras
|
import Graphics.X11.Xlib.Extras
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
-- |
|
|
||||||
-- Window manager operations
|
-- Window manager operations
|
||||||
-- manage. Add a new window to be managed in the current workspace.
|
|
||||||
|
-- | Detect whether a window has fixed size or is transient. This check
|
||||||
|
-- can be used to determine whether the window should be floating or not
|
||||||
|
--
|
||||||
|
isFixedSizeOrTransient :: Display -> Window -> X Bool
|
||||||
|
isFixedSizeOrTransient d w = do
|
||||||
|
sh <- io $ getWMNormalHints d w
|
||||||
|
let isFixedSize = isJust (sh_min_size sh) && sh_min_size sh == sh_max_size sh
|
||||||
|
isTransient <- isJust <$> io (getTransientForHint d w)
|
||||||
|
return (isFixedSize || isTransient)
|
||||||
|
|
||||||
|
-- |
|
||||||
|
-- Add a new window to be managed in the current workspace.
|
||||||
-- Bring it into focus.
|
-- Bring it into focus.
|
||||||
--
|
--
|
||||||
-- Whether the window is already managed, or not, it is mapped, has its
|
-- Whether the window is already managed, or not, it is mapped, has its
|
||||||
@@ -52,10 +105,8 @@ import Graphics.X11.Xlib.Extras
|
|||||||
--
|
--
|
||||||
manage :: Window -> X ()
|
manage :: Window -> X ()
|
||||||
manage w = whenX (not <$> isClient w) $ withDisplay $ \d -> do
|
manage w = whenX (not <$> isClient w) $ withDisplay $ \d -> do
|
||||||
sh <- io $ getWMNormalHints d w
|
|
||||||
|
|
||||||
let isFixedSize = sh_min_size sh /= Nothing && sh_min_size sh == sh_max_size sh
|
shouldFloat <- isFixedSizeOrTransient d w
|
||||||
isTransient <- isJust <$> io (getTransientForHint d w)
|
|
||||||
|
|
||||||
rr <- snd `fmap` floatLocation w
|
rr <- snd `fmap` floatLocation w
|
||||||
-- ensure that float windows don't go over the edge of the screen
|
-- ensure that float windows don't go over the edge of the screen
|
||||||
@@ -63,15 +114,15 @@ manage w = whenX (not <$> isClient w) $ withDisplay $ \d -> do
|
|||||||
= W.RationalRect (0.5 - wid/2) (0.5 - h/2) wid h
|
= W.RationalRect (0.5 - wid/2) (0.5 - h/2) wid h
|
||||||
adjust r = r
|
adjust r = r
|
||||||
|
|
||||||
f ws | isFixedSize || isTransient = W.float w (adjust rr) . W.insertUp w . W.view i $ ws
|
f ws | shouldFloat = W.float w (adjust rr) . W.insertUp w . W.view i $ ws
|
||||||
| otherwise = W.insertUp w ws
|
| otherwise = W.insertUp w ws
|
||||||
where i = W.tag $ W.workspace $ W.current ws
|
where i = W.tag $ W.workspace $ W.current ws
|
||||||
|
|
||||||
mh <- asks (manageHook . config)
|
mh <- asks (manageHook . config)
|
||||||
g <- appEndo <$> userCodeDef (Endo id) (runQuery mh w)
|
g <- appEndo <$> userCodeDef (Endo id) (runQuery mh w)
|
||||||
windows (g . f)
|
windows (g . f)
|
||||||
|
|
||||||
-- | unmanage. A window no longer exists, remove it from the window
|
-- | A window no longer exists; remove it from the window
|
||||||
-- list, on whatever workspace it is.
|
-- list, on whatever workspace it is.
|
||||||
--
|
--
|
||||||
unmanage :: Window -> X ()
|
unmanage :: Window -> X ()
|
||||||
@@ -91,9 +142,9 @@ killWindow w = withDisplay $ \d -> do
|
|||||||
io $ if wmdelt `elem` protocols
|
io $ if wmdelt `elem` protocols
|
||||||
then allocaXEvent $ \ev -> do
|
then allocaXEvent $ \ev -> do
|
||||||
setEventType ev clientMessage
|
setEventType ev clientMessage
|
||||||
setClientMessageEvent ev w wmprot 32 wmdelt 0
|
setClientMessageEvent ev w wmprot 32 wmdelt currentTime
|
||||||
sendEvent d w False noEventMask ev
|
sendEvent d w False noEventMask ev
|
||||||
else killClient d w >> return ()
|
else void (killClient d w)
|
||||||
|
|
||||||
-- | Kill the currently focused client.
|
-- | Kill the currently focused client.
|
||||||
kill :: X ()
|
kill :: X ()
|
||||||
@@ -102,7 +153,7 @@ kill = withFocused killWindow
|
|||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
-- Managing windows
|
-- Managing windows
|
||||||
|
|
||||||
-- | windows. Modify the current window list with a pure function, and refresh
|
-- | Modify the current window list with a pure function, and refresh
|
||||||
windows :: (WindowSet -> WindowSet) -> X ()
|
windows :: (WindowSet -> WindowSet) -> X ()
|
||||||
windows f = do
|
windows f = do
|
||||||
XState { windowset = old } <- get
|
XState { windowset = old } <- get
|
||||||
@@ -144,7 +195,7 @@ windows f = do
|
|||||||
|
|
||||||
let m = W.floating ws
|
let m = W.floating ws
|
||||||
flt = [(fw, scaleRationalRect viewrect r)
|
flt = [(fw, scaleRationalRect viewrect r)
|
||||||
| fw <- filter (flip M.member m) (W.index this)
|
| fw <- filter (`M.member` m) (W.index this)
|
||||||
, Just r <- [M.lookup fw m]]
|
, Just r <- [M.lookup fw m]]
|
||||||
vs = flt ++ rs
|
vs = flt ++ rs
|
||||||
|
|
||||||
@@ -170,38 +221,58 @@ windows f = do
|
|||||||
-- all windows that are no longer in the windowset are marked as
|
-- all windows that are no longer in the windowset are marked as
|
||||||
-- withdrawn, it is important to do this after the above, otherwise 'hide'
|
-- withdrawn, it is important to do this after the above, otherwise 'hide'
|
||||||
-- will overwrite withdrawnState with iconicState
|
-- will overwrite withdrawnState with iconicState
|
||||||
mapM_ (flip setWMState withdrawnState) (W.allWindows old \\ W.allWindows ws)
|
mapM_ (`setWMState` withdrawnState) (W.allWindows old \\ W.allWindows ws)
|
||||||
|
|
||||||
isMouseFocused <- asks mouseFocused
|
isMouseFocused <- asks mouseFocused
|
||||||
unless isMouseFocused $ clearEvents enterWindowMask
|
unless isMouseFocused $ clearEvents enterWindowMask
|
||||||
asks (logHook . config) >>= userCodeDef ()
|
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 $ const old
|
||||||
|
windows $ const new
|
||||||
|
return a
|
||||||
|
|
||||||
|
-- | Perform an @X@ action. If it returns @Any True@, unwind the
|
||||||
|
-- changes to the @WindowSet@ and replay them using @windows@. This is
|
||||||
|
-- a version of @windowBracket@ that discards the return value and
|
||||||
|
-- handles an @X@ action that reports 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.
|
-- | Produce the actual rectangle from a screen and a ratio on that screen.
|
||||||
scaleRationalRect :: Rectangle -> W.RationalRect -> Rectangle
|
scaleRationalRect :: Rectangle -> W.RationalRect -> Rectangle
|
||||||
scaleRationalRect (Rectangle sx sy sw sh) (W.RationalRect rx ry rw rh)
|
scaleRationalRect (Rectangle sx sy sw sh) (W.RationalRect rx ry rw rh)
|
||||||
= Rectangle (sx + scale sw rx) (sy + scale sh ry) (scale sw rw) (scale sh rh)
|
= Rectangle (sx + scale sw rx) (sy + scale sh ry) (scale sw rw) (scale sh rh)
|
||||||
where scale s r = floor (toRational s * r)
|
where scale s r = floor (toRational s * r)
|
||||||
|
|
||||||
-- | setWMState. set the WM_STATE property
|
-- | Set a window's WM_STATE property.
|
||||||
setWMState :: Window -> Int -> X ()
|
setWMState :: Window -> Int -> X ()
|
||||||
setWMState w v = withDisplay $ \dpy -> do
|
setWMState w v = withDisplay $ \dpy -> do
|
||||||
a <- atom_WM_STATE
|
a <- atom_WM_STATE
|
||||||
io $ changeProperty32 dpy w a a propModeReplace [fromIntegral v, fromIntegral none]
|
io $ changeProperty32 dpy w a a propModeReplace [fromIntegral v, fromIntegral none]
|
||||||
|
|
||||||
-- | Set the border color using the window's color map, if possible,
|
-- | Set the border color using the window's color map, if possible;
|
||||||
-- otherwise fallback to the color in @Pixel@.
|
-- otherwise fall back to the color in @Pixel@.
|
||||||
setWindowBorderWithFallback :: Display -> Window -> String -> Pixel -> X ()
|
setWindowBorderWithFallback :: Display -> Window -> String -> Pixel -> X ()
|
||||||
setWindowBorderWithFallback dpy w color basic = io $
|
setWindowBorderWithFallback dpy w color basic = io $
|
||||||
C.handle fallback $ do
|
C.handle fallback $ do
|
||||||
wa <- getWindowAttributes dpy w
|
wa <- getWindowAttributes dpy w
|
||||||
pixel <- color_pixel . fst <$> allocNamedColor dpy (wa_colormap wa) color
|
pixel <- setPixelSolid . color_pixel . fst <$> allocNamedColor dpy (wa_colormap wa) color
|
||||||
setWindowBorder dpy w pixel
|
setWindowBorder dpy w pixel
|
||||||
where
|
where
|
||||||
fallback :: C.SomeException -> IO ()
|
fallback :: C.SomeException -> IO ()
|
||||||
fallback e = do hPrint stderr e >> hFlush stderr
|
fallback _ = setWindowBorder dpy w basic
|
||||||
setWindowBorder dpy w basic
|
|
||||||
|
|
||||||
-- | hide. Hide a window by unmapping it, and setting Iconified.
|
-- | Hide a window by unmapping it and setting Iconified.
|
||||||
hide :: Window -> X ()
|
hide :: Window -> X ()
|
||||||
hide w = whenX (gets (S.member w . mapped)) $ withDisplay $ \d -> do
|
hide w = whenX (gets (S.member w . mapped)) $ withDisplay $ \d -> do
|
||||||
cMask <- asks $ clientMask . config
|
cMask <- asks $ clientMask . config
|
||||||
@@ -214,15 +285,15 @@ hide w = whenX (gets (S.member w . mapped)) $ withDisplay $ \d -> do
|
|||||||
modify (\s -> s { waitingUnmap = M.insertWith (+) w 1 (waitingUnmap s)
|
modify (\s -> s { waitingUnmap = M.insertWith (+) w 1 (waitingUnmap s)
|
||||||
, mapped = S.delete w (mapped s) })
|
, mapped = S.delete w (mapped s) })
|
||||||
|
|
||||||
-- | reveal. Show a window by mapping it and setting Normal
|
-- | Show a window by mapping it and setting Normal.
|
||||||
-- this is harmless if the window was already visible
|
-- This is harmless if the window was already visible.
|
||||||
reveal :: Window -> X ()
|
reveal :: Window -> X ()
|
||||||
reveal w = withDisplay $ \d -> do
|
reveal w = withDisplay $ \d -> do
|
||||||
setWMState w normalState
|
setWMState w normalState
|
||||||
io $ mapWindow d w
|
io $ mapWindow d w
|
||||||
whenX (isClient w) $ modify (\s -> s { mapped = S.insert w (mapped s) })
|
whenX (isClient w) $ modify (\s -> s { mapped = S.insert w (mapped s) })
|
||||||
|
|
||||||
-- | Set some properties when we initially gain control of a window
|
-- | Set some properties when we initially gain control of a window.
|
||||||
setInitialProperties :: Window -> X ()
|
setInitialProperties :: Window -> X ()
|
||||||
setInitialProperties w = asks normalBorder >>= \nb -> withDisplay $ \d -> do
|
setInitialProperties w = asks normalBorder >>= \nb -> withDisplay $ \d -> do
|
||||||
setWMState w iconicState
|
setWMState w iconicState
|
||||||
@@ -233,7 +304,7 @@ setInitialProperties w = asks normalBorder >>= \nb -> withDisplay $ \d -> do
|
|||||||
-- required by the border setting in 'windows'
|
-- required by the border setting in 'windows'
|
||||||
io $ setWindowBorder d w nb
|
io $ setWindowBorder d w nb
|
||||||
|
|
||||||
-- | refresh. Render the currently visible workspaces, as determined by
|
-- | Render the currently visible workspaces, as determined by
|
||||||
-- the 'StackSet'. Also, set focus to the focused window.
|
-- the 'StackSet'. Also, set focus to the focused window.
|
||||||
--
|
--
|
||||||
-- This is our 'view' operation (MVC), in that it pretty prints our model
|
-- This is our 'view' operation (MVC), in that it pretty prints our model
|
||||||
@@ -242,7 +313,7 @@ setInitialProperties w = asks normalBorder >>= \nb -> withDisplay $ \d -> do
|
|||||||
refresh :: X ()
|
refresh :: X ()
|
||||||
refresh = windows id
|
refresh = windows id
|
||||||
|
|
||||||
-- | clearEvents. Remove all events of a given type from the event queue.
|
-- | Remove all events of a given type from the event queue.
|
||||||
clearEvents :: EventMask -> X ()
|
clearEvents :: EventMask -> X ()
|
||||||
clearEvents mask = withDisplay $ \d -> io $ do
|
clearEvents mask = withDisplay $ \d -> io $ do
|
||||||
sync d False
|
sync d False
|
||||||
@@ -250,8 +321,8 @@ clearEvents mask = withDisplay $ \d -> io $ do
|
|||||||
more <- checkMaskEvent d mask p
|
more <- checkMaskEvent d mask p
|
||||||
when more again -- beautiful
|
when more again -- beautiful
|
||||||
|
|
||||||
-- | tileWindow. Moves and resizes w such that it fits inside the given
|
-- | Move and resize @w@ such that it fits inside the given rectangle,
|
||||||
-- rectangle, including its border.
|
-- including its border.
|
||||||
tileWindow :: Window -> Rectangle -> X ()
|
tileWindow :: Window -> Rectangle -> X ()
|
||||||
tileWindow w r = withDisplay $ \d -> withWindowAttributes d w $ \wa -> do
|
tileWindow w r = withDisplay $ \d -> withWindowAttributes d w $ \wa -> do
|
||||||
-- give all windows at least 1x1 pixels
|
-- give all windows at least 1x1 pixels
|
||||||
@@ -278,27 +349,28 @@ containedIn r1@(Rectangle x1 y1 w1 h1) r2@(Rectangle x2 y2 w2 h2)
|
|||||||
nubScreens :: [Rectangle] -> [Rectangle]
|
nubScreens :: [Rectangle] -> [Rectangle]
|
||||||
nubScreens xs = nub . filter (\x -> not $ any (x `containedIn`) xs) $ xs
|
nubScreens xs = nub . filter (\x -> not $ any (x `containedIn`) xs) $ xs
|
||||||
|
|
||||||
-- | Cleans the list of screens according to the rules documented for
|
-- | Clean the list of screens according to the rules documented for
|
||||||
-- nubScreens.
|
-- nubScreens.
|
||||||
getCleanedScreenInfo :: MonadIO m => Display -> m [Rectangle]
|
getCleanedScreenInfo :: MonadIO m => Display -> m [Rectangle]
|
||||||
getCleanedScreenInfo = io . fmap nubScreens . getScreenInfo
|
getCleanedScreenInfo = io . fmap nubScreens . getScreenInfo
|
||||||
|
|
||||||
-- | rescreen. The screen configuration may have changed (due to
|
-- | The screen configuration may have changed (due to -- xrandr),
|
||||||
-- xrandr), update the state and refresh the screen, and reset the gap.
|
-- update the state and refresh the screen, and reset the gap.
|
||||||
rescreen :: X ()
|
rescreen :: X ()
|
||||||
rescreen = do
|
rescreen = withDisplay getCleanedScreenInfo >>= \case
|
||||||
xinesc <- withDisplay getCleanedScreenInfo
|
[] -> trace "getCleanedScreenInfo returned []"
|
||||||
|
xinesc:xinescs ->
|
||||||
windows $ \ws@(W.StackSet { W.current = v, W.visible = vs, W.hidden = hs }) ->
|
windows $ \ws@W.StackSet{ W.current = v, W.visible = vs, W.hidden = hs } ->
|
||||||
let (xs, ys) = splitAt (length xinesc) $ map W.workspace (v:vs) ++ hs
|
let (xs, ys) = splitAt (length xinescs) (map W.workspace vs ++ hs)
|
||||||
(a:as) = zipWith3 W.Screen xs [0..] $ map SD xinesc
|
a = W.Screen (W.workspace v) 0 (SD xinesc)
|
||||||
in ws { W.current = a
|
as = zipWith3 W.Screen xs [1..] $ map SD xinescs
|
||||||
, W.visible = as
|
in ws { W.current = a
|
||||||
, W.hidden = ys }
|
, W.visible = as
|
||||||
|
, W.hidden = ys }
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
|
|
||||||
-- | setButtonGrab. Tell whether or not to intercept clicks on a given window
|
-- | Tell whether or not to intercept clicks on a given window
|
||||||
setButtonGrab :: Bool -> Window -> X ()
|
setButtonGrab :: Bool -> Window -> X ()
|
||||||
setButtonGrab grab w = do
|
setButtonGrab grab w = do
|
||||||
pointerMode <- asks $ \c -> if clickJustFocuses (config c)
|
pointerMode <- asks $ \c -> if clickJustFocuses (config c)
|
||||||
@@ -353,7 +425,7 @@ setFocusX w = withWindowSet $ \ws -> do
|
|||||||
currevt <- asks currentEvent
|
currevt <- asks currentEvent
|
||||||
let inputHintSet = wmh_flags hints `testBit` inputHintBit
|
let inputHintSet = wmh_flags hints `testBit` inputHintBit
|
||||||
|
|
||||||
when ((inputHintSet && wmh_input hints) || (not inputHintSet)) $
|
when (inputHintSet && wmh_input hints || not inputHintSet) $
|
||||||
io $ do setInputFocus dpy w revertToPointerRoot 0
|
io $ do setInputFocus dpy w revertToPointerRoot 0
|
||||||
when (wmtf `elem` protocols) $
|
when (wmtf `elem` protocols) $
|
||||||
io $ allocaXEvent $ \ev -> do
|
io $ allocaXEvent $ \ev -> do
|
||||||
@@ -361,56 +433,96 @@ setFocusX w = withWindowSet $ \ws -> do
|
|||||||
setClientMessageEvent ev w wmprot 32 wmtf $ maybe currentTime event_time currevt
|
setClientMessageEvent ev w wmprot 32 wmtf $ maybe currentTime event_time currevt
|
||||||
sendEvent dpy w False noEventMask ev
|
sendEvent dpy w False noEventMask ev
|
||||||
where event_time ev =
|
where event_time ev =
|
||||||
if (ev_event_type ev) `elem` timedEvents then
|
if ev_event_type ev `elem` timedEvents then
|
||||||
ev_time ev
|
ev_time ev
|
||||||
else
|
else
|
||||||
currentTime
|
currentTime
|
||||||
timedEvents = [ keyPress, keyRelease, buttonPress, buttonRelease, enterNotify, leaveNotify, selectionRequest ]
|
timedEvents = [ keyPress, keyRelease, buttonPress, buttonRelease, enterNotify, leaveNotify, selectionRequest ]
|
||||||
|
|
||||||
|
cacheNumlockMask :: X ()
|
||||||
|
cacheNumlockMask = do
|
||||||
|
dpy <- asks display
|
||||||
|
ms <- io $ getModifierMapping dpy
|
||||||
|
xs <- sequence [ do ks <- io $ keycodeToKeysym dpy kc 0
|
||||||
|
if ks == xK_Num_Lock
|
||||||
|
then return (setBit 0 (fromIntegral m))
|
||||||
|
else return (0 :: KeyMask)
|
||||||
|
| (m, kcs) <- ms, kc <- kcs, kc /= 0
|
||||||
|
]
|
||||||
|
modify (\s -> s { numberlockMask = foldr (.|.) 0 xs })
|
||||||
|
|
||||||
|
-- | Given a list of keybindings, turn the given 'KeySym's into actual
|
||||||
|
-- 'KeyCode's and prepare them for grabbing.
|
||||||
|
mkGrabs :: [(KeyMask, KeySym)] -> X [(KeyMask, KeyCode)]
|
||||||
|
mkGrabs ks = withDisplay $ \dpy -> do
|
||||||
|
let (minCode, maxCode) = displayKeycodes dpy
|
||||||
|
allCodes = [fromIntegral minCode .. fromIntegral maxCode]
|
||||||
|
-- build a map from keysyms to lists of keysyms (doing what
|
||||||
|
-- XGetKeyboardMapping would do if the X11 package bound it)
|
||||||
|
syms <- forM allCodes $ \code -> io (keycodeToKeysym dpy code 0)
|
||||||
|
let -- keycodeToKeysym returns noSymbol for all unbound keycodes,
|
||||||
|
-- and we don't want to grab those whenever someone accidentally
|
||||||
|
-- uses def :: KeySym
|
||||||
|
keysymMap = M.delete noSymbol $
|
||||||
|
M.fromListWith (++) (zip syms [[code] | code <- allCodes])
|
||||||
|
keysymToKeycodes sym = M.findWithDefault [] sym keysymMap
|
||||||
|
extraMods <- extraModifiers
|
||||||
|
pure [ (mask .|. extraMod, keycode)
|
||||||
|
| (mask, sym) <- ks
|
||||||
|
, keycode <- keysymToKeycodes sym
|
||||||
|
, extraMod <- extraMods
|
||||||
|
]
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
-- Message handling
|
-- Message handling
|
||||||
|
|
||||||
-- | Throw a message to the current 'LayoutClass' possibly modifying how we
|
-- | Throw a message to the current 'LayoutClass' possibly modifying how we
|
||||||
-- layout the windows, then refresh.
|
-- layout the windows, in which case changes are handled through a refresh.
|
||||||
sendMessage :: Message a => a -> X ()
|
sendMessage :: Message a => a -> X ()
|
||||||
sendMessage a = do
|
sendMessage a = windowBracket_ $ do
|
||||||
w <- W.workspace . W.current <$> gets windowset
|
w <- gets $ W.workspace . W.current . windowset
|
||||||
ml' <- handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing
|
ml' <- handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing
|
||||||
whenJust ml' $ \l' ->
|
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.workspace = (W.workspace $ W.current ws)
|
||||||
{ W.layout = l' }}}
|
{ W.layout = l' }}}
|
||||||
|
return (Any $ isJust ml')
|
||||||
|
|
||||||
-- | Send a message to all layouts, without refreshing.
|
-- | Send a message to all layouts, without refreshing.
|
||||||
broadcastMessage :: Message a => a -> X ()
|
broadcastMessage :: Message a => a -> X ()
|
||||||
broadcastMessage a = withWindowSet $ \ws -> do
|
broadcastMessage a = withWindowSet $ \ws -> do
|
||||||
let c = W.workspace . W.current $ ws
|
-- this is O(n²), but we can't really fix this as there's code in
|
||||||
v = map W.workspace . W.visible $ ws
|
-- xmonad-contrib that touches the windowset during handleMessage
|
||||||
h = W.hidden ws
|
-- (returning Nothing for changes to not get overwritten), so we
|
||||||
mapM_ (sendMessageWithNoRefresh a) (c : v ++ h)
|
-- unfortunately need to do this one by one and persist layout states
|
||||||
|
-- of each workspace separately)
|
||||||
|
let c = W.workspace . W.current $ ws
|
||||||
|
v = map W.workspace . W.visible $ ws
|
||||||
|
h = W.hidden ws
|
||||||
|
mapM_ (sendMessageWithNoRefresh a) (c : v ++ h)
|
||||||
|
|
||||||
-- | Send a message to a layout, without refreshing.
|
-- | Send a message to a layout, without refreshing.
|
||||||
sendMessageWithNoRefresh :: Message a => a -> W.Workspace WorkspaceId (Layout Window) Window -> X ()
|
sendMessageWithNoRefresh :: Message a => a -> WindowSpace -> X ()
|
||||||
sendMessageWithNoRefresh a w =
|
sendMessageWithNoRefresh a w =
|
||||||
handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing >>=
|
handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing >>=
|
||||||
updateLayout (W.tag w)
|
updateLayout (W.tag w)
|
||||||
|
|
||||||
-- | Update the layout field of a workspace
|
-- | Update the layout field of a workspace.
|
||||||
updateLayout :: WorkspaceId -> Maybe (Layout Window) -> X ()
|
updateLayout :: WorkspaceId -> Maybe (Layout Window) -> X ()
|
||||||
updateLayout i ml = whenJust ml $ \l ->
|
updateLayout i ml = whenJust ml $ \l ->
|
||||||
runOnWorkspaces $ \ww -> return $ if W.tag ww == i then ww { W.layout = l} else ww
|
runOnWorkspaces $ \ww -> return $ if W.tag ww == i then ww { W.layout = l} else ww
|
||||||
|
|
||||||
-- | Set the layout of the currently viewed workspace
|
-- | Set the layout of the currently viewed workspace.
|
||||||
setLayout :: Layout Window -> X ()
|
setLayout :: Layout Window -> X ()
|
||||||
setLayout l = do
|
setLayout l = do
|
||||||
ss@(W.StackSet { W.current = c@(W.Screen { W.workspace = ws })}) <- gets windowset
|
ss@W.StackSet{ W.current = c@W.Screen{ W.workspace = ws }} <- gets windowset
|
||||||
handleMessage (W.layout ws) (SomeMessage ReleaseResources)
|
handleMessage (W.layout ws) (SomeMessage ReleaseResources)
|
||||||
windows $ const $ ss {W.current = c { W.workspace = ws { W.layout = l } } }
|
windows $ const $ ss{ W.current = c{ W.workspace = ws{ W.layout = l } } }
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
-- Utilities
|
-- Utilities
|
||||||
|
|
||||||
-- | Return workspace visible on screen 'sc', or 'Nothing'.
|
-- | Return workspace visible on screen @sc@, or 'Nothing'.
|
||||||
screenWorkspace :: ScreenId -> X (Maybe WorkspaceId)
|
screenWorkspace :: ScreenId -> X (Maybe WorkspaceId)
|
||||||
screenWorkspace sc = withWindowSet $ return . W.lookupWorkspace sc
|
screenWorkspace sc = withWindowSet $ return . W.lookupWorkspace sc
|
||||||
|
|
||||||
@@ -418,7 +530,14 @@ screenWorkspace sc = withWindowSet $ return . W.lookupWorkspace sc
|
|||||||
withFocused :: (Window -> X ()) -> X ()
|
withFocused :: (Window -> X ()) -> X ()
|
||||||
withFocused f = withWindowSet $ \w -> whenJust (W.peek w) f
|
withFocused f = withWindowSet $ \w -> whenJust (W.peek w) f
|
||||||
|
|
||||||
-- | 'True' if window is under management by us
|
-- | Apply an 'X' operation to all unfocused windows on the current workspace, if there are any.
|
||||||
|
withUnfocused :: (Window -> X ()) -> X ()
|
||||||
|
withUnfocused f = withWindowSet $ \ws ->
|
||||||
|
whenJust (W.peek ws) $ \w ->
|
||||||
|
let unfocusedWindows = filter (/= w) $ W.index ws
|
||||||
|
in mapM_ f unfocusedWindows
|
||||||
|
|
||||||
|
-- | Is the window is under management by xmonad?
|
||||||
isClient :: Window -> X Bool
|
isClient :: Window -> X Bool
|
||||||
isClient w = withWindowSet $ return . W.member w
|
isClient w = withWindowSet $ return . W.member w
|
||||||
|
|
||||||
@@ -429,16 +548,20 @@ extraModifiers = do
|
|||||||
nlm <- gets numberlockMask
|
nlm <- gets numberlockMask
|
||||||
return [0, nlm, lockMask, nlm .|. lockMask ]
|
return [0, nlm, lockMask, nlm .|. lockMask ]
|
||||||
|
|
||||||
-- | Strip numlock\/capslock from a mask
|
-- | Strip numlock\/capslock from a mask.
|
||||||
cleanMask :: KeyMask -> X KeyMask
|
cleanMask :: KeyMask -> X KeyMask
|
||||||
cleanMask km = do
|
cleanMask km = do
|
||||||
nlm <- gets numberlockMask
|
nlm <- gets numberlockMask
|
||||||
return (complement (nlm .|. lockMask) .&. km)
|
return (complement (nlm .|. lockMask) .&. km)
|
||||||
|
|
||||||
-- | Get the 'Pixel' value for a named color
|
-- | Set the 'Pixel' alpha value to 255.
|
||||||
|
setPixelSolid :: Pixel -> Pixel
|
||||||
|
setPixelSolid p = p .|. 0xff000000
|
||||||
|
|
||||||
|
-- | Get the 'Pixel' value for a named color.
|
||||||
initColor :: Display -> String -> IO (Maybe Pixel)
|
initColor :: Display -> String -> IO (Maybe Pixel)
|
||||||
initColor dpy c = C.handle (\(C.SomeException _) -> return Nothing) $
|
initColor dpy c = C.handle (\(C.SomeException _) -> return Nothing) $
|
||||||
(Just . color_pixel . fst) <$> allocNamedColor dpy colormap c
|
Just . setPixelSolid . color_pixel . fst <$> allocNamedColor dpy colormap c
|
||||||
where colormap = defaultColormap dpy (defaultScreen dpy)
|
where colormap = defaultColormap dpy (defaultScreen dpy)
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
@@ -458,9 +581,9 @@ writeStateToFile = do
|
|||||||
maybeShow _ = Nothing
|
maybeShow _ = Nothing
|
||||||
|
|
||||||
wsData = W.mapLayout show . windowset
|
wsData = W.mapLayout show . windowset
|
||||||
extState = catMaybes . map maybeShow . M.toList . extensibleState
|
extState = mapMaybe maybeShow . M.toList . extensibleState
|
||||||
|
|
||||||
path <- stateFileName
|
path <- asks $ stateFileName . directories
|
||||||
stateData <- gets (\s -> StateFile (wsData s) (extState s))
|
stateData <- gets (\s -> StateFile (wsData s) (extState s))
|
||||||
catchIO (writeFile path $ show stateData)
|
catchIO (writeFile path $ show stateData)
|
||||||
|
|
||||||
@@ -468,7 +591,7 @@ writeStateToFile = do
|
|||||||
-- return that state. The state file is removed after reading it.
|
-- return that state. The state file is removed after reading it.
|
||||||
readStateFile :: (LayoutClass l Window, Read (l Window)) => XConfig l -> X (Maybe XState)
|
readStateFile :: (LayoutClass l Window, Read (l Window)) => XConfig l -> X (Maybe XState)
|
||||||
readStateFile xmc = do
|
readStateFile xmc = do
|
||||||
path <- stateFileName
|
path <- asks $ stateFileName . directories
|
||||||
|
|
||||||
-- I'm trying really hard here to make sure we read the entire
|
-- 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.
|
-- contents of the file before it is removed from the file system.
|
||||||
@@ -501,23 +624,8 @@ readStateFile xmc = do
|
|||||||
readStrict :: Handle -> IO String
|
readStrict :: Handle -> IO String
|
||||||
readStrict h = hGetContents h >>= \s -> length s `seq` return s
|
readStrict h = hGetContents h >>= \s -> length s `seq` return s
|
||||||
|
|
||||||
-- | Migrate state from a previously running xmonad instance that used
|
-- | @restart name resume@ attempts to restart xmonad by executing the program
|
||||||
-- the older @--resume@ technique.
|
-- @name@. If @resume@ is 'True', restart with the current window state.
|
||||||
{-# 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'.
|
-- When executing another window manager, @resume@ should be 'False'.
|
||||||
restart :: String -> Bool -> X ()
|
restart :: String -> Bool -> X ()
|
||||||
restart prog resume = do
|
restart prog resume = do
|
||||||
@@ -527,33 +635,47 @@ restart prog resume = do
|
|||||||
catchIO (executeFile prog True [] Nothing)
|
catchIO (executeFile prog True [] Nothing)
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
-- | Floating layer support
|
-- Floating layer support
|
||||||
|
|
||||||
-- | Given a window, find the screen it is located on, and compute
|
-- | Given a window, find the screen it is located on, and compute
|
||||||
-- the geometry of that window wrt. that screen.
|
-- the geometry of that window WRT that screen.
|
||||||
floatLocation :: Window -> X (ScreenId, W.RationalRect)
|
floatLocation :: Window -> X (ScreenId, W.RationalRect)
|
||||||
floatLocation w =
|
floatLocation w =
|
||||||
catchX go $ do
|
catchX go $ do
|
||||||
-- Fallback solution if `go' fails. Which it might, since it
|
-- Fallback solution if `go' fails. Which it might, since it
|
||||||
-- calls `getWindowAttributes'.
|
-- calls `getWindowAttributes'.
|
||||||
sc <- W.current <$> gets windowset
|
sc <- gets $ W.current . windowset
|
||||||
return (W.screen sc, W.RationalRect 0 0 1 1)
|
return (W.screen sc, W.RationalRect 0 0 1 1)
|
||||||
|
|
||||||
where fi x = fromIntegral x
|
where go = withDisplay $ \d -> do
|
||||||
go = withDisplay $ \d -> do
|
|
||||||
ws <- gets windowset
|
ws <- gets windowset
|
||||||
wa <- io $ getWindowAttributes d w
|
wa <- io $ getWindowAttributes d w
|
||||||
let bw = (fromIntegral . wa_border_width) wa
|
let bw = (fromIntegral . wa_border_width) wa
|
||||||
sc <- fromMaybe (W.current ws) <$> pointScreen (fi $ wa_x wa) (fi $ wa_y wa)
|
point_sc <- pointScreen (fi $ wa_x wa) (fi $ wa_y wa)
|
||||||
|
managed <- isClient w
|
||||||
|
|
||||||
let sr = screenRect . W.screenDetail $ sc
|
-- ignore pointScreen for new windows unless it's the current
|
||||||
rr = W.RationalRect ((fi (wa_x wa) - fi (rect_x sr)) % fi (rect_width sr))
|
-- screen, otherwise the float's relative size is computed against
|
||||||
((fi (wa_y wa) - fi (rect_y sr)) % fi (rect_height sr))
|
-- a different screen and the float ends up with the wrong size
|
||||||
(fi (wa_width wa + bw*2) % fi (rect_width sr))
|
let sr_eq = (==) `on` fmap (screenRect . W.screenDetail)
|
||||||
(fi (wa_height wa + bw*2) % fi (rect_height sr))
|
sc = fromMaybe (W.current ws) $
|
||||||
|
if managed || point_sc `sr_eq` Just (W.current ws) then point_sc else Nothing
|
||||||
|
sr = screenRect . W.screenDetail $ sc
|
||||||
|
x = (fi (wa_x wa) - fi (rect_x sr)) % fi (rect_width sr)
|
||||||
|
y = (fi (wa_y wa) - fi (rect_y sr)) % fi (rect_height sr)
|
||||||
|
width = fi (wa_width wa + bw*2) % fi (rect_width sr)
|
||||||
|
height = fi (wa_height wa + bw*2) % fi (rect_height sr)
|
||||||
|
-- adjust x/y of unmanaged windows if we ignored or didn't get pointScreen,
|
||||||
|
-- it might be out of bounds otherwise
|
||||||
|
rr = if managed || point_sc `sr_eq` Just sc
|
||||||
|
then W.RationalRect x y width height
|
||||||
|
else W.RationalRect (0.5 - width/2) (0.5 - height/2) width height
|
||||||
|
|
||||||
return (W.screen sc, rr)
|
return (W.screen sc, rr)
|
||||||
|
|
||||||
|
fi :: (Integral a, Num b) => a -> b
|
||||||
|
fi = fromIntegral
|
||||||
|
|
||||||
-- | Given a point, determine the screen (if any) that contains it.
|
-- | Given a point, determine the screen (if any) that contains it.
|
||||||
pointScreen :: Position -> Position
|
pointScreen :: Position -> Position
|
||||||
-> X (Maybe (W.Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail))
|
-> X (Maybe (W.Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail))
|
||||||
@@ -584,14 +706,20 @@ float w = do
|
|||||||
|
|
||||||
-- | Accumulate mouse motion events
|
-- | Accumulate mouse motion events
|
||||||
mouseDrag :: (Position -> Position -> X ()) -> X () -> X ()
|
mouseDrag :: (Position -> Position -> X ()) -> X () -> X ()
|
||||||
mouseDrag f done = do
|
mouseDrag = mouseDragCursor Nothing
|
||||||
|
|
||||||
|
-- | Like 'mouseDrag', but with the ability to specify a custom cursor
|
||||||
|
-- shape.
|
||||||
|
mouseDragCursor :: Maybe Glyph -> (Position -> Position -> X ()) -> X () -> X ()
|
||||||
|
mouseDragCursor cursorGlyph f done = do
|
||||||
drag <- gets dragging
|
drag <- gets dragging
|
||||||
case drag of
|
case drag of
|
||||||
Just _ -> return () -- error case? we're already dragging
|
Just _ -> return () -- error case? we're already dragging
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
XConf { theRoot = root, display = d } <- ask
|
XConf { theRoot = root, display = d } <- ask
|
||||||
io $ grabPointer d root False (buttonReleaseMask .|. pointerMotionMask)
|
io $ do cursor <- maybe (pure none) (createFontCursor d) cursorGlyph
|
||||||
grabModeAsync grabModeAsync none none currentTime
|
grabPointer d root False (buttonReleaseMask .|. pointerMotionMask)
|
||||||
|
grabModeAsync grabModeAsync none cursor currentTime
|
||||||
modify $ \s -> s { dragging = Just (motion, cleanup) }
|
modify $ \s -> s { dragging = Just (motion, cleanup) }
|
||||||
where
|
where
|
||||||
cleanup = do
|
cleanup = do
|
||||||
@@ -602,39 +730,41 @@ mouseDrag f done = do
|
|||||||
clearEvents pointerMotionMask
|
clearEvents pointerMotionMask
|
||||||
return z
|
return z
|
||||||
|
|
||||||
-- | drag the window under the cursor with the mouse while it is dragged
|
-- | Drag the window under the cursor with the mouse while it is dragged.
|
||||||
mouseMoveWindow :: Window -> X ()
|
mouseMoveWindow :: Window -> X ()
|
||||||
mouseMoveWindow w = whenX (isClient w) $ withDisplay $ \d -> do
|
mouseMoveWindow w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||||
io $ raiseWindow d w
|
|
||||||
wa <- io $ getWindowAttributes d w
|
wa <- io $ getWindowAttributes d w
|
||||||
(_, _, _, ox', oy', _, _, _) <- io $ queryPointer d w
|
(_, _, _, ox', oy', _, _, _) <- io $ queryPointer d w
|
||||||
let ox = fromIntegral ox'
|
let ox = fromIntegral ox'
|
||||||
oy = fromIntegral oy'
|
oy = fromIntegral oy'
|
||||||
mouseDrag (\ex ey -> do
|
mouseDragCursor
|
||||||
|
(Just xC_fleur)
|
||||||
|
(\ex ey -> do
|
||||||
io $ moveWindow d w (fromIntegral (fromIntegral (wa_x wa) + (ex - ox)))
|
io $ moveWindow d w (fromIntegral (fromIntegral (wa_x wa) + (ex - ox)))
|
||||||
(fromIntegral (fromIntegral (wa_y wa) + (ey - oy)))
|
(fromIntegral (fromIntegral (wa_y wa) + (ey - oy)))
|
||||||
float w
|
float w
|
||||||
)
|
)
|
||||||
(float w)
|
(float w)
|
||||||
|
|
||||||
-- | resize the window under the cursor with the mouse while it is dragged
|
-- | Resize the window under the cursor with the mouse while it is dragged.
|
||||||
mouseResizeWindow :: Window -> X ()
|
mouseResizeWindow :: Window -> X ()
|
||||||
mouseResizeWindow w = whenX (isClient w) $ withDisplay $ \d -> do
|
mouseResizeWindow w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||||
io $ raiseWindow d w
|
|
||||||
wa <- io $ getWindowAttributes d w
|
wa <- io $ getWindowAttributes d w
|
||||||
sh <- io $ getWMNormalHints d w
|
sh <- io $ getWMNormalHints d w
|
||||||
io $ warpPointer d none w 0 0 0 0 (fromIntegral (wa_width wa)) (fromIntegral (wa_height wa))
|
io $ warpPointer d none w 0 0 0 0 (fromIntegral (wa_width wa)) (fromIntegral (wa_height wa))
|
||||||
mouseDrag (\ex ey -> do
|
mouseDragCursor
|
||||||
|
(Just xC_bottom_right_corner)
|
||||||
|
(\ex ey -> do
|
||||||
io $ resizeWindow d w `uncurry`
|
io $ resizeWindow d w `uncurry`
|
||||||
applySizeHintsContents sh (ex - fromIntegral (wa_x wa),
|
applySizeHintsContents sh (ex - fromIntegral (wa_x wa),
|
||||||
ey - fromIntegral (wa_y wa))
|
ey - fromIntegral (wa_y wa))
|
||||||
float w)
|
float w)
|
||||||
|
|
||||||
(float w)
|
(float w)
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
-- | Support for window size hints
|
-- Support for window size hints
|
||||||
|
|
||||||
|
-- | An alias for a (width, height) pair
|
||||||
type D = (Dimension, Dimension)
|
type D = (Dimension, Dimension)
|
||||||
|
|
||||||
-- | Given a window, build an adjuster function that will reduce the given
|
-- | Given a window, build an adjuster function that will reduce the given
|
||||||
@@ -644,7 +774,7 @@ mkAdjust w = withDisplay $ \d -> liftIO $ do
|
|||||||
sh <- getWMNormalHints d w
|
sh <- getWMNormalHints d w
|
||||||
wa <- C.try $ getWindowAttributes d w
|
wa <- C.try $ getWindowAttributes d w
|
||||||
case wa of
|
case wa of
|
||||||
Left err -> const (return id) (err :: C.SomeException)
|
Left (_ :: C.SomeException) -> return id
|
||||||
Right wa' ->
|
Right wa' ->
|
||||||
let bw = fromIntegral $ wa_border_width wa'
|
let bw = fromIntegral $ wa_border_width wa'
|
||||||
in return $ applySizeHints bw sh
|
in return $ applySizeHints bw sh
|
||||||
@@ -662,7 +792,7 @@ applySizeHintsContents :: Integral a => SizeHints -> (a, a) -> D
|
|||||||
applySizeHintsContents sh (w, h) =
|
applySizeHintsContents sh (w, h) =
|
||||||
applySizeHints' sh (fromIntegral $ max 1 w, fromIntegral $ max 1 h)
|
applySizeHints' sh (fromIntegral $ max 1 w, fromIntegral $ max 1 h)
|
||||||
|
|
||||||
-- | XXX comment me
|
-- | Use X11 size hints to scale a pair of dimensions.
|
||||||
applySizeHints' :: SizeHints -> D -> D
|
applySizeHints' :: SizeHints -> D -> D
|
||||||
applySizeHints' sh =
|
applySizeHints' sh =
|
||||||
maybe id applyMaxSizeHint (sh_max_size sh)
|
maybe id applyMaxSizeHint (sh_max_size sh)
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
{-# LANGUAGE PatternGuards #-}
|
{-# LANGUAGE PatternGuards #-}
|
||||||
|
{-# LANGUAGE DeriveFunctor #-}
|
||||||
|
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
@@ -52,9 +53,13 @@ module XMonad.StackSet (
|
|||||||
) where
|
) where
|
||||||
|
|
||||||
import Prelude hiding (filter)
|
import Prelude hiding (filter)
|
||||||
|
import Control.Applicative.Backwards (Backwards (Backwards, forwards))
|
||||||
|
import Data.Foldable (foldr, toList)
|
||||||
import Data.Maybe (listToMaybe,isJust,fromMaybe)
|
import Data.Maybe (listToMaybe,isJust,fromMaybe)
|
||||||
import qualified Data.List as L (deleteBy,find,splitAt,filter,nub)
|
import qualified Data.List as L (deleteBy,find,splitAt,filter,nub)
|
||||||
import Data.List ( (\\) )
|
import Data.List ( (\\) )
|
||||||
|
import qualified Data.List.NonEmpty as NE
|
||||||
|
import Data.List.NonEmpty (NonEmpty((:|)))
|
||||||
import qualified Data.Map as M (Map,insert,delete,empty)
|
import qualified Data.Map as M (Map,insert,delete,empty)
|
||||||
|
|
||||||
-- $intro
|
-- $intro
|
||||||
@@ -85,25 +90,24 @@ import qualified Data.Map as M (Map,insert,delete,empty)
|
|||||||
-- continuation reified as a data structure.
|
-- continuation reified as a data structure.
|
||||||
--
|
--
|
||||||
-- The Zipper lets us replace an item deep in a complex data
|
-- The Zipper lets us replace an item deep in a complex data
|
||||||
-- structure, e.g., a tree or a term, without an mutation. The
|
-- structure, e.g., a tree or a term, without a mutation. The
|
||||||
-- resulting data structure will share as much of its components with
|
-- resulting data structure will share as much of its components with
|
||||||
-- the old structure as possible.
|
-- the old structure as possible.
|
||||||
--
|
--
|
||||||
-- Oleg Kiselyov, 27 Apr 2005, haskell\@, "Zipper as a delimited continuation"
|
-- <https://mail.haskell.org/pipermail/haskell/2005-April/015769.html Oleg Kiselyov, 27 Apr 2005, haskell\@, "Zipper as a delimited continuation">
|
||||||
--
|
--
|
||||||
-- We use the zipper to keep track of the focused workspace and the
|
-- We use the zipper to keep track of the focused workspace and the
|
||||||
-- focused window on each workspace, allowing us to have correct focus
|
-- focused window on each workspace, allowing us to have correct focus
|
||||||
-- by construction. We closely follow Huet's original implementation:
|
-- by construction. We closely follow Huet's original implementation:
|
||||||
--
|
--
|
||||||
-- G. Huet, /Functional Pearl: The Zipper/,
|
-- <https://www.st.cs.uni-saarland.de/edu/seminare/2005/advanced-fp/docs/huet-zipper.pdf G. Huet, Functional Pearl: The Zipper; 1997, J. Functional Programming 75(5):549–554>
|
||||||
-- 1997, J. Functional Programming 75(5):549-554.
|
--
|
||||||
-- and:
|
-- and
|
||||||
-- R. Hinze and J. Jeuring, /Functional Pearl: The Web/.
|
--
|
||||||
|
-- <https://dspace.library.uu.nl/handle/1874/2532 R. Hinze and J. Jeuring, Functional Pearl: Weaving a Web>
|
||||||
--
|
--
|
||||||
-- and Conor McBride's zipper differentiation paper.
|
-- and Conor McBride's zipper differentiation paper.
|
||||||
-- Another good reference is:
|
-- Another good reference is: <https://wiki.haskell.org/Zipper The Zipper, Haskell wikibook>
|
||||||
--
|
|
||||||
-- The Zipper, Haskell wikibook
|
|
||||||
|
|
||||||
-- $xinerama
|
-- $xinerama
|
||||||
-- Xinerama in X11 lets us view multiple virtual workspaces
|
-- Xinerama in X11 lets us view multiple virtual workspaces
|
||||||
@@ -151,7 +155,7 @@ data Workspace i l a = Workspace { tag :: !i, layout :: l, stack :: Maybe (Stac
|
|||||||
deriving (Show, Read, Eq)
|
deriving (Show, Read, Eq)
|
||||||
|
|
||||||
-- | A structure for window geometries
|
-- | A structure for window geometries
|
||||||
data RationalRect = RationalRect Rational Rational Rational Rational
|
data RationalRect = RationalRect !Rational !Rational !Rational !Rational
|
||||||
deriving (Show, Read, Eq)
|
deriving (Show, Read, Eq)
|
||||||
|
|
||||||
-- |
|
-- |
|
||||||
@@ -175,8 +179,19 @@ data RationalRect = RationalRect Rational Rational Rational Rational
|
|||||||
data Stack a = Stack { focus :: !a -- focused thing in this set
|
data Stack a = Stack { focus :: !a -- focused thing in this set
|
||||||
, up :: [a] -- clowns to the left
|
, up :: [a] -- clowns to the left
|
||||||
, down :: [a] } -- jokers to the right
|
, down :: [a] } -- jokers to the right
|
||||||
deriving (Show, Read, Eq)
|
deriving (Show, Read, Eq, Functor)
|
||||||
|
|
||||||
|
instance Foldable Stack where
|
||||||
|
toList = integrate
|
||||||
|
foldr f z = foldr f z . toList
|
||||||
|
|
||||||
|
instance Traversable Stack where
|
||||||
|
traverse f s =
|
||||||
|
flip Stack
|
||||||
|
-- 'Backwards' applies the Applicative in reverse order.
|
||||||
|
<$> forwards (traverse (Backwards . f) (up s))
|
||||||
|
<*> f (focus s)
|
||||||
|
<*> traverse f (down s)
|
||||||
|
|
||||||
-- | this function indicates to catch that an error is expected
|
-- | this function indicates to catch that an error is expected
|
||||||
abort :: String -> a
|
abort :: String -> a
|
||||||
@@ -194,10 +209,11 @@ abort x = error $ "xmonad: StackSet: " ++ x
|
|||||||
-- Xinerama: Virtual workspaces are assigned to physical screens, starting at 0.
|
-- Xinerama: Virtual workspaces are assigned to physical screens, starting at 0.
|
||||||
--
|
--
|
||||||
new :: (Integral s) => l -> [i] -> [sd] -> StackSet i l a s sd
|
new :: (Integral s) => l -> [i] -> [sd] -> StackSet i l a s sd
|
||||||
new l wids m | not (null wids) && length m <= length wids && not (null m)
|
new l (wid:wids) (m:ms) | length ms <= length wids
|
||||||
= StackSet cur visi unseen M.empty
|
= StackSet cur visi (map ws unseen) M.empty
|
||||||
where (seen,unseen) = L.splitAt (length m) $ map (\i -> Workspace i l Nothing) wids
|
where ws i = Workspace i l Nothing
|
||||||
(cur:visi) = [ Screen i s sd | (i, s, sd) <- zip3 seen [0..] m ]
|
(seen, unseen) = L.splitAt (length ms) wids
|
||||||
|
cur:visi = Screen (ws wid) 0 m : [ Screen (ws i) s sd | (i, s, sd) <- zip3 seen [1..] ms ]
|
||||||
-- now zip up visibles with their screen id
|
-- now zip up visibles with their screen id
|
||||||
new _ _ _ = abort "non-positive argument to StackSet.new"
|
new _ _ _ = abort "non-positive argument to StackSet.new"
|
||||||
|
|
||||||
@@ -224,7 +240,7 @@ view i s
|
|||||||
|
|
||||||
| otherwise = s -- not a member of the stackset
|
| otherwise = s -- not a member of the stackset
|
||||||
|
|
||||||
where equating f = \x y -> f x == f y
|
where equating f x y = f x == f y
|
||||||
|
|
||||||
-- 'Catch'ing this might be hard. Relies on monotonically increasing
|
-- 'Catch'ing this might be hard. Relies on monotonically increasing
|
||||||
-- workspace tags defined in 'new'
|
-- workspace tags defined in 'new'
|
||||||
@@ -297,7 +313,7 @@ integrate :: Stack a -> [a]
|
|||||||
integrate (Stack x l r) = reverse l ++ x : r
|
integrate (Stack x l r) = reverse l ++ x : r
|
||||||
|
|
||||||
-- |
|
-- |
|
||||||
-- /O(n)/ Flatten a possibly empty stack into a list.
|
-- /O(n)/. Flatten a possibly empty stack into a list.
|
||||||
integrate' :: Maybe (Stack a) -> [a]
|
integrate' :: Maybe (Stack a) -> [a]
|
||||||
integrate' = maybe [] integrate
|
integrate' = maybe [] integrate
|
||||||
|
|
||||||
@@ -329,32 +345,44 @@ filter p (Stack f ls rs) = case L.filter p (f:rs) of
|
|||||||
index :: StackSet i l a s sd -> [a]
|
index :: StackSet i l a s sd -> [a]
|
||||||
index = with [] integrate
|
index = with [] integrate
|
||||||
|
|
||||||
-- |
|
-- | /O(1), O(w) on the wrapping case/. Move the window focus up the
|
||||||
-- /O(1), O(w) on the wrapping case/.
|
-- stack, wrapping if we reach the end. The wrapping should model a
|
||||||
--
|
-- @cycle@ on the current stack. The @master@ window and window order
|
||||||
-- focusUp, focusDown. Move the window focus up or down the stack,
|
|
||||||
-- wrapping if we reach the end. The wrapping should model a 'cycle'
|
|
||||||
-- on the current stack. The 'master' window, and window order,
|
|
||||||
-- are unaffected by movement of focus.
|
-- are unaffected by movement of focus.
|
||||||
--
|
focusUp :: StackSet i l a s sd -> StackSet i l a s sd
|
||||||
-- swapUp, swapDown, swap the neighbour in the stack ordering, wrapping
|
|
||||||
-- if we reach the end. Again the wrapping model should 'cycle' on
|
|
||||||
-- the current stack.
|
|
||||||
--
|
|
||||||
focusUp, focusDown, swapUp, swapDown :: StackSet i l a s sd -> StackSet i l a s sd
|
|
||||||
focusUp = modify' focusUp'
|
focusUp = modify' focusUp'
|
||||||
|
|
||||||
|
-- | /O(1), O(w) on the wrapping case/. Like 'focusUp', but move the
|
||||||
|
-- window focus down the stack.
|
||||||
|
focusDown :: StackSet i l a s sd -> StackSet i l a s sd
|
||||||
focusDown = modify' focusDown'
|
focusDown = modify' focusDown'
|
||||||
|
|
||||||
|
-- | /O(1), O(w) on the wrapping case/. Swap the upwards (left)
|
||||||
|
-- neighbour in the stack ordering, wrapping if we reach the end. Much
|
||||||
|
-- like for 'focusUp' and 'focusDown', the wrapping model should 'cycle'
|
||||||
|
-- on the current stack.
|
||||||
|
swapUp :: StackSet i l a s sd -> StackSet i l a s sd
|
||||||
swapUp = modify' swapUp'
|
swapUp = modify' swapUp'
|
||||||
|
|
||||||
|
-- | /O(1), O(w) on the wrapping case/. Like 'swapUp', but for swapping
|
||||||
|
-- the downwards (right) neighbour.
|
||||||
|
swapDown :: StackSet i l a s sd -> StackSet i l a s sd
|
||||||
swapDown = modify' (reverseStack . swapUp' . reverseStack)
|
swapDown = modify' (reverseStack . swapUp' . reverseStack)
|
||||||
|
|
||||||
-- | Variants of 'focusUp' and 'focusDown' that work on a
|
-- | A variant of 'focusUp' with the same asymptotics that works on a
|
||||||
-- 'Stack' rather than an entire 'StackSet'.
|
-- 'Stack' rather than an entire 'StackSet'.
|
||||||
focusUp', focusDown' :: Stack a -> Stack a
|
focusUp' :: Stack a -> Stack a
|
||||||
focusUp' (Stack t (l:ls) rs) = Stack l ls (t:rs)
|
focusUp' (Stack t (l:ls) rs) = Stack l ls (t:rs)
|
||||||
focusUp' (Stack t [] rs) = Stack x xs [] where (x:xs) = reverse (t:rs)
|
focusUp' (Stack t [] rs) = Stack x xs []
|
||||||
focusDown' = reverseStack . focusUp' . reverseStack
|
where (x :| xs) = NE.reverse (t :| rs)
|
||||||
|
|
||||||
|
-- | A variant of 'focusDown' with the same asymptotics that works on a
|
||||||
|
-- 'Stack' rather than an entire 'StackSet'.
|
||||||
|
focusDown' :: Stack a -> Stack a
|
||||||
|
focusDown' = reverseStack . focusUp' . reverseStack
|
||||||
|
|
||||||
|
-- | A variant of 'spawUp' with the same asymptotics that works on a
|
||||||
|
-- 'Stack' rather than an entire 'StackSet'.
|
||||||
swapUp' :: Stack a -> Stack a
|
swapUp' :: Stack a -> Stack a
|
||||||
swapUp' (Stack t (l:ls) rs) = Stack t ls (l:rs)
|
swapUp' (Stack t (l:ls) rs) = Stack t ls (l:rs)
|
||||||
swapUp' (Stack t [] rs) = Stack t (reverse rs) []
|
swapUp' (Stack t [] rs) = Stack t (reverse rs) []
|
||||||
@@ -508,8 +536,8 @@ sink w s = s { floating = M.delete w (floating s) }
|
|||||||
-- Focus stays with the item moved.
|
-- Focus stays with the item moved.
|
||||||
swapMaster :: StackSet i l a s sd -> StackSet i l a s sd
|
swapMaster :: StackSet i l a s sd -> StackSet i l a s sd
|
||||||
swapMaster = modify' $ \c -> case c of
|
swapMaster = modify' $ \c -> case c of
|
||||||
Stack _ [] _ -> c -- already master.
|
Stack _ [] _ -> c -- already master.
|
||||||
Stack t ls rs -> Stack t [] (xs ++ x : rs) where (x:xs) = reverse ls
|
Stack t (l:ls) rs -> Stack t [] (xs ++ x : rs) where (x :| xs) = NE.reverse (l :| ls)
|
||||||
|
|
||||||
-- natural! keep focus, move current to the top, move top to current.
|
-- natural! keep focus, move current to the top, move top to current.
|
||||||
|
|
||||||
@@ -525,8 +553,8 @@ shiftMaster = modify' $ \c -> case c of
|
|||||||
-- | /O(s)/. Set focus to the master window.
|
-- | /O(s)/. Set focus to the master window.
|
||||||
focusMaster :: StackSet i l a s sd -> StackSet i l a s sd
|
focusMaster :: StackSet i l a s sd -> StackSet i l a s sd
|
||||||
focusMaster = modify' $ \c -> case c of
|
focusMaster = modify' $ \c -> case c of
|
||||||
Stack _ [] _ -> c
|
Stack _ [] _ -> c
|
||||||
Stack t ls rs -> Stack x [] (xs ++ t : rs) where (x:xs) = reverse ls
|
Stack t (l:ls) rs -> Stack x [] (xs ++ t : rs) where (x :| xs) = NE.reverse (l :| ls)
|
||||||
|
|
||||||
--
|
--
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
|
14
stack.yaml
14
stack.yaml
@@ -1,7 +1,15 @@
|
|||||||
resolver: lts-7.19
|
resolver: lts-19.6
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
- ./
|
- ./
|
||||||
|
|
||||||
extra-deps:
|
extra-deps:
|
||||||
- X11-1.8
|
- X11-1.10
|
||||||
|
|
||||||
|
nix:
|
||||||
|
packages:
|
||||||
|
- zlib
|
||||||
|
- xorg.libX11
|
||||||
|
- xorg.libXrandr
|
||||||
|
- xorg.libXScrnSaver
|
||||||
|
- xorg.libXext
|
||||||
|
@@ -36,7 +36,7 @@ instance (Integral i, Integral s, Eq a, Arbitrary a, Arbitrary l, Arbitrary sd)
|
|||||||
-- Pick a random window "number" in each workspace, to give focus.
|
-- Pick a random window "number" in each workspace, to give focus.
|
||||||
focus <- sequence [ if null windows
|
focus <- sequence [ if null windows
|
||||||
then return Nothing
|
then return Nothing
|
||||||
else liftM Just $ choose (0, length windows - 1)
|
else Just <$> choose (0, length windows - 1)
|
||||||
| windows <- wsWindows ]
|
| windows <- wsWindows ]
|
||||||
|
|
||||||
let tags = [1 .. fromIntegral numWs]
|
let tags = [1 .. fromIntegral numWs]
|
||||||
@@ -80,7 +80,7 @@ newtype NonEmptyWindowsStackSet = NonEmptyWindowsStackSet T
|
|||||||
|
|
||||||
instance Arbitrary NonEmptyWindowsStackSet where
|
instance Arbitrary NonEmptyWindowsStackSet where
|
||||||
arbitrary =
|
arbitrary =
|
||||||
NonEmptyWindowsStackSet `fmap` (arbitrary `suchThat` (not . null . allWindows))
|
NonEmptyWindowsStackSet <$> (arbitrary `suchThat` (not . null . allWindows))
|
||||||
|
|
||||||
instance Arbitrary Rectangle where
|
instance Arbitrary Rectangle where
|
||||||
arbitrary = Rectangle <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
|
arbitrary = Rectangle <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
|
||||||
@@ -99,7 +99,7 @@ newtype NonEmptyNubList a = NonEmptyNubList [a]
|
|||||||
deriving ( Eq, Ord, Show, Read )
|
deriving ( Eq, Ord, Show, Read )
|
||||||
|
|
||||||
instance (Eq a, Arbitrary a) => Arbitrary (NonEmptyNubList a) where
|
instance (Eq a, Arbitrary a) => Arbitrary (NonEmptyNubList a) where
|
||||||
arbitrary = NonEmptyNubList `fmap` ((liftM nub arbitrary) `suchThat` (not . null))
|
arbitrary = NonEmptyNubList <$> ((nub <$> arbitrary) `suchThat` (not . null))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ arbitraryTag :: T -> Gen Tag
|
|||||||
arbitraryTag x = do
|
arbitraryTag x = do
|
||||||
let ts = tags x
|
let ts = tags x
|
||||||
-- There must be at least 1 workspace, thus at least 1 tag.
|
-- There must be at least 1 workspace, thus at least 1 tag.
|
||||||
idx <- choose (0, (length ts) - 1)
|
idx <- choose (0, length ts - 1)
|
||||||
return $ ts!!idx
|
return $ ts!!idx
|
||||||
|
|
||||||
-- | Pull out an arbitrary window from a StackSet that is guaranteed to have a
|
-- | Pull out an arbitrary window from a StackSet that is guaranteed to have a
|
||||||
@@ -136,5 +136,5 @@ arbitraryWindow :: NonEmptyWindowsStackSet -> Gen Window
|
|||||||
arbitraryWindow (NonEmptyWindowsStackSet x) = do
|
arbitraryWindow (NonEmptyWindowsStackSet x) = do
|
||||||
let ws = allWindows x
|
let ws = allWindows x
|
||||||
-- We know that there are at least 1 window in a NonEmptyWindowsStackSet.
|
-- We know that there are at least 1 window in a NonEmptyWindowsStackSet.
|
||||||
idx <- choose(0, (length ws) - 1)
|
idx <- choose (0, length ws - 1)
|
||||||
return $ ws!!idx
|
return $ ws!!idx
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import Test.QuickCheck
|
import Test.QuickCheck
|
||||||
|
|
||||||
-- Our QC instances and properties.
|
-- Our QC instances and properties:
|
||||||
import Instances
|
import Instances
|
||||||
import Properties.Delete
|
import Properties.Delete
|
||||||
import Properties.Failure
|
import Properties.Failure
|
||||||
@@ -196,6 +196,5 @@ tests =
|
|||||||
,("pointWithin", property prop_point_within)
|
,("pointWithin", property prop_point_within)
|
||||||
,("pointWithin mirror", property prop_point_within_mirror)
|
,("pointWithin mirror", property prop_point_within_mirror)
|
||||||
|
|
||||||
]
|
] <>
|
||||||
|
prop_laws_Stack
|
||||||
|
|
||||||
|
@@ -18,7 +18,7 @@ prop_delete x =
|
|||||||
where _ = x :: T
|
where _ = x :: T
|
||||||
|
|
||||||
-- delete is reversible with 'insert'.
|
-- 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) =
|
prop_delete_insert (x :: T) =
|
||||||
case peek x of
|
case peek x of
|
||||||
@@ -64,7 +64,7 @@ prop_delete_focus_not_end = do
|
|||||||
-- last one in the stack.
|
-- last one in the stack.
|
||||||
`suchThat` \(x' :: T) ->
|
`suchThat` \(x' :: T) ->
|
||||||
let currWins = index x'
|
let currWins = index x'
|
||||||
in length (currWins) >= 2 && peek x' /= Just (last currWins)
|
in length currWins >= 2 && peek x' /= Just (last currWins)
|
||||||
-- This is safe, as we know there are >= 2 windows
|
-- This is safe, as we know there are >= 2 windows
|
||||||
let Just n = peek x
|
let Just n = peek x
|
||||||
return $ peek (delete n x) == peek (focusDown x)
|
return $ peek (delete n x) == peek (focusDown x)
|
||||||
|
@@ -2,7 +2,7 @@ module Properties.Failure where
|
|||||||
|
|
||||||
import XMonad.StackSet hiding (filter)
|
import XMonad.StackSet hiding (filter)
|
||||||
|
|
||||||
import qualified Control.Exception.Extensible as C
|
import qualified Control.Exception as C
|
||||||
import System.IO.Unsafe
|
import System.IO.Unsafe
|
||||||
import Data.List (isPrefixOf)
|
import Data.List (isPrefixOf)
|
||||||
|
|
||||||
|
@@ -32,8 +32,8 @@ prop_focusWindow_master (NonNegative n) (x :: T) =
|
|||||||
in index (focusWindow (s !! i) x) == index x
|
in index (focusWindow (s !! i) x) == index x
|
||||||
|
|
||||||
-- shifting focus is trivially reversible
|
-- shifting focus is trivially reversible
|
||||||
prop_focus_left (x :: T) = (focusUp (focusDown x)) == x
|
prop_focus_left (x :: T) = focusUp (focusDown x) == x
|
||||||
prop_focus_right (x :: T) = (focusDown (focusUp x)) == x
|
prop_focus_right (x :: T) = focusDown (focusUp x) == x
|
||||||
|
|
||||||
-- focus master is idempotent
|
-- focus master is idempotent
|
||||||
prop_focusMaster_idem (x :: T) = focusMaster x == focusMaster (focusMaster x)
|
prop_focusMaster_idem (x :: T) = focusMaster x == focusMaster (focusMaster x)
|
||||||
@@ -47,9 +47,9 @@ prop_focusWindow_works (NonNegative (n :: Int)) (x :: T) =
|
|||||||
in (focus . fromJust . stack . workspace . current) (focusWindow (s !! i) x) == (s !! i)
|
in (focus . fromJust . stack . workspace . current) (focusWindow (s !! i) x) == (s !! i)
|
||||||
|
|
||||||
-- rotation through the height of a stack gets us back to the start
|
-- rotation through the height of a stack gets us back to the start
|
||||||
prop_focus_all_l (x :: T) = (foldr (const focusUp) x [1..n]) == x
|
prop_focus_all_l (x :: T) = foldr (const focusUp) x [1..n] == x
|
||||||
where n = length (index x)
|
where n = length (index x)
|
||||||
prop_focus_all_r (x :: T) = (foldr (const focusDown) x [1..n]) == x
|
prop_focus_all_r (x :: T) = foldr (const focusDown) x [1..n] == x
|
||||||
where n = length (index x)
|
where n = length (index x)
|
||||||
|
|
||||||
-- prop_rotate_all (x :: T) = f (f x) == f x
|
-- prop_rotate_all (x :: T) = f (f x) == f x
|
||||||
|
@@ -35,7 +35,7 @@ prop_greedyView_local (x :: T) = do
|
|||||||
-- greedyView is idempotent
|
-- greedyView is idempotent
|
||||||
prop_greedyView_idem (x :: T) = do
|
prop_greedyView_idem (x :: T) = do
|
||||||
n <- arbitraryTag x
|
n <- arbitraryTag x
|
||||||
return $ greedyView n (greedyView n x) == (greedyView n x)
|
return $ greedyView n (greedyView n x) == greedyView n x
|
||||||
|
|
||||||
-- greedyView is reversible, though shuffles the order of hidden/visible
|
-- greedyView is reversible, though shuffles the order of hidden/visible
|
||||||
prop_greedyView_reversible (x :: T) = do
|
prop_greedyView_reversible (x :: T) = do
|
||||||
|
@@ -46,7 +46,7 @@ prop_insert_delete x = do
|
|||||||
|
|
||||||
-- inserting n elements increases current stack size by n
|
-- inserting n elements increases current stack size by n
|
||||||
prop_size_insert is (EmptyStackSet x) =
|
prop_size_insert is (EmptyStackSet x) =
|
||||||
size (foldr insertUp x ws ) == (length ws)
|
size (foldr insertUp x ws) == length ws
|
||||||
where
|
where
|
||||||
ws = nub is
|
ws = nub is
|
||||||
size = length . index
|
size = length . index
|
||||||
|
@@ -29,6 +29,6 @@ prop_purelayout_full rect = do
|
|||||||
|
|
||||||
-- what happens when we send an IncMaster message to Full --- Nothing
|
-- what happens when we send an IncMaster message to Full --- Nothing
|
||||||
prop_sendmsg_full (NonNegative k) =
|
prop_sendmsg_full (NonNegative k) =
|
||||||
isNothing (Full `pureMessage` (SomeMessage (IncMasterN k)))
|
isNothing (Full `pureMessage` SomeMessage (IncMasterN k))
|
||||||
|
|
||||||
prop_desc_full = description Full == show Full
|
prop_desc_full = description Full == show Full
|
||||||
|
@@ -29,12 +29,12 @@ prop_tile_non_overlap rect windows nmaster = noOverlaps (tile pct rect nmaster w
|
|||||||
|
|
||||||
-- splitting horizontally yields sensible results
|
-- splitting horizontally yields sensible results
|
||||||
prop_split_horizontal (NonNegative n) x =
|
prop_split_horizontal (NonNegative n) x =
|
||||||
(noOverflows (+) (rect_x x) (rect_width x)) ==>
|
noOverflows (+) (rect_x x) (rect_width x) ==>
|
||||||
sum (map rect_width xs) == rect_width x
|
sum (map rect_width xs) == rect_width x
|
||||||
&&
|
&&
|
||||||
all (== rect_height x) (map rect_height xs)
|
all (\s -> rect_height s == rect_height x) xs
|
||||||
&&
|
&&
|
||||||
(map rect_x xs) == (sort $ map rect_x xs)
|
map rect_x xs == sort (map rect_x xs)
|
||||||
|
|
||||||
where
|
where
|
||||||
xs = splitHorizontally n x
|
xs = splitHorizontally n x
|
||||||
@@ -72,7 +72,7 @@ prop_shrink_tall (NonNegative n) (Positive delta) (NonNegative frac) =
|
|||||||
-- remaining fraction should shrink
|
-- remaining fraction should shrink
|
||||||
where
|
where
|
||||||
l1 = Tall n delta frac
|
l1 = Tall n delta frac
|
||||||
Just l2@(Tall n' delta' frac') = l1 `pureMessage` (SomeMessage Shrink)
|
Just l2@(Tall n' delta' frac') = l1 `pureMessage` SomeMessage Shrink
|
||||||
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
|
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
|
||||||
|
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ prop_expand_tall (NonNegative n)
|
|||||||
where
|
where
|
||||||
frac = min 1 (n1 % d1)
|
frac = min 1 (n1 % d1)
|
||||||
l1 = Tall n delta frac
|
l1 = Tall n delta frac
|
||||||
Just l2@(Tall n' delta' frac') = l1 `pureMessage` (SomeMessage Expand)
|
Just l2@(Tall n' delta' frac') = l1 `pureMessage` SomeMessage Expand
|
||||||
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
|
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
|
||||||
|
|
||||||
-- what happens when we send an IncMaster message to Tall
|
-- what happens when we send an IncMaster message to Tall
|
||||||
@@ -102,7 +102,7 @@ prop_incmaster_tall (NonNegative n) (Positive delta) (NonNegative frac)
|
|||||||
delta == delta' && frac == frac' && n' == n + k
|
delta == delta' && frac == frac' && n' == n + k
|
||||||
where
|
where
|
||||||
l1 = Tall n delta frac
|
l1 = Tall n delta frac
|
||||||
Just l2@(Tall n' delta' frac') = l1 `pureMessage` (SomeMessage (IncMasterN k))
|
Just l2@(Tall n' delta' frac') = l1 `pureMessage` SomeMessage (IncMasterN k)
|
||||||
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
|
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -52,15 +52,14 @@ prop_aspect_hint_shrink hint (w,h) = case applyAspectHint hint (w,h) of
|
|||||||
-- applyAspectHint does nothing when the supplied (x,y) fits
|
-- applyAspectHint does nothing when the supplied (x,y) fits
|
||||||
-- the desired range
|
-- the desired range
|
||||||
prop_aspect_fits =
|
prop_aspect_fits =
|
||||||
forAll ((,,,) <$> pos <*> pos <*> pos <*> pos) $ \ (x,y,a,b) ->
|
forAll ((,,,) <$> pos <*> pos <*> pos <*> pos) $ \ (x,y,a,b) ->
|
||||||
let f v = applyAspectHint ((x, y+a), (x+b, y)) v
|
let f = applyAspectHint ((x, y+a), (x+b, y))
|
||||||
in and [ noOverflows (*) x (y+a), noOverflows (*) (x+b) y ]
|
in noOverflows (*) x (y+a) && noOverflows (*) (x+b) y
|
||||||
==> f (x,y) == (x,y)
|
==> f (x,y) == (x,y)
|
||||||
|
|
||||||
where pos = choose (0, 65535)
|
where pos = choose (0, 65535)
|
||||||
mul a b = toInteger (a*b) /= toInteger a * toInteger b
|
|
||||||
|
|
||||||
prop_point_within r @ (Rectangle x y w h) =
|
prop_point_within r@(Rectangle x y w h) =
|
||||||
forAll ((,) <$>
|
forAll ((,) <$>
|
||||||
choose (0, fromIntegral w - 1) <*>
|
choose (0, fromIntegral w - 1) <*>
|
||||||
choose (0, fromIntegral h - 1)) $
|
choose (0, fromIntegral h - 1)) $
|
||||||
|
@@ -27,7 +27,7 @@ prop_shift_reversible (x :: T) = do
|
|||||||
-- shiftMaster
|
-- shiftMaster
|
||||||
|
|
||||||
-- focus/local/idempotent same as swapMaster:
|
-- focus/local/idempotent same as swapMaster:
|
||||||
prop_shift_master_focus (x :: T) = peek x == (peek $ shiftMaster x)
|
prop_shift_master_focus (x :: T) = peek x == peek (shiftMaster x)
|
||||||
prop_shift_master_local (x :: T) = hidden_spaces x == hidden_spaces (shiftMaster x)
|
prop_shift_master_local (x :: T) = hidden_spaces x == hidden_spaces (shiftMaster x)
|
||||||
prop_shift_master_idempotent (x :: T) = shiftMaster (shiftMaster x) == shiftMaster x
|
prop_shift_master_idempotent (x :: T) = shiftMaster (shiftMaster x) == shiftMaster x
|
||||||
-- ordering is constant modulo the focused window:
|
-- ordering is constant modulo the focused window:
|
||||||
@@ -57,14 +57,14 @@ prop_shift_win_fix_current = do
|
|||||||
x <- arbitrary `suchThat` \(x' :: T) ->
|
x <- arbitrary `suchThat` \(x' :: T) ->
|
||||||
-- Invariant, otherWindows are NOT in the current workspace.
|
-- Invariant, otherWindows are NOT in the current workspace.
|
||||||
let otherWindows = allWindows x' L.\\ index x'
|
let otherWindows = allWindows x' L.\\ index x'
|
||||||
in length(tags x') >= 2 && length(otherWindows) >= 1
|
in length (tags x') >= 2 && not (null otherWindows)
|
||||||
-- Sadly we have to construct `otherWindows` again, for the actual StackSet
|
-- Sadly we have to construct `otherWindows` again, for the actual StackSet
|
||||||
-- that got chosen.
|
-- that got chosen.
|
||||||
let otherWindows = allWindows x L.\\ index x
|
let otherWindows = allWindows x L.\\ index x
|
||||||
-- We know such tag must exists, due to the precondition
|
-- We know such tag must exists, due to the precondition
|
||||||
n <- arbitraryTag x `suchThat` (/= currentTag x)
|
n <- arbitraryTag x `suchThat` (/= currentTag x)
|
||||||
-- we know length is >= 1, from above precondition
|
-- we know length is >= 1, from above precondition
|
||||||
idx <- choose(0, length(otherWindows) - 1)
|
idx <- choose (0, length otherWindows - 1)
|
||||||
let w = otherWindows !! idx
|
let w = otherWindows !! idx
|
||||||
return $ (current $ x) == (current $ shiftWin n w x)
|
return $ current x == current (shiftWin n w x)
|
||||||
|
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||||
|
|
||||||
module Properties.Stack where
|
module Properties.Stack where
|
||||||
|
|
||||||
import Test.QuickCheck
|
import Test.QuickCheck
|
||||||
@@ -9,12 +11,18 @@ import qualified XMonad.StackSet as S (filter)
|
|||||||
|
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
|
|
||||||
|
import Data.Proxy
|
||||||
|
import Test.QuickCheck.Classes (
|
||||||
|
Laws (lawsTypeclass, lawsProperties), Proxy1 (Proxy1),
|
||||||
|
foldableLaws, traversableLaws,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
-- The list returned by index should be the same length as the actual
|
-- The list returned by index should be the same length as the actual
|
||||||
-- windows kept in the zipper
|
-- windows kept in the zipper
|
||||||
prop_index_length (x :: T) =
|
prop_index_length (x :: T) =
|
||||||
case stack . workspace . current $ x of
|
case stack . workspace . current $ x of
|
||||||
Nothing -> length (index x) == 0
|
Nothing -> null (index x)
|
||||||
Just it -> length (index x) == length (focus it : up it ++ down it)
|
Just it -> length (index x) == length (focus it : up it ++ down it)
|
||||||
|
|
||||||
|
|
||||||
@@ -33,7 +41,7 @@ prop_allWindowsMember (NonEmptyWindowsStackSet x) = do
|
|||||||
-- which is a key component in this test (together with member).
|
-- which is a key component in this test (together with member).
|
||||||
let ws = allWindows x
|
let ws = allWindows x
|
||||||
-- We know that there are at least 1 window in a NonEmptyWindowsStackSet.
|
-- We know that there are at least 1 window in a NonEmptyWindowsStackSet.
|
||||||
idx <- choose(0, (length ws) - 1)
|
idx <- choose (0, length ws - 1)
|
||||||
return $ member (ws!!idx) x
|
return $ member (ws!!idx) x
|
||||||
|
|
||||||
|
|
||||||
@@ -46,6 +54,24 @@ prop_filter_order (x :: T) =
|
|||||||
-- differentiate should return Nothing if the list is empty or Just stack, with
|
-- differentiate should return Nothing if the list is empty or Just stack, with
|
||||||
-- the first element of the list is current, and the rest of the list is down.
|
-- the first element of the list is current, and the rest of the list is down.
|
||||||
prop_differentiate xs =
|
prop_differentiate xs =
|
||||||
if null xs then differentiate xs == Nothing
|
if null xs then isNothing (differentiate xs)
|
||||||
else (differentiate xs) == Just (Stack (head xs) [] (tail xs))
|
else differentiate xs == Just (Stack (head xs) [] (tail xs))
|
||||||
where _ = xs :: [Int]
|
where _ = xs :: [Int]
|
||||||
|
|
||||||
|
|
||||||
|
-- Check type class laws of 'Data.Foldable.Foldable' and 'Data.Traversable.Traversable'.
|
||||||
|
newtype TestStack a = TestStack (Stack a)
|
||||||
|
deriving (Eq, Read, Show, Foldable, Functor)
|
||||||
|
|
||||||
|
instance (Arbitrary a) => Arbitrary (TestStack a) where
|
||||||
|
arbitrary = TestStack <$> (Stack <$> arbitrary <*> arbitrary <*> arbitrary)
|
||||||
|
shrink = traverse shrink
|
||||||
|
|
||||||
|
instance Traversable TestStack where
|
||||||
|
traverse f (TestStack sx) = fmap TestStack (traverse f sx)
|
||||||
|
|
||||||
|
prop_laws_Stack = format (foldableLaws p) <> format (traversableLaws p)
|
||||||
|
where
|
||||||
|
p = Proxy :: Proxy TestStack
|
||||||
|
format laws = [ ("Stack: " <> lawsTypeclass laws <> ": " <> name, prop)
|
||||||
|
| (name, prop) <- lawsProperties laws ]
|
||||||
|
@@ -58,7 +58,7 @@ invariant (s :: T) = and
|
|||||||
-- inBounds = and [ w >=0 && w < size s | (w,sc) <- M.assocs (screens s) ]
|
-- inBounds = and [ w >=0 && w < size s | (w,sc) <- M.assocs (screens s) ]
|
||||||
|
|
||||||
monotonic [] = True
|
monotonic [] = True
|
||||||
monotonic (x:[]) = True
|
monotonic [x] = True
|
||||||
monotonic (x:y:zs) | x == y-1 = monotonic (y:zs)
|
monotonic (x:y:zs) | x == y-1 = monotonic (y:zs)
|
||||||
| otherwise = False
|
| otherwise = False
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ prop_empty (EmptyStackSet x) =
|
|||||||
prop_empty_current (EmptyStackSet x) = currentTag x == head (tags x)
|
prop_empty_current (EmptyStackSet x) = currentTag x == head (tags x)
|
||||||
|
|
||||||
-- no windows will be a member of an empty workspace
|
-- no windows will be a member of an empty workspace
|
||||||
prop_member_empty i (EmptyStackSet x) = member i x == False
|
prop_member_empty i (EmptyStackSet x) = not (member i x)
|
||||||
|
|
||||||
-- peek either yields nothing on the Empty workspace, or Just a valid window
|
-- peek either yields nothing on the Empty workspace, or Just a valid window
|
||||||
prop_member_peek (x :: T) =
|
prop_member_peek (x :: T) =
|
||||||
|
@@ -11,8 +11,8 @@ import XMonad.StackSet hiding (filter)
|
|||||||
-- swapUp, swapDown, swapMaster: reordiring windows
|
-- swapUp, swapDown, swapMaster: reordiring windows
|
||||||
|
|
||||||
-- swap is trivially reversible
|
-- swap is trivially reversible
|
||||||
prop_swap_left (x :: T) = (swapUp (swapDown x)) == x
|
prop_swap_left (x :: T) = swapUp (swapDown x) == x
|
||||||
prop_swap_right (x :: T) = (swapDown (swapUp x)) == x
|
prop_swap_right (x :: T) = swapDown (swapUp x) == x
|
||||||
-- TODO swap is reversible
|
-- TODO swap is reversible
|
||||||
-- swap is reversible, but involves moving focus back the window with
|
-- swap is reversible, but involves moving focus back the window with
|
||||||
-- master on it. easy to do with a mouse...
|
-- master on it. easy to do with a mouse...
|
||||||
@@ -26,12 +26,12 @@ prop_promote_reversible x b = (not . null . fromMaybe [] . flip index x . curren
|
|||||||
-}
|
-}
|
||||||
|
|
||||||
-- swap doesn't change focus
|
-- swap doesn't change focus
|
||||||
prop_swap_master_focus (x :: T) = peek x == (peek $ swapMaster x)
|
prop_swap_master_focus (x :: T) = peek x == peek (swapMaster x)
|
||||||
-- = case peek x of
|
-- = case peek x of
|
||||||
-- Nothing -> True
|
-- Nothing -> True
|
||||||
-- Just f -> focus (stack (workspace $ current (swap x))) == f
|
-- Just f -> focus (stack (workspace $ current (swap x))) == f
|
||||||
prop_swap_left_focus (x :: T) = peek x == (peek $ swapUp x)
|
prop_swap_left_focus (x :: T) = peek x == peek (swapUp x)
|
||||||
prop_swap_right_focus (x :: T) = peek x == (peek $ swapDown x)
|
prop_swap_right_focus (x :: T) = peek x == peek (swapDown x)
|
||||||
|
|
||||||
-- swap is local
|
-- swap is local
|
||||||
prop_swap_master_local (x :: T) = hidden_spaces x == hidden_spaces (swapMaster x)
|
prop_swap_master_local (x :: T) = hidden_spaces x == hidden_spaces (swapMaster x)
|
||||||
@@ -39,9 +39,9 @@ prop_swap_left_local (x :: T) = hidden_spaces x == hidden_spaces (swapUp x)
|
|||||||
prop_swap_right_local (x :: T) = hidden_spaces x == hidden_spaces (swapDown x)
|
prop_swap_right_local (x :: T) = hidden_spaces x == hidden_spaces (swapDown x)
|
||||||
|
|
||||||
-- rotation through the height of a stack gets us back to the start
|
-- rotation through the height of a stack gets us back to the start
|
||||||
prop_swap_all_l (x :: T) = (foldr (const swapUp) x [1..n]) == x
|
prop_swap_all_l (x :: T) = foldr (const swapUp) x [1..n] == x
|
||||||
where n = length (index x)
|
where n = length (index x)
|
||||||
prop_swap_all_r (x :: T) = (foldr (const swapDown) x [1..n]) == x
|
prop_swap_all_r (x :: T) = foldr (const swapDown) x [1..n] == x
|
||||||
where n = length (index x)
|
where n = length (index x)
|
||||||
|
|
||||||
prop_swap_master_idempotent (x :: T) = swapMaster (swapMaster x) == swapMaster x
|
prop_swap_master_idempotent (x :: T) = swapMaster (swapMaster x) == swapMaster x
|
||||||
|
@@ -37,7 +37,7 @@ prop_view_local (x :: T) = do
|
|||||||
-- view is idempotent
|
-- view is idempotent
|
||||||
prop_view_idem (x :: T) = do
|
prop_view_idem (x :: T) = do
|
||||||
n <- arbitraryTag x
|
n <- arbitraryTag x
|
||||||
return $ view n (view n x) == (view n x)
|
return $ view n (view n x) == view n x
|
||||||
|
|
||||||
-- view is reversible, though shuffles the order of hidden/visible
|
-- view is reversible, though shuffles the order of hidden/visible
|
||||||
prop_view_reversible (x :: T) = do
|
prop_view_reversible (x :: T) = do
|
||||||
|
@@ -12,8 +12,8 @@ hidden_spaces x = map workspace (visible x) ++ hidden x
|
|||||||
-- normalise workspace list
|
-- normalise workspace list
|
||||||
normal s = s { hidden = sortBy g (hidden s), visible = sortBy f (visible s) }
|
normal s = s { hidden = sortBy g (hidden s), visible = sortBy f (visible s) }
|
||||||
where
|
where
|
||||||
f = \a b -> tag (workspace a) `compare` tag (workspace b)
|
f a b = tag (workspace a) `compare` tag (workspace b)
|
||||||
g = \a b -> tag a `compare` tag b
|
g a b = tag a `compare` tag b
|
||||||
|
|
||||||
|
|
||||||
noOverlaps [] = True
|
noOverlaps [] = True
|
||||||
|
83
util/GenerateManpage.hs
Normal file → Executable file
83
util/GenerateManpage.hs
Normal file → Executable file
@@ -1,47 +1,20 @@
|
|||||||
{-# LANGUAGE FlexibleContexts #-}
|
#!/usr/bin/env runhaskell
|
||||||
|
|
||||||
-- Generates a in-memory version of "man/xmonad.1.markdown" that has the list
|
-- Reads markdown (man/xmonad.1.markdown) from stdin, subtitutes
|
||||||
-- of known key-bindings is inserted automatically from "Config.hs". That
|
-- ___KEYBINDINGS___ for key-binding definitions generated from
|
||||||
-- document is then rendered with Pandoc as "man/xmonad.1" and
|
-- src/XMonad/Config.hs, prints result to stdout.
|
||||||
-- "man/xmonad.1.html".
|
|
||||||
--
|
--
|
||||||
-- Unlike the rest of xmonad, this file is released under the GNU General
|
-- Unlike the rest of xmonad, this file is released under the GNU General
|
||||||
-- Public License version 2 or later.
|
-- Public License version 2 or later. (Historical reasons, used to link with
|
||||||
|
-- GPL-licensed pandoc.)
|
||||||
|
|
||||||
import Control.Monad.IO.Class (liftIO)
|
|
||||||
import Data.Char
|
import Data.Char
|
||||||
import Data.List
|
import Data.List
|
||||||
import qualified Data.Text as T
|
|
||||||
import qualified Data.Text.IO as TIO
|
|
||||||
import Text.Pandoc
|
|
||||||
import Text.Regex.Posix
|
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
keybindings <- guessBindings
|
keybindings <- guessBindings
|
||||||
|
interact $ unlines . replace "___KEYBINDINGS___" keybindings . lines
|
||||||
markdownSource <- readFile "./man/xmonad.1.markdown"
|
|
||||||
|
|
||||||
runIOorExplode $ do
|
|
||||||
parsed <- readMarkdown (def { readerStandalone = True, readerExtensions = pandocExtensions })
|
|
||||||
. T.pack
|
|
||||||
. unlines
|
|
||||||
. replace "___KEYBINDINGS___" keybindings
|
|
||||||
. lines
|
|
||||||
$ markdownSource
|
|
||||||
|
|
||||||
manTemplate <- getDefaultTemplate "man"
|
|
||||||
manBody <- writeMan def { writerTemplate = Just manTemplate } parsed
|
|
||||||
liftIO $ TIO.writeFile "./man/xmonad.1" $ manBody
|
|
||||||
liftIO $ putStrLn "Documentation created: man/xmonad.1"
|
|
||||||
|
|
||||||
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:
|
-- | The format for the docstrings in "Config.hs" takes the following form:
|
||||||
--
|
--
|
||||||
@@ -49,7 +22,7 @@ main = do
|
|||||||
-- -- mod-x %! Frob the whatsit
|
-- -- mod-x %! Frob the whatsit
|
||||||
-- @
|
-- @
|
||||||
--
|
--
|
||||||
-- "Frob the whatsit" will be used as the description for keybinding "mod-x".--
|
-- "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
|
-- If the name of the key binding is omitted, the function tries to guess it
|
||||||
-- from the rest of the line. For example:
|
-- from the rest of the line. For example:
|
||||||
--
|
--
|
||||||
@@ -61,25 +34,33 @@ main = do
|
|||||||
|
|
||||||
guessBindings :: IO String
|
guessBindings :: IO String
|
||||||
guessBindings = do
|
guessBindings = do
|
||||||
buf <- readFile "./src/XMonad/Config.hs"
|
buf <- readFile "./src/XMonad/Config.hs"
|
||||||
return (intercalate "\n\n" (map markdownDefn (allBindings buf)))
|
return (intercalate "\n\n" (map markdownDefn (allBindings buf)))
|
||||||
|
|
||||||
allBindings :: String -> [(String, String)]
|
allBindings :: String -> [(String, String)]
|
||||||
allBindings xs = map (binding . map trim) (xs =~ "(.*)--(.*)%!(.*)")
|
allBindings = concatMap parseLine . lines
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
guessKeys :: String -> String
|
|
||||||
guessKeys line =
|
|
||||||
case keys of
|
|
||||||
[key] -> concat $ intersperse "-" (modifiers ++ [map toLower key])
|
|
||||||
_ -> error ("guessKeys: unexpected number of keys " ++ show keys)
|
|
||||||
where
|
where
|
||||||
modifiers = map (!!1) (line =~ "(mod|shift|control)Mask")
|
parseLine :: String -> [(String, String)]
|
||||||
(_, _, _, keys) = line =~ "xK_([_[:alnum:]]+)" :: (String, String, String, [String])
|
parseLine l
|
||||||
|
| " -- " `isInfixOf` l
|
||||||
|
, Just d <- parseDesc l = [(intercalate "-" (parseKeys l), d)]
|
||||||
|
| otherwise = []
|
||||||
|
|
||||||
|
parseDesc :: String -> Maybe String
|
||||||
|
parseDesc = fmap (trim . drop 4) . find (" %! " `isPrefixOf`) . tails
|
||||||
|
|
||||||
|
parseKeys :: String -> [String]
|
||||||
|
parseKeys l = case lex l of
|
||||||
|
[("", _)] -> []
|
||||||
|
[("--", rest)] -> case words rest of
|
||||||
|
k : "%!" : _ -> [k]
|
||||||
|
_ -> []
|
||||||
|
[(k, rest)] -> parseKey k ++ parseKeys rest
|
||||||
|
|
||||||
|
parseKey :: String -> [String]
|
||||||
|
parseKey k | "Mask" `isSuffixOf` k = [reverse (drop 4 (reverse k))]
|
||||||
|
| "xK_" `isPrefixOf` k = [map toLower (drop 3 k)]
|
||||||
|
| otherwise = []
|
||||||
|
|
||||||
-- FIXME: What escaping should we be doing on these strings?
|
-- FIXME: What escaping should we be doing on these strings?
|
||||||
markdownDefn :: (String, String) -> String
|
markdownDefn :: (String, String) -> String
|
||||||
|
103
xmonad.cabal
103
xmonad.cabal
@@ -1,5 +1,5 @@
|
|||||||
name: xmonad
|
name: xmonad
|
||||||
version: 0.14.1
|
version: 0.17.2
|
||||||
synopsis: A tiling window manager
|
synopsis: A tiling window manager
|
||||||
description: xmonad is a tiling window manager for X. Windows are arranged
|
description: xmonad is a tiling window manager for X. Windows are arranged
|
||||||
automatically to tile the screen without gaps or overlap, maximising
|
automatically to tile the screen without gaps or overlap, maximising
|
||||||
@@ -12,53 +12,47 @@ description: xmonad is a tiling window manager for X. Windows are arrange
|
|||||||
screens.
|
screens.
|
||||||
license: BSD3
|
license: BSD3
|
||||||
license-file: LICENSE
|
license-file: LICENSE
|
||||||
author: Spencer Janssen, Don Stewart, Adam Vogt, David Roundy, Jason Creighton
|
author: Spencer Janssen, Don Stewart, Adam Vogt, David Roundy, Jason Creighton,
|
||||||
, Brent Yorgey, Peter Jones, Peter Simons, Andrea Rossato, Devin Mullins
|
Brent Yorgey, Peter Jones, Peter Simons, Andrea Rossato, Devin Mullins,
|
||||||
, Lukas Mai, Alec Berryman, Stefan O'Rear, Daniel Wagner, Peter J. Jones
|
Lukas Mai, Alec Berryman, Stefan O'Rear, Daniel Wagner, Peter J. Jones,
|
||||||
, Daniel Schoepe, Karsten Schoelzel, Neil Mitchell, Joachim Breitner
|
Daniel Schoepe, Karsten Schoelzel, Neil Mitchell, Joachim Breitner,
|
||||||
, Peter De Wachter, Eric Mertens, Geoff Reedy, Michiel Derhaeg
|
Peter De Wachter, Eric Mertens, Geoff Reedy, Michiel Derhaeg,
|
||||||
, Philipp Balzarek, Valery V. Vorotyntsev, Alex Tarkovsky, Fabian Beuke
|
Philipp Balzarek, Valery V. Vorotyntsev, Alex Tarkovsky, Fabian Beuke,
|
||||||
, Felix Hirn, Michael Sloan, Tomas Janousek, Vanessa McHale, Nicolas Pouillard
|
Felix Hirn, Michael Sloan, Tomas Janousek, Vanessa McHale, Nicolas Pouillard,
|
||||||
, Aaron Denney, Austin Seipp, Benno Fünfstück, Brandon S Allbery, Chris Mears
|
Aaron Denney, Austin Seipp, Benno Fünfstück, Brandon S Allbery, Chris Mears,
|
||||||
, Christian Thiemann, Clint Adams, Daniel Neri, David Lazar, Ferenc Wagner
|
Christian Thiemann, Clint Adams, Daniel Neri, David Lazar, Ferenc Wagner,
|
||||||
, Francesco Ariis, Gábor Lipták, Ivan N. Veselov, Ivan Tarasov, Javran Cheng
|
Francesco Ariis, Gábor Lipták, Ivan N. Veselov, Ivan Tarasov, Javran Cheng,
|
||||||
, Jens Petersen, Joey Hess, Jonne Ransijn, Josh Holland, Khudyakov Alexey
|
Jens Petersen, Joey Hess, Jonne Ransijn, Josh Holland, Khudyakov Alexey,
|
||||||
, Klaus Weidner, Michael G. Sloan, Mikkel Christiansen, Nicolas Dudebout
|
Klaus Weidner, Michael G. Sloan, Mikkel Christiansen, Nicolas Dudebout,
|
||||||
, Ondřej Súkup, Paul Hebble, Shachaf Ben-Kiki, Siim Põder, Tim McIver
|
Ondřej Súkup, Paul Hebble, Shachaf Ben-Kiki, Siim Põder, Tim McIver,
|
||||||
, Trevor Elliott, Wouter Swierstra, Conrad Irwin, Tim Thelion
|
Trevor Elliott, Wouter Swierstra, Conrad Irwin, Tim Thelion, Tony Zorman
|
||||||
maintainer: xmonad@haskell.org
|
maintainer: xmonad@haskell.org
|
||||||
tested-with: GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.3, GHC == 8.6.1
|
tested-with: GHC == 8.6.5 || == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.7 || == 9.4.4 || == 9.6.1
|
||||||
category: System
|
category: System
|
||||||
homepage: http://xmonad.org
|
homepage: http://xmonad.org
|
||||||
bug-reports: https://github.com/xmonad/xmonad/issues
|
bug-reports: https://github.com/xmonad/xmonad/issues
|
||||||
build-type: Simple
|
build-type: Simple
|
||||||
extra-source-files: README.md
|
extra-source-files: README.md
|
||||||
CHANGES.md
|
CHANGES.md
|
||||||
CONFIG
|
CONTRIBUTING.md
|
||||||
STYLE
|
INSTALL.md
|
||||||
tests/*.hs
|
MAINTAINERS.md
|
||||||
tests/Properties/*.hs
|
TUTORIAL.md
|
||||||
tests/Properties/Layout/*.hs
|
|
||||||
man/xmonad.1.markdown
|
man/xmonad.1.markdown
|
||||||
man/xmonad.1
|
man/xmonad.1
|
||||||
man/xmonad.1.html
|
man/xmonad.1.html
|
||||||
util/GenerateManpage.hs
|
man/xmonad.hs
|
||||||
util/hpcReport.sh
|
util/hpcReport.sh
|
||||||
cabal-version: >= 1.8
|
cabal-version: 1.12
|
||||||
|
|
||||||
source-repository head
|
source-repository head
|
||||||
type: git
|
type: git
|
||||||
location: https://github.com/xmonad/xmonad
|
location: https://github.com/xmonad/xmonad
|
||||||
|
|
||||||
flag testing
|
flag pedantic
|
||||||
|
description: Be pedantic (-Werror and the like)
|
||||||
default: False
|
default: False
|
||||||
manual: True
|
manual: True
|
||||||
description: Testing mode, only build minimal components
|
|
||||||
|
|
||||||
flag generatemanpage
|
|
||||||
default: False
|
|
||||||
manual: True
|
|
||||||
description: Build the tool for generating the man page
|
|
||||||
|
|
||||||
library
|
library
|
||||||
exposed-modules: XMonad
|
exposed-modules: XMonad
|
||||||
@@ -71,36 +65,40 @@ library
|
|||||||
XMonad.StackSet
|
XMonad.StackSet
|
||||||
other-modules: Paths_xmonad
|
other-modules: Paths_xmonad
|
||||||
hs-source-dirs: src
|
hs-source-dirs: src
|
||||||
build-depends: base >= 4.9 && < 5
|
build-depends: base >= 4.11 && < 5
|
||||||
, X11 >= 1.8 && < 1.10
|
, X11 >= 1.10 && < 1.11
|
||||||
, containers
|
, containers
|
||||||
, data-default
|
, data-default-class
|
||||||
, directory
|
, directory
|
||||||
, extensible-exceptions
|
|
||||||
, filepath
|
, filepath
|
||||||
, mtl
|
, mtl
|
||||||
, process
|
, process
|
||||||
, setlocale
|
, setlocale
|
||||||
|
, time
|
||||||
|
, transformers >= 0.3
|
||||||
, unix
|
, unix
|
||||||
, utf8-string >= 0.3 && < 1.1
|
ghc-options: -funbox-strict-fields -Wall -Wno-unused-do-bind
|
||||||
ghc-options: -funbox-strict-fields -Wall -fno-warn-unused-do-bind
|
default-language: Haskell2010
|
||||||
|
|
||||||
if flag(testing)
|
-- Keep this in sync with the oldest version in 'tested-with'
|
||||||
buildable: False
|
if impl(ghc > 8.6.5)
|
||||||
|
ghc-options: -Wno-unused-imports
|
||||||
|
|
||||||
|
if flag(pedantic)
|
||||||
|
ghc-options: -Werror
|
||||||
|
|
||||||
executable xmonad
|
executable xmonad
|
||||||
main-is: Main.hs
|
main-is: Main.hs
|
||||||
build-depends: base, X11, mtl, unix, xmonad
|
build-depends: base, xmonad
|
||||||
ghc-options: -Wall -fno-warn-unused-do-bind
|
ghc-options: -Wall -Wno-unused-do-bind
|
||||||
|
default-language: Haskell2010
|
||||||
|
|
||||||
executable generatemanpage
|
-- Keep this in sync with the oldest version in 'tested-with'
|
||||||
main-is: GenerateManpage.hs
|
if impl(ghc > 8.6.5)
|
||||||
hs-source-dirs: util
|
ghc-options: -Wno-unused-imports
|
||||||
|
|
||||||
if flag(generatemanpage)
|
if flag(pedantic)
|
||||||
build-depends: base, pandoc >= 2, regex-posix, text
|
ghc-options: -Werror
|
||||||
else
|
|
||||||
buildable: False
|
|
||||||
|
|
||||||
test-suite properties
|
test-suite properties
|
||||||
type: exitcode-stdio-1.0
|
type: exitcode-stdio-1.0
|
||||||
@@ -123,4 +121,13 @@ test-suite properties
|
|||||||
Properties.Workspace
|
Properties.Workspace
|
||||||
Utils
|
Utils
|
||||||
hs-source-dirs: tests
|
hs-source-dirs: tests
|
||||||
build-depends: base, QuickCheck >= 2, X11, containers, extensible-exceptions, xmonad
|
build-depends: base
|
||||||
|
, QuickCheck >= 2
|
||||||
|
, quickcheck-classes >= 0.4.3
|
||||||
|
, X11
|
||||||
|
, containers
|
||||||
|
, xmonad
|
||||||
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
if flag(pedantic)
|
||||||
|
ghc-options: -Werror
|
||||||
|
Reference in New Issue
Block a user