mirror of
https://github.com/xmonad/xmonad.git
synced 2025-07-26 17:51:51 -07:00
Compare commits
214 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
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 |
16
.github/ISSUE_TEMPLATE.md
vendored
16
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,12 +1,16 @@
|
||||
### Problem Description
|
||||
|
||||
Describe the problem you are having, what you expect to happen
|
||||
instead, and how to reproduce the problem.
|
||||
Describe the problem you are having and what you expect to happen
|
||||
instead.
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
Give detailed step-by-step instructions on how to reproduce the problem.
|
||||
|
||||
### Configuration File
|
||||
|
||||
Please include the smallest configuration file that reproduces the
|
||||
problem you are experiencing:
|
||||
Please include the smallest _full_ configuration file that reproduces
|
||||
the problem you are experiencing:
|
||||
|
||||
```haskell
|
||||
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 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 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
|
||||
|
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@v2
|
||||
|
||||
- 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
|
115
.github/workflows/haskell-ci-hackage.patch
vendored
Normal file
115
.github/workflows/haskell-ci-hackage.patch
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
Piggy-back on the haskell-ci workflow for automatic releases to Hackage.
|
||||
|
||||
This extends the workflow with two additional triggers:
|
||||
|
||||
* When a release is created on GitHub, 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).
|
||||
|
||||
* To make a final release, the workflow can be triggered manually by entering
|
||||
the correct version number matching the version in the cabal file. This is
|
||||
here because promoting the candidate on Hackage discards the uploaded docs
|
||||
(https://github.com/haskell/hackage-server/issues/70).
|
||||
|
||||
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,17 @@
|
||||
#
|
||||
name: Haskell-CI
|
||||
on:
|
||||
- - push
|
||||
- - pull_request
|
||||
+ push:
|
||||
+ pull_request:
|
||||
+ release:
|
||||
+ types:
|
||||
+ - published
|
||||
+ workflow_dispatch:
|
||||
+ inputs:
|
||||
+ version:
|
||||
+ # releases to Hackage are final and cannot be reverted, thus require
|
||||
+ # manual entry of version as a poor man's mistake avoidance
|
||||
+ description: version (must match version in cabal file)
|
||||
jobs:
|
||||
linux:
|
||||
name: Haskell-CI - Linux - ${{ matrix.compiler }}
|
||||
@@ -28,6 +37,7 @@
|
||||
include:
|
||||
- compiler: ghc-9.0.1
|
||||
allow-failure: false
|
||||
+ upload: true
|
||||
- compiler: ghc-8.10.4
|
||||
allow-failure: false
|
||||
- compiler: ghc-8.8.4
|
||||
@@ -171,8 +181,66 @@
|
||||
${CABAL} -vnormal check
|
||||
- name: haddock
|
||||
run: |
|
||||
- $CABAL v2-haddock $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
|
||||
$CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
|
||||
+ - name: upload artifacts (sdist)
|
||||
+ if: matrix.upload
|
||||
+ uses: actions/upload-artifact@v2
|
||||
+ with:
|
||||
+ path: ${{ github.workspace }}/sdist/*.tar.gz
|
||||
+ - name: upload artifacts (haddock)
|
||||
+ if: matrix.upload
|
||||
+ uses: actions/upload-artifact@v2
|
||||
+ with:
|
||||
+ path: ${{ github.workspace }}/haddock/*-docs.tar.gz
|
||||
+ - name: hackage upload (candidate)
|
||||
+ if: matrix.upload && github.event_name == 'release'
|
||||
+ run: |
|
||||
+ set -ex
|
||||
+ PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
||||
+ curl \
|
||||
+ --silent --show-error --fail \
|
||||
+ --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/
|
||||
+ curl \
|
||||
+ --silent --show-error --fail \
|
||||
+ -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
|
||||
+ env:
|
||||
+ HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
||||
+ PACKAGE_NAME: ${{ github.event.repository.name }}
|
||||
+ PACKAGE_VERSION: ${{ github.event.release.tag_name }}
|
||||
+ - name: hackage upload (release)
|
||||
+ if: matrix.upload && github.event_name == 'workflow_dispatch'
|
||||
+ run: |
|
||||
+ set -ex
|
||||
+ PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
||||
+ curl \
|
||||
+ --silent --show-error --fail \
|
||||
+ --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/
|
||||
+ curl \
|
||||
+ --silent --show-error --fail \
|
||||
+ -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
|
||||
+ env:
|
||||
+ HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
||||
+ PACKAGE_NAME: ${{ github.event.repository.name }}
|
||||
+ PACKAGE_VERSION: ${{ github.event.inputs.version }}
|
248
.github/workflows/haskell-ci.yml
vendored
Normal file
248
.github/workflows/haskell-ci.yml
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
# 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.12
|
||||
#
|
||||
# REGENDATA ("0.12",["github","cabal.project"])
|
||||
#
|
||||
name: Haskell-CI
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
# releases to Hackage are final and cannot be reverted, thus require
|
||||
# manual entry of version as a poor man's mistake avoidance
|
||||
description: version (must match version in cabal file)
|
||||
jobs:
|
||||
linux:
|
||||
name: Haskell-CI - Linux - ${{ matrix.compiler }}
|
||||
runs-on: ubuntu-18.04
|
||||
container:
|
||||
image: buildpack-deps:bionic
|
||||
continue-on-error: ${{ matrix.allow-failure }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- compiler: ghc-9.0.1
|
||||
allow-failure: false
|
||||
upload: true
|
||||
- compiler: ghc-8.10.4
|
||||
allow-failure: false
|
||||
- compiler: ghc-8.8.4
|
||||
allow-failure: false
|
||||
- compiler: ghc-8.6.5
|
||||
allow-failure: false
|
||||
- compiler: ghc-8.4.4
|
||||
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
|
||||
apt-add-repository -y 'ppa:hvr/ghc'
|
||||
apt-get update
|
||||
apt-get install -y $CC cabal-install-3.4 libx11-dev libxext-dev libxinerama-dev libxrandr-dev libxss-dev
|
||||
env:
|
||||
CC: ${{ matrix.compiler }}
|
||||
- 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=$(echo "/opt/$CC" | sed 's/-/\//')
|
||||
HCNAME=ghc
|
||||
HC=$HCDIR/bin/$HCNAME
|
||||
echo "HC=$HC" >> $GITHUB_ENV
|
||||
echo "HCPKG=$HCDIR/bin/$HCNAME-pkg" >> $GITHUB_ENV
|
||||
echo "HADDOCK=$HCDIR/bin/haddock" >> $GITHUB_ENV
|
||||
echo "CABAL=/opt/cabal/3.4/bin/cabal -vnormal+nowrap" >> $GITHUB_ENV
|
||||
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=--$HCNAME --with-compiler=$HC" >> $GITHUB_ENV
|
||||
echo "GHCJSARITH=0" >> $GITHUB_ENV
|
||||
env:
|
||||
CC: ${{ matrix.compiler }}
|
||||
- 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
|
||||
- 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@v2
|
||||
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
|
||||
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: cache
|
||||
uses: actions/cache@v2
|
||||
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: upload artifacts (sdist)
|
||||
if: matrix.upload
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: ${{ github.workspace }}/sdist/*.tar.gz
|
||||
- name: upload artifacts (haddock)
|
||||
if: matrix.upload
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: ${{ github.workspace }}/haddock/*-docs.tar.gz
|
||||
- name: hackage upload (candidate)
|
||||
if: matrix.upload && github.event_name == 'release'
|
||||
run: |
|
||||
set -ex
|
||||
PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
||||
curl \
|
||||
--silent --show-error --fail \
|
||||
--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/
|
||||
curl \
|
||||
--silent --show-error --fail \
|
||||
-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
|
||||
env:
|
||||
HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
||||
PACKAGE_NAME: ${{ github.event.repository.name }}
|
||||
PACKAGE_VERSION: ${{ github.event.release.tag_name }}
|
||||
- name: hackage upload (release)
|
||||
if: matrix.upload && github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
set -ex
|
||||
PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
||||
curl \
|
||||
--silent --show-error --fail \
|
||||
--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/
|
||||
curl \
|
||||
--silent --show-error --fail \
|
||||
-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
|
||||
env:
|
||||
HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
||||
PACKAGE_NAME: ${{ github.event.repository.name }}
|
||||
PACKAGE_VERSION: ${{ github.event.inputs.version }}
|
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-latest
|
||||
name: Nix Flake - Linux
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v13
|
||||
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@v2
|
||||
- name: Build
|
||||
run: nix build --print-build-logs
|
40
.github/workflows/packdeps.yml
vendored
Normal file
40
.github/workflows/packdeps.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
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@v2
|
||||
- name: Setup Haskell
|
||||
uses: haskell/actions/setup@v1
|
||||
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
|
||||
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
|
95
.github/workflows/stack.yml
vendored
Normal file
95
.github/workflows/stack.yml
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
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-12
|
||||
ghc: 8.4.4
|
||||
- resolver: lts-14
|
||||
ghc: 8.6.5
|
||||
- resolver: lts-16
|
||||
ghc: 8.8.4
|
||||
- resolver: lts-17
|
||||
ghc: 8.10.4
|
||||
- resolver: lts-18
|
||||
ghc: 8.10.7
|
||||
|
||||
steps:
|
||||
- name: Clone project
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Prepare apt sources
|
||||
run: |
|
||||
set -ex
|
||||
sudo add-apt-repository -y ppa:hvr/ghc
|
||||
sudo apt update -y
|
||||
|
||||
- name: Install C dependencies
|
||||
run: |
|
||||
set -ex
|
||||
sudo apt install -y \
|
||||
libx11-dev \
|
||||
libxext-dev \
|
||||
libxinerama-dev \
|
||||
libxrandr-dev \
|
||||
libxss-dev \
|
||||
#
|
||||
|
||||
- name: Install GHC
|
||||
# use system ghc (if available) in stack, don't waste GH Actions cache space
|
||||
continue-on-error: true
|
||||
run: |
|
||||
set -ex
|
||||
sudo apt install -y ghc-${{ matrix.ghc }}
|
||||
echo /opt/ghc/${{ matrix.ghc }}/bin >> $GITHUB_PATH
|
||||
|
||||
- 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
|
||||
run: |
|
||||
echo "::set-output name=date::$(date +%Y-%m)"
|
||||
|
||||
- name: Cache Haskell package metadata
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.stack/pantry
|
||||
key: stack-pantry-${{ runner.os }}-${{ steps.cache-date.outputs.date }}
|
||||
restore-keys: |
|
||||
stack-pantry-${{ runner.os }}-
|
||||
|
||||
- name: Cache Haskell dependencies
|
||||
uses: actions/cache@v2
|
||||
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 }}-
|
||||
stack-${{ runner.os }}-${{ matrix.resolver }}-
|
||||
|
||||
- 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
|
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
|
92
CHANGES.md
92
CHANGES.md
@@ -1,6 +1,96 @@
|
||||
# Change Log / Release Notes
|
||||
|
||||
## unknown (unknown)
|
||||
## 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.
|
||||
|
||||
[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)
|
||||
|
||||
|
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.
|
@@ -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
|
||||
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].
|
||||
|
||||
- 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!
|
||||
|
||||
@@ -55,13 +61,48 @@ Here are some tips for getting your changes merged into xmonad:
|
||||
* Your changes should include relevant entries in the `CHANGES.md`
|
||||
file. Help us communicate changes to the community.
|
||||
|
||||
* Make sure you test your changes using the [xmonad-testing][]
|
||||
repository. Include a new configuration file that shows off your
|
||||
changes if possible by creating a PR on that repository as well.
|
||||
* Make sure you test your changes against the most recent commit of
|
||||
[xmonad][] (and [xmonad-contrib][], if you're contributing there).
|
||||
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 run the automated tests. Both [xmonad-contrib][]
|
||||
and [xmonad][] have test-suites that you could run with
|
||||
`stack test` for example.
|
||||
|
||||
* Make sure you read the section on rebasing and squashing commits
|
||||
below.
|
||||
|
||||
## Style Guidelines
|
||||
|
||||
Below are some common style guidelines that all of the core modules
|
||||
follow. Before submitting a pull request, make sure that your code does
|
||||
as well!
|
||||
|
||||
* 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 module that you are making changes to
|
||||
(`n` spaces for indentation, where to break long type signatures, …)
|
||||
|
||||
* New code should not introduce any new warnings. If you want to
|
||||
check this yourself before submitting a pull request, there is the
|
||||
`pedantic` flag, which is enforced in our CI. You can enable it by
|
||||
building your changes with `stack build --flag xmonad:pedantic` or
|
||||
`cabal build --flag pedantic`.
|
||||
|
||||
* Likewise, your code should be free of [hlint] warnings; this is also
|
||||
enforced in our GitHub CI.
|
||||
|
||||
* Partial functions are to be avoided: the window manager should not
|
||||
crash, so do not call `error` or `undefined`
|
||||
|
||||
* 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).
|
||||
|
||||
## Rebasing and Squashing Commits
|
||||
|
||||
Under no circumstances should you ever merge the master branch into
|
||||
@@ -86,22 +127,27 @@ request (i.e. the `CHANGES.md` file). Here is how you do that.
|
||||
|
||||
1. Make sure that you have a `git remote` configured for the main
|
||||
repository. I like to call this remote `upstream`:
|
||||
|
||||
$ git remote add upstream https://github.com/xmonad/xmonad-contrib.git
|
||||
```shell
|
||||
$ git remote add upstream https://github.com/xmonad/xmonad-contrib.git
|
||||
```
|
||||
|
||||
2. Pull from upstream and rewrite your changes on top of master. For
|
||||
this to work you should not have any modified files in your
|
||||
working directory. Run these commands from within your feature
|
||||
branch (the branch you are asking to be merged):
|
||||
|
||||
$ git fetch --all
|
||||
$ git pull --rebase upstream master
|
||||
```shell
|
||||
$ git fetch --all
|
||||
$ git pull --rebase upstream master
|
||||
```
|
||||
|
||||
3. If the rebase was successful you can now push your feature branch
|
||||
back to GitHub. You need to force the push since your commits
|
||||
have been rewritten and have new IDs:
|
||||
|
||||
$ git push --force-with-lease
|
||||
```shell
|
||||
$ git push --force-with-lease
|
||||
```
|
||||
|
||||
4. Your pull request should now be conflict-free and only contain the
|
||||
changes that you actually made.
|
||||
@@ -117,8 +163,9 @@ each pull request contains just one commit.
|
||||
|
||||
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
|
||||
```shell
|
||||
$ 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.
|
||||
@@ -131,11 +178,16 @@ each pull request contains just one commit.
|
||||
|
||||
6. If everything was successful you can push your changed history
|
||||
back up to GitHub:
|
||||
```shell
|
||||
$ git push --force-with-lease
|
||||
```
|
||||
|
||||
$ git push --force-with-lease
|
||||
|
||||
[hlint]: https://github.com/ndmitchell/hlint
|
||||
[xmonad]: https://github.com/xmonad/xmonad
|
||||
[xmonad-contrib]: https://github.com/xmonad/xmonad-contrib
|
||||
[xmonad-testing]: https://github.com/xmonad/xmonad-testing
|
||||
[x11]: https://github.com/xmonad/X11
|
||||
[ml]: https://mail.haskell.org/cgi-bin/mailman/listinfo/xmonad
|
||||
[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
|
||||
|
376
INSTALL.md
Normal file
376
INSTALL.md
Normal file
@@ -0,0 +1,376 @@
|
||||
# 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 \
|
||||
> libx11 libxft libxinerama libxrandr libxss \
|
||||
> pkgconf
|
||||
```
|
||||
|
||||
## 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.15 https://github.com/xmonad/xmonad
|
||||
$ git clone --branch v0.16 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`!
|
||||
|
||||
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`!
|
||||
|
||||
#### 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/
|
45
LICENSE
45
LICENSE
@@ -1,31 +1,28 @@
|
||||
Copyright (c) 2007,2008 Spencer Janssen
|
||||
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
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
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
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
3. Neither the name of the author nor the names of his contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
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.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 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.
|
||||
|
148
MAINTAINERS.md
148
MAINTAINERS.md
@@ -2,82 +2,132 @@
|
||||
|
||||
## The XMonad Core Team
|
||||
|
||||
* Adam Vogt [GitHub][aavogt]
|
||||
|
||||
* Brandon S Allbery [GitHub][geekosaur], IRC: `geekosaur`
|
||||
|
||||
* Brent Yorgey [GitHub][byorgey], IRC: `byorgey`
|
||||
|
||||
* Daniel Wagner [GitHub][dmwit], IRC: `dmwit`
|
||||
* 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]
|
||||
* slotThe [GitHub][slotThe], IRC: `Solid`
|
||||
|
||||
* Peter J. Jones [GitHub][pjones], [Twitter][twitter:pjones], [OpenPGP Key][pgp:pjones], IRC: `pmade`
|
||||
* Tomáš Janoušek [GitHub][liskin], [Twitter][twitter:liskin], IRC: `liskin`, [GPG][gpg:liskin]
|
||||
|
||||
[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:liskin]: https://github.com/liskin.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
|
||||
|
||||
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
|
||||
development. Changes you make on this branch will be merged back
|
||||
into `master` as one of the last steps.
|
||||
2. Review documentation files and make sure they are accurate:
|
||||
|
||||
2. Update the version number in the `*.cabal` files and verify
|
||||
dependencies and documentation. This includes the `tested-with:`
|
||||
field.
|
||||
- [`README.md`](README.md)
|
||||
- [`CHANGES.md`](CHANGES.md)
|
||||
- [`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
|
||||
versions correct. If you need to update the version of a
|
||||
dependency then you should rebuild and retest.
|
||||
If the manpage changes, wait for the CI to rebuild the rendered outputs.
|
||||
|
||||
4. Review documentation files and make sure they are accurate:
|
||||
3. 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.
|
||||
|
||||
- `README.md`
|
||||
- `CHANGES.md`
|
||||
- and the `example-config.hs` in the `xmonad-testing` repo
|
||||
4. Create a release on GitHub:
|
||||
|
||||
5. Generate the manpage:
|
||||
- https://github.com/xmonad/xmonad/releases/new
|
||||
- https://github.com/xmonad/xmonad-contrib/releases/new
|
||||
|
||||
* `cabal configure` with the `-fgeneratemanpage` flag
|
||||
* Build the project
|
||||
* Run the `generatemanpage` tool from the top level of this repo
|
||||
* Review the man page: `man -l man/xmonad.1`
|
||||
CI will upload a release candidate to Hackage. Check again that
|
||||
everything looks good. To publish a final release, run the CI workflow
|
||||
once again with the correct version number:
|
||||
|
||||
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`)
|
||||
See [haskell-ci-hackage.patch][] for details about the release infrastructure.
|
||||
|
||||
8. Upload the packages to Hackage (`cabal upload`)
|
||||
5. Update the website:
|
||||
|
||||
9. Merge the release branches into `master`
|
||||
- Post a [new release announcement][web-announce]
|
||||
- Check install instructions, guided tour, keybindings cheat sheet, …
|
||||
|
||||
10. Update the website:
|
||||
7. Post announcement to:
|
||||
|
||||
* Generate and push haddocks with `xmonad-web/gen-docs.sh`
|
||||
- [xmonad.org website](https://github.com/xmonad/xmonad-web/tree/gh-pages/news/_posts)
|
||||
- [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/)
|
||||
|
||||
* Check that `tour.html` and `intro.html` are up to date, and
|
||||
mention all core bindings
|
||||
See [old announcements][old-announce] for inspiration.
|
||||
|
||||
11. Update the topic for the IRC channel (`#xmonad`)
|
||||
[packdeps]: https://hackage.haskell.org/package/packdeps
|
||||
[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/tree/55614349421ebafaef4a47424fcb16efa80ff768
|
||||
|
||||
12. Send the `announce-0.XX.txt` file to:
|
||||
## Website and Other Accounts
|
||||
|
||||
- XMonad mailing list
|
||||
- Haskell Cafe
|
||||
* The [xmonad twitter] is tended to by [liskin].
|
||||
|
||||
[packdeps]: http://hackage.haskell.org/package/packdeps
|
||||
* The [xmonad.org] domain is owned by [eyenx] and the website itself is
|
||||
deployed via GitHub Pages. It can be updated by making a pull request
|
||||
against the [xmonad-web] repository.
|
||||
|
||||
[aavogt]: https://github.com/orgs/xmonad/people/aavogt
|
||||
[geekosaur]: https://github.com/orgs/xmonad/people/geekosaur
|
||||
[byorgey]: https://github.com/orgs/xmonad/people/byorgey
|
||||
[dmwit]: https://github.com/orgs/xmonad/people/dmwit
|
||||
[davidlazar]: https://github.com/orgs/xmonad/people/davidlazar
|
||||
[twifkak]: https://github.com/orgs/xmonad/people/twifkak
|
||||
|
||||
[pjones]: https://github.com/orgs/xmonad/people/pjones
|
||||
[twitter:pjones]: https://twitter.com/contextualdev
|
||||
[pgp:pjones]: http://pgp.mit.edu/pks/lookup?op=get&search=0x526722D1204284CB
|
||||
[eyenx]: https://github.com/eyenx
|
||||
[xmonad-web]: https://github.com/xmonad/xmonad-web/
|
||||
[xmonad.org]: https://xmonad.org/
|
||||
[xmonad twitter]: https://twitter.com/xmonad
|
||||
|
202
README.md
202
README.md
@@ -1,8 +1,42 @@
|
||||
# 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>
|
||||
</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
|
||||
screen use. Window manager features are accessible from the keyboard:
|
||||
a mouse is optional. xmonad is written, configured and extensible in
|
||||
@@ -12,118 +46,74 @@ dynamically, and different layouts may be used on each
|
||||
workspace. Xinerama is fully supported, allowing windows to be tiled
|
||||
on several physical screens.
|
||||
|
||||
## Quick Start
|
||||
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
|
||||
cabal install xmonad xmonad-contrib
|
||||
For installation and configuration instructions, please see:
|
||||
|
||||
* 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.
|
||||
On many systems xmonad is available as a binary package in your
|
||||
package system (e.g. on Debian or Gentoo). If at all possible, use this
|
||||
in preference to a source build, as the dependency resolution will be
|
||||
simpler.
|
||||
|
||||
We'll now walk through the complete list of toolchain dependencies.
|
||||
|
||||
* GHC: the Glasgow Haskell Compiler
|
||||
|
||||
You first need a Haskell compiler. Your distribution's package
|
||||
system will have binaries of GHC (the Glasgow Haskell Compiler),
|
||||
the compiler we use, so install that first. If your operating
|
||||
system's package system doesn't provide a binary version of GHC
|
||||
and the `cabal-install` tool, you can install both using the
|
||||
[Haskell Platform][platform].
|
||||
|
||||
It shouldn't be necessary to compile GHC from source -- every common
|
||||
system has a pre-build binary version. However, if you want to
|
||||
build from source, the following links will be helpful:
|
||||
|
||||
- GHC: <http://haskell.org/ghc/>
|
||||
|
||||
- Cabal: <http://haskell.org/cabal/download.html>
|
||||
|
||||
* X11 libraries:
|
||||
|
||||
Since you're building an X application, you'll need the C X11
|
||||
library headers. On many platforms, these come pre-installed. For
|
||||
others, such as Debian, you can get them from your package manager:
|
||||
|
||||
# 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)
|
||||
Please do read the [CONTRIBUTING][gh:xmonad:contributing] document for more
|
||||
information about bug reporting and code contributions. For a brief overview
|
||||
of the architecture and code conventions, see the [documentation for the
|
||||
`XMonad.Doc.Developing` module][doc:developing]. If in doubt, [talk to
|
||||
us][web:community].
|
||||
|
||||
## Authors
|
||||
|
||||
* Spencer Janssen
|
||||
* Don Stewart
|
||||
* Jason Creighton
|
||||
Started in 2007 by [Spencer Janssen][gh:spencerjanssen], [Don
|
||||
Stewart][gh:donsbot] and [Jason Creighton][gh:JasonCreighton], the
|
||||
[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
|
||||
[xmonadcontrib]: https://hackage.haskell.org/package/xmonad-contrib
|
||||
[xmc-prompt-shell]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Prompt-Shell.html
|
||||
[platform]: http://haskell.org/platform/
|
||||
[example-config]: https://github.com/xmonad/xmonad-testing/blob/master/example-config.hs
|
||||
[config]: https://github.com/xmonad/xmonad/blob/master/CONFIG
|
||||
[gh:spencerjanssen]: https://github.com/spencerjanssen
|
||||
[gh:donsbot]: https://github.com/donsbot
|
||||
[gh:JasonCreighton]: https://github.com/JasonCreighton
|
||||
|
||||
[doc:developing]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Doc-Developing.html
|
||||
[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).
|
1267
TUTORIAL.md
Normal file
1267
TUTORIAL.md
Normal file
File diff suppressed because it is too large
Load Diff
14
cabal.haskell-ci
Normal file
14
cabal.haskell-ci
Normal file
@@ -0,0 +1,14 @@
|
||||
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
|
@@ -1 +1,4 @@
|
||||
packages: ./
|
||||
-- cabal.project
|
||||
|
||||
packages:
|
||||
xmonad.cabal
|
||||
|
27
flake.nix
Normal file
27
flake.nix
Normal file
@@ -0,0 +1,27 @@
|
||||
# This file is maintained by @IvanMalison (github)
|
||||
{
|
||||
inputs = {
|
||||
flake-utils.url = github:numtide/flake-utils;
|
||||
git-ignore-nix.url = github:IvanMalison/gitignore.nix/master;
|
||||
};
|
||||
outputs = { self, flake-utils, nixpkgs, git-ignore-nix }:
|
||||
let
|
||||
overlay = final: prev: {
|
||||
haskellPackages = prev.haskellPackages.override (old: {
|
||||
overrides = prev.lib.composeExtensions (old.overrides or (_: _: {}))
|
||||
(hself: hsuper: {
|
||||
xmonad = hself.callCabal2nix "xmonad" (git-ignore-nix.gitIgnoreSource ./.) { };
|
||||
});
|
||||
});
|
||||
};
|
||||
overlays = [ overlay ];
|
||||
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 overlay overlays; } ;
|
||||
}
|
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=$@ $<
|
141
man/xmonad.1
141
man/xmonad.1
@@ -1,13 +1,13 @@
|
||||
.\" Automatically generated by Pandoc 2.2.1
|
||||
.\" Automatically generated by Pandoc 2.5
|
||||
.\"
|
||||
.TH "XMONAD" "1" "20 August 2018" "Tiling Window Manager" ""
|
||||
.TH "XMONAD" "1" "27 October 2021" "Tiling Window Manager" ""
|
||||
.hy
|
||||
.SH Name
|
||||
.PP
|
||||
xmonad \- Tiling Window Manager
|
||||
.SH Description
|
||||
.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.
|
||||
Windows are managed using automatic layout algorithms, which can be
|
||||
dynamically reconfigured.
|
||||
@@ -15,14 +15,14 @@ At any time windows are arranged so as to maximize the use of screen
|
||||
real estate.
|
||||
All features of the window manager are accessible purely from the
|
||||
keyboard: a mouse is entirely optional.
|
||||
\f[I]xmonad\f[] is configured in Haskell, and custom layout algorithms
|
||||
\f[I]xmonad\f[R] is configured in Haskell, and custom layout algorithms
|
||||
may be implemented by the user in config files.
|
||||
A principle of \f[I]xmonad\f[] is predictability: the user should know
|
||||
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
|
||||
action.
|
||||
.PP
|
||||
By default, \f[I]xmonad\f[] provides three layout algorithms: tall, wide
|
||||
and fullscreen.
|
||||
By default, \f[I]xmonad\f[R] 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
|
||||
@@ -31,9 +31,9 @@ Multiple physical monitors are supported via Xinerama, allowing
|
||||
simultaneous display of a number of screens.
|
||||
.PP
|
||||
By utilizing the expressivity of a modern functional language with a
|
||||
rich static type system, \f[I]xmonad\f[] provides a complete, featureful
|
||||
window manager in less than 1200 lines of code, with an emphasis on
|
||||
correctness and robustness.
|
||||
rich static type system, \f[I]xmonad\f[R] 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.
|
||||
@@ -41,7 +41,7 @@ A benefit of this is that the code is simple to understand, and easy to
|
||||
modify.
|
||||
.SH Usage
|
||||
.PP
|
||||
\f[I]xmonad\f[] places each window into a \[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
|
||||
though with mod\-j and mod\-k.
|
||||
Windows are either displayed full screen, tiled horizontally, or tiled
|
||||
@@ -58,7 +58,7 @@ When running with multiple monitors (Xinerama), each screen has exactly
|
||||
1 workspace visible.
|
||||
mod\-{w,e,r} switch the focus between screens, while shift\-mod\-{w,e,r}
|
||||
move the current window to that screen.
|
||||
When \f[I]xmonad\f[] starts, workspace 1 is on screen 1, workspace 2 is
|
||||
When \f[I]xmonad\f[R] 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.
|
||||
@@ -68,216 +68,155 @@ xmonad has several flags which you may pass to the executable.
|
||||
These flags are:
|
||||
.TP
|
||||
.B \[en]recompile
|
||||
Recompiles your configuration in \f[I]~/.xmonad/xmonad.hs\f[]
|
||||
.RS
|
||||
.RE
|
||||
Recompiles your \f[I]xmonad.hs\f[R] configuration
|
||||
.TP
|
||||
.B \[en]restart
|
||||
Causes the currently running \f[I]xmonad\f[] process to restart
|
||||
.RS
|
||||
.RE
|
||||
Causes the currently running \f[I]xmonad\f[R] process to restart
|
||||
.TP
|
||||
.B \[en]replace
|
||||
Replace the current window manager with xmonad
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B \[en]version
|
||||
Display version of \f[I]xmonad\f[]
|
||||
.RS
|
||||
.RE
|
||||
Display version of \f[I]xmonad\f[R]
|
||||
.TP
|
||||
.B \[en]verbose\-version
|
||||
Display detailed version of \f[I]xmonad\f[]
|
||||
.RS
|
||||
.RE
|
||||
.PP
|
||||
##Default keyboard bindings
|
||||
Display detailed version of \f[I]xmonad\f[R]
|
||||
.SS Default keyboard bindings
|
||||
.TP
|
||||
.B mod\-shift\-return
|
||||
Launch terminal
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-p
|
||||
Launch dmenu
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-p
|
||||
Launch gmrun
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-c
|
||||
Close the focused window
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-space
|
||||
Rotate through the available layout algorithms
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-space
|
||||
Reset the layouts on the current workspace to default
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-n
|
||||
Resize viewed windows to the correct size
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-tab
|
||||
Move focus to the next window
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-tab
|
||||
Move focus to the previous window
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-j
|
||||
Move focus to the next window
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-k
|
||||
Move focus to the previous window
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-m
|
||||
Move focus to the master window
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-return
|
||||
Swap the focused window and the master window
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-j
|
||||
Swap the focused window with the next window
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-k
|
||||
Swap the focused window with the previous window
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-h
|
||||
Shrink the master area
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-l
|
||||
Expand the master area
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-t
|
||||
Push window back into tiling
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-comma
|
||||
Increment the number of windows in the master area
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-period
|
||||
Deincrement the number of windows in the master area
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-q
|
||||
Quit xmonad
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-q
|
||||
Restart xmonad
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-slash
|
||||
Run xmessage with a summary of the default keybindings (useful for
|
||||
beginners)
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-question
|
||||
Run xmessage with a summary of the default keybindings (useful for
|
||||
beginners)
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-[1..9]
|
||||
Switch to workspace N
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-[1..9]
|
||||
Move client to workspace N
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-{w,e,r}
|
||||
Switch to physical/Xinerama screens 1, 2, or 3
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-shift\-{w,e,r}
|
||||
Move client to screen 1, 2, or 3
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-button1
|
||||
Set the window to floating mode and move by dragging
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-button2
|
||||
Raise the window to the top of the stack
|
||||
.RS
|
||||
.RE
|
||||
.TP
|
||||
.B mod\-button3
|
||||
Set the window to floating mode and resize by dragging
|
||||
.RS
|
||||
.RE
|
||||
.SH Examples
|
||||
.PP
|
||||
To use xmonad as your window manager add to your \f[I]~/.xinitrc\f[]
|
||||
file:
|
||||
To use xmonad as your window manager add to your
|
||||
\f[I]\[ti]/.xinitrc\f[R] file:
|
||||
.RS
|
||||
.PP
|
||||
exec xmonad
|
||||
.RE
|
||||
.SH Customization
|
||||
.PP
|
||||
xmonad is customized in ~/.xmonad/xmonad.hs, and then restarted with
|
||||
mod\-q.
|
||||
xmonad is customized in your \f[I]xmonad.hs\f[R], and then restarted
|
||||
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
|
||||
You can find many extensions to the core feature set in the xmonad\-
|
||||
contrib package, available through your package manager or from
|
||||
xmonad.org (http://xmonad.org).
|
||||
xmonad.org (https://xmonad.org).
|
||||
.SS Modular Configuration
|
||||
.PP
|
||||
As of \f[I]xmonad\-0.9\f[], any additional Haskell modules may be placed
|
||||
in \f[I]~/.xmonad/lib/\f[] are available in GHC's searchpath.
|
||||
As of \f[I]xmonad\-0.9\f[R], any additional Haskell modules may be
|
||||
placed in \f[I]\[ti]/.xmonad/lib/\f[R] are available in GHC\[cq]s
|
||||
searchpath.
|
||||
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
|
||||
.nf
|
||||
\f[C]
|
||||
module\ XMonad.Stack.MyAdditions\ (function1)\ where
|
||||
\ \ function1\ =\ error\ "function1:\ Not\ implemented\ yet!"
|
||||
\f[]
|
||||
module XMonad.Stack.MyAdditions (function1) where
|
||||
function1 = error \[dq]function1: Not implemented yet!\[dq]
|
||||
\f[R]
|
||||
.fi
|
||||
.PP
|
||||
Your xmonad.hs may then import XMonad.Stack.MyAdditions as if that
|
||||
|
@@ -5,7 +5,7 @@
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<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>
|
||||
<style type="text/css">
|
||||
code{white-space: pre-wrap;}
|
||||
@@ -31,7 +31,7 @@ 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);
|
||||
{ content: attr(title);
|
||||
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;
|
||||
@@ -76,15 +76,12 @@ code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
</style>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">XMONAD(1) Tiling Window Manager</h1>
|
||||
<p class="author"></p>
|
||||
<p class="date">20 August 2018</p>
|
||||
<p class="date">27 October 2021</p>
|
||||
</header>
|
||||
<nav id="TOC">
|
||||
<ul>
|
||||
@@ -92,6 +89,7 @@ code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warni
|
||||
<li><a href="#description">Description</a></li>
|
||||
<li><a href="#usage">Usage</a><ul>
|
||||
<li><a href="#flags">Flags</a></li>
|
||||
<li><a href="#default-keyboard-bindings">Default keyboard bindings</a></li>
|
||||
</ul></li>
|
||||
<li><a href="#examples">Examples</a></li>
|
||||
<li><a href="#customization">Customization</a><ul>
|
||||
@@ -114,7 +112,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>
|
||||
<dl>
|
||||
<dt>–recompile</dt>
|
||||
<dd>Recompiles your configuration in <em>~/.xmonad/xmonad.hs</em>
|
||||
<dd>Recompiles your <em>xmonad.hs</em> configuration
|
||||
</dd>
|
||||
<dt>–restart</dt>
|
||||
<dd>Causes the currently running <em>xmonad</em> process to restart
|
||||
@@ -129,7 +127,7 @@ code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warni
|
||||
<dd>Display detailed version of <em>xmonad</em>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>##Default keyboard bindings</p>
|
||||
<h2 id="default-keyboard-bindings">Default keyboard bindings</h2>
|
||||
<dl>
|
||||
<dt>mod-shift-return</dt>
|
||||
<dd>Launch terminal
|
||||
@@ -231,12 +229,17 @@ code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warni
|
||||
<p>exec xmonad</p>
|
||||
</blockquote>
|
||||
<h1 id="customization">Customization</h1>
|
||||
<p>xmonad is customized in ~/.xmonad/xmonad.hs, and then restarted with mod-q.</p>
|
||||
<p>You can find many extensions to the core feature set in the xmonad- contrib package, available through your package manager or from <a href="http://xmonad.org">xmonad.org</a>.</p>
|
||||
<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>
|
||||
<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>
|
||||
<p>As of <em>xmonad-0.9</em>, any additional Haskell modules may be placed in <em>~/.xmonad/lib/</em> are available in GHC’s searchpath. Hierarchical modules are supported: for example, the file <em>~/.xmonad/lib/XMonad/Stack/MyAdditions.hs</em> could contain:</p>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb1-1" data-line-number="1"><span class="kw">module</span> <span class="dt">XMonad.Stack.MyAdditions</span> (function1) <span class="kw">where</span></a>
|
||||
<a class="sourceLine" id="cb1-2" data-line-number="2"> function1 <span class="fu">=</span> error <span class="st">"function1: Not implemented yet!"</span></a></code></pre></div>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb1-1" title="1"><span class="kw">module</span> <span class="dt">XMonad.Stack.MyAdditions</span> (function1) <span class="kw">where</span></a>
|
||||
<a class="sourceLine" id="cb1-2" title="2"> function1 <span class="ot">=</span> <span class="fu">error</span> <span class="st">"function1: Not implemented yet!"</span></a></code></pre></div>
|
||||
<p>Your xmonad.hs may then import XMonad.Stack.MyAdditions as if that module was contained within xmonad or xmonad-contrib.</p>
|
||||
<h1 id="bugs">Bugs</h1>
|
||||
<p>Probably. If you find any, please report them to the <a href="https://github.com/xmonad/xmonad/issues">bugtracker</a></p>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
% XMONAD(1) Tiling Window Manager
|
||||
%
|
||||
% 20 August 2018
|
||||
% 27 October 2021
|
||||
|
||||
# Name
|
||||
|
||||
@@ -58,7 +58,7 @@ xmonad has several flags which you may pass to the executable.
|
||||
These flags are:
|
||||
|
||||
--recompile
|
||||
: Recompiles your configuration in _~/.xmonad/xmonad.hs_
|
||||
: Recompiles your _xmonad.hs_ configuration
|
||||
|
||||
--restart
|
||||
: Causes the currently running _xmonad_ process to restart
|
||||
@@ -72,9 +72,100 @@ These flags are:
|
||||
--verbose-version
|
||||
: 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
|
||||
|
||||
@@ -83,8 +174,16 @@ To use xmonad as your window manager add to your _~/.xinitrc_ file:
|
||||
> exec xmonad
|
||||
|
||||
# Customization
|
||||
xmonad is customized in ~/.xmonad/xmonad.hs, and then restarted
|
||||
with mod-q.
|
||||
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
|
||||
@@ -107,5 +206,5 @@ module was contained within xmonad or xmonad-contrib.
|
||||
# Bugs
|
||||
Probably. If you find any, please report them to the [bugtracker]
|
||||
|
||||
[xmonad.org]: http://xmonad.org
|
||||
[xmonad.org]: https://xmonad.org
|
||||
[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
|
@@ -129,7 +129,7 @@ myKeys conf@(XConfig {XMonad.modMask = modm}) = M.fromList $
|
||||
, ((modm , xK_q ), spawn "xmonad --recompile; xmonad --restart")
|
||||
|
||||
-- Run xmessage with a summary of the default keybindings (useful for beginners)
|
||||
, ((modm .|. shiftMask, xK_slash ), spawn ("echo \"" ++ help ++ "\" | xmessage -file -"))
|
||||
, ((modm .|. shiftMask, xK_slash ), xmessage help)
|
||||
]
|
||||
++
|
||||
|
||||
@@ -293,6 +293,7 @@ help = unlines ["The default modifier key is 'alt'. Default keybindings:",
|
||||
"mod-Space Rotate through the available layout algorithms",
|
||||
"mod-Shift-Space Reset the layouts on the current workSpace to default",
|
||||
"mod-n Resize/refresh viewed windows to the correct size",
|
||||
"mod-Shift-/ Show this help message with the default keybindings",
|
||||
"",
|
||||
"-- move focus up or down the window stack",
|
||||
"mod-Tab Move focus to the next window",
|
||||
|
@@ -39,7 +39,7 @@ import XMonad.Operations
|
||||
import XMonad.ManageHook
|
||||
import qualified XMonad.StackSet as W
|
||||
import Data.Bits ((.|.))
|
||||
import Data.Default
|
||||
import Data.Default.Class
|
||||
import Data.Monoid
|
||||
import qualified Data.Map as M
|
||||
import System.Exit
|
||||
@@ -239,7 +239,7 @@ keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
||||
, (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
|
||||
where
|
||||
helpCommand :: X ()
|
||||
helpCommand = spawn ("echo " ++ show help ++ " | xmessage -file -")
|
||||
helpCommand = xmessage help
|
||||
|
||||
-- | Mouse bindings: default actions bound to mouse events
|
||||
mouseBindings :: XConfig Layout -> M.Map (KeyMask, Button) (Window -> X ())
|
||||
@@ -277,6 +277,7 @@ instance (a ~ Choose Tall (Choose (Mirror Tall) Full)) => Default (XConfig a) wh
|
||||
, XMonad.handleExtraArgs = \ xs theConf -> case xs of
|
||||
[] -> return theConf
|
||||
_ -> fail ("unrecognized flags:" ++ show xs)
|
||||
, XMonad.extensibleConf = M.empty
|
||||
}
|
||||
|
||||
-- | The default set of configuration values itself
|
||||
@@ -296,6 +297,7 @@ help = unlines ["The default modifier key is 'alt'. Default keybindings:",
|
||||
"mod-Space Rotate through the available layout algorithms",
|
||||
"mod-Shift-Space Reset the layouts on the current workSpace to default",
|
||||
"mod-n Resize/refresh viewed windows to the correct size",
|
||||
"mod-Shift-/ Show this help message with the default keybindings",
|
||||
"",
|
||||
"-- move focus up or down the window stack",
|
||||
"mod-Tab Move focus to the next window",
|
||||
|
@@ -1,5 +1,6 @@
|
||||
{-# LANGUAGE ExistentialQuantification, FlexibleInstances, GeneralizedNewtypeDeriving,
|
||||
MultiParamTypeClasses, TypeSynonymInstances, DeriveDataTypeable #-}
|
||||
MultiParamTypeClasses, TypeSynonymInstances, DeriveDataTypeable,
|
||||
LambdaCase, NamedFieldPuns, DeriveTraversable #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
@@ -22,26 +23,29 @@ module XMonad.Core (
|
||||
XConf(..), XConfig(..), LayoutClass(..),
|
||||
Layout(..), readsLayout, Typeable, Message,
|
||||
SomeMessage(..), fromMessage, LayoutMessages(..),
|
||||
StateExtension(..), ExtensionClass(..),
|
||||
StateExtension(..), ExtensionClass(..), ConfExtension(..),
|
||||
runX, catchX, userCode, userCodeDef, io, catchIO, installSignalHandlers, uninstallSignalHandlers,
|
||||
withDisplay, withWindowSet, isRoot, runOnWorkspaces,
|
||||
getAtom, spawn, spawnPID, xfork, recompile, trace, whenJust, whenX,
|
||||
getXMonadDir, getXMonadCacheDir, getXMonadDataDir, stateFileName,
|
||||
getAtom, spawn, spawnPID, xfork, xmessage, recompile, trace, whenJust, whenX,
|
||||
getXMonadDir, getXMonadCacheDir, getXMonadDataDir, stateFileName, binFileName,
|
||||
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
|
||||
|
||||
import XMonad.StackSet hiding (modify)
|
||||
|
||||
import Prelude
|
||||
import Control.Exception.Extensible (fromException, try, bracket, throw, finally, SomeException(..))
|
||||
import qualified Control.Exception.Extensible as E
|
||||
import Control.Applicative(Applicative, pure, (<$>), (<*>))
|
||||
import Control.Exception (fromException, try, bracket, bracket_, throw, finally, SomeException(..))
|
||||
import qualified Control.Exception as E
|
||||
import Control.Applicative ((<|>), empty)
|
||||
import Control.Monad.Fail
|
||||
import Control.Monad.State
|
||||
import Control.Monad.Reader
|
||||
import Data.Semigroup
|
||||
import Data.Default
|
||||
import Data.Traversable (for)
|
||||
import Data.Time.Clock (UTCTime)
|
||||
import Data.Default.Class
|
||||
import Data.List (isInfixOf)
|
||||
import System.FilePath
|
||||
import System.IO
|
||||
import System.Info
|
||||
@@ -58,8 +62,6 @@ import Graphics.X11.Xlib.Extras (getWindowAttributes, WindowAttributes, Event)
|
||||
import Data.Typeable
|
||||
import Data.List ((\\))
|
||||
import Data.Maybe (isJust,fromMaybe)
|
||||
import Data.Monoid hiding ((<>))
|
||||
import System.Environment (lookupEnv)
|
||||
|
||||
import qualified Data.Map as M
|
||||
import qualified Data.Set as S
|
||||
@@ -93,8 +95,8 @@ data XConf = XConf
|
||||
, mousePosition :: !(Maybe (Position, Position))
|
||||
-- ^ position of the mouse according to
|
||||
-- the event currently being processed
|
||||
, currentEvent :: !(Maybe Event)
|
||||
-- ^ event currently being processed
|
||||
, currentEvent :: !(Maybe Event) -- ^ event currently being processed
|
||||
, directories :: !Directories -- ^ directories to use
|
||||
}
|
||||
|
||||
-- todo, better name
|
||||
@@ -122,6 +124,11 @@ data XConfig l = XConfig
|
||||
, rootMask :: !EventMask -- ^ The root events that xmonad is interested in
|
||||
, handleExtraArgs :: !([String] -> XConfig Layout -> IO (XConfig Layout))
|
||||
-- ^ Modify the configuration, complain about extra arguments etc. with arguments that are not handled by default
|
||||
, 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 +142,8 @@ type WorkspaceId = String
|
||||
newtype ScreenId = S Int deriving (Eq,Ord,Show,Read,Enum,Num,Integral,Real)
|
||||
|
||||
-- | The 'Rectangle' with screen dimensions
|
||||
data ScreenDetail = SD { screenRect :: !Rectangle } deriving (Eq,Show, Read)
|
||||
newtype ScreenDetail = SD { screenRect :: Rectangle }
|
||||
deriving (Eq,Show, Read)
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
@@ -148,7 +156,7 @@ data ScreenDetail = SD { screenRect :: !Rectangle } deriving (Eq,Show, Read)
|
||||
-- instantiated on 'XConf' and 'XState' automatically.
|
||||
--
|
||||
newtype X a = X (ReaderT XConf (StateT XState IO) a)
|
||||
deriving (Functor, Monad, MonadFail, MonadIO, MonadState XState, MonadReader XConf, Typeable)
|
||||
deriving (Functor, Monad, MonadFail, MonadIO, MonadState XState, MonadReader XConf)
|
||||
|
||||
instance Applicative X where
|
||||
pure = return
|
||||
@@ -255,14 +263,18 @@ readsLayout (Layout l) s = [(Layout (asTypeOf x l), rs) | (x, rs) <- reads s]
|
||||
-- | Every layout must be an instance of 'LayoutClass', which defines
|
||||
-- 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'
|
||||
--
|
||||
-- You should also strongly consider implementing 'description',
|
||||
-- although it is not required.
|
||||
-- * 'description'
|
||||
--
|
||||
-- Note that any code which /uses/ 'LayoutClass' methods should only
|
||||
-- ever call 'runLayout', 'handleMessage', and 'description'! In
|
||||
@@ -271,7 +283,7 @@ readsLayout (Layout l) s = [(Layout (asTypeOf x l), rs) | (x, rs) <- reads s]
|
||||
-- 'runLayout', 'handleMessage', and so on. This ensures that the
|
||||
-- proper methods will be used, regardless of the particular methods
|
||||
-- that any 'LayoutClass' instance chooses to define.
|
||||
class Show (layout a) => LayoutClass layout a where
|
||||
class (Show (layout a), Typeable layout) => LayoutClass layout a where
|
||||
|
||||
-- | By default, 'runLayout' calls 'doLayout' if there are any
|
||||
-- windows to be laid out, and 'emptyLayout' otherwise. Most
|
||||
@@ -373,12 +385,12 @@ instance Message Event
|
||||
-- layouts) should consider handling.
|
||||
data LayoutMessages = Hide -- ^ sent when a layout becomes non-visible
|
||||
| ReleaseResources -- ^ sent when xmonad is exiting or restarting
|
||||
deriving (Typeable, Eq)
|
||||
deriving Eq
|
||||
|
||||
instance Message LayoutMessages
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- Extensible state
|
||||
-- Extensible state/config
|
||||
--
|
||||
|
||||
-- | Every module must make the data it wants to store
|
||||
@@ -386,6 +398,7 @@ instance Message LayoutMessages
|
||||
--
|
||||
-- Minimal complete definition: initialValue
|
||||
class Typeable a => ExtensionClass a where
|
||||
{-# MINIMAL initialValue #-}
|
||||
-- | Defines an initial value for the state extension
|
||||
initialValue :: a
|
||||
-- | Specifies whether the state extension should be
|
||||
@@ -404,6 +417,9 @@ data StateExtension =
|
||||
| forall a. (Read a, Show a, ExtensionClass a) => PersistentExtension a
|
||||
-- ^ Persistent extension
|
||||
|
||||
-- | Existential type to store a config extension.
|
||||
data ConfExtension = forall a. Typeable a => ConfExtension a
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- | General utilities
|
||||
--
|
||||
@@ -439,6 +455,16 @@ xfork x = io . forkProcess . finally nullStdin $ do
|
||||
dupTo fd stdInput
|
||||
closeFd fd
|
||||
|
||||
-- | Use @xmessage@ to show information to the user.
|
||||
xmessage :: MonadIO m => String -> m ()
|
||||
xmessage msg = void . xfork $ do
|
||||
executeFile "xmessage" 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
|
||||
-- each workspace with the output of that function being the modified workspace.
|
||||
runOnWorkspaces :: (WindowSpace -> X WindowSpace) -> X ()
|
||||
@@ -449,137 +475,254 @@ runOnWorkspaces job = do
|
||||
$ current ws : visible ws
|
||||
modify $ \s -> s { windowset = ws { current = c, visible = v, hidden = h } }
|
||||
|
||||
-- | Return the path to the xmonad configuration directory. This
|
||||
-- directory is where user configuration files are stored (e.g, the
|
||||
-- xmonad.hs file). You may also create a @lib@ subdirectory in the
|
||||
-- configuration directory and the default recompile command will add
|
||||
-- it to the GHC include path.
|
||||
-- | All the directories that xmonad will use. They will be used for
|
||||
-- the following purposes:
|
||||
--
|
||||
-- Several directories are considered. In order of
|
||||
-- preference:
|
||||
-- * @dataDir@: This directory is used by XMonad to store data files
|
||||
-- such as the run-time state file.
|
||||
--
|
||||
-- 1. The directory specified in the @XMONAD_CONFIG_DIR@ environment variable.
|
||||
-- 2. The @~\/.xmonad@ directory.
|
||||
-- 3. The @XDG_CONFIG_HOME/xmonad@ directory.
|
||||
-- * @cfgDir@: This directory is where user configuration files are
|
||||
-- stored (e.g, the xmonad.hs file). You may also create a @lib@
|
||||
-- subdirectory in the configuration directory and the default recompile
|
||||
-- command will add it to the GHC include path.
|
||||
--
|
||||
-- The first directory that exists will be used. If none of the
|
||||
-- directories exist then (1) will be used if it is set, otherwise (2)
|
||||
-- will be used. Either way, a directory will be created if necessary.
|
||||
getXMonadDir :: MonadIO m => m String
|
||||
getXMonadDir =
|
||||
findFirstDirWithEnv "XMONAD_CONFIG_DIR"
|
||||
[ getAppUserDataDirectory "xmonad"
|
||||
, getXDGDirectory XDGConfig "xmonad"
|
||||
]
|
||||
-- * @cacheDir@: This directory is used to store temporary files that
|
||||
-- can easily be recreated such as the configuration binary and any
|
||||
-- intermediate object files generated by GHC.
|
||||
-- Also, the XPrompt history file goes here.
|
||||
--
|
||||
-- For how these directories are chosen, see 'getDirectories'.
|
||||
--
|
||||
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
|
||||
-- used to store temporary files that can easily be recreated. For
|
||||
-- example, the XPrompt history file.
|
||||
--
|
||||
-- Several directories are considered. In order of preference:
|
||||
--
|
||||
-- 1. The directory specified in the @XMONAD_CACHE_DIR@ environment variable.
|
||||
-- 2. The @~\/.xmonad@ directory.
|
||||
-- 3. The @XDG_CACHE_HOME/xmonad@ directory.
|
||||
--
|
||||
-- The first directory that exists will be used. If none of the
|
||||
-- directories exist then (1) will be used if it is set, otherwise (2)
|
||||
-- will be used. Either way, a directory will be created if necessary.
|
||||
getXMonadCacheDir :: MonadIO m => m String
|
||||
getXMonadCacheDir =
|
||||
findFirstDirWithEnv "XMONAD_CACHE_DIR"
|
||||
[ getAppUserDataDirectory "xmonad"
|
||||
, getXDGDirectory XDGCache "xmonad"
|
||||
]
|
||||
-- | Convenient type alias for the most common case in which one might
|
||||
-- want to use the 'Directories' type.
|
||||
type Directories = Directories' FilePath
|
||||
|
||||
-- | Return the path to the xmonad data directory. This directory is
|
||||
-- used by XMonad to store data files such as the run-time state file
|
||||
-- and the configuration binary generated by GHC.
|
||||
-- | Build up the 'Dirs' that xmonad will use. They are chosen as
|
||||
-- follows:
|
||||
--
|
||||
-- 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.
|
||||
-- 2. The @~\/.xmonad@ directory.
|
||||
-- 3. The @XDG_DATA_HOME/xmonad@ directory.
|
||||
-- The xmonad configuration file (or the build script, if present) is
|
||||
-- always assumed to be in @cfgDir@.
|
||||
--
|
||||
-- The first directory that exists will be used. If none of the
|
||||
-- directories exist then (1) will be used if it is set, otherwise (2)
|
||||
-- will be used. Either way, a directory will be created if necessary.
|
||||
getXMonadDataDir :: MonadIO m => m String
|
||||
getXMonadDataDir =
|
||||
findFirstDirWithEnv "XMONAD_DATA_DIR"
|
||||
[ getAppUserDataDirectory "xmonad"
|
||||
, getXDGDirectory XDGData "xmonad"
|
||||
]
|
||||
|
||||
-- | Helper function that will find the first existing directory and
|
||||
-- return its path. If none of the directories can be found, create
|
||||
-- and return the first from the list. If the list is empty this
|
||||
-- function returns the historical @~\/.xmonad@ directory.
|
||||
findFirstDirOf :: MonadIO m => [IO FilePath] -> m FilePath
|
||||
findFirstDirOf [] = findFirstDirOf [getAppUserDataDirectory "xmonad"]
|
||||
findFirstDirOf possibles = do
|
||||
found <- go possibles
|
||||
|
||||
case found of
|
||||
Just path -> return path
|
||||
Nothing -> do
|
||||
primary <- io (head possibles)
|
||||
io (createDirectoryIfMissing True primary)
|
||||
return primary
|
||||
|
||||
getDirectories :: IO Directories
|
||||
getDirectories = xmEnvDirs <|> xmDirs <|> xdgDirs
|
||||
where
|
||||
go [] = return Nothing
|
||||
go (x:xs) = do
|
||||
dir <- io x
|
||||
exists <- io (doesDirectoryExist dir)
|
||||
if exists then return (Just dir) else go xs
|
||||
-- | Check for xmonad's environment variables first
|
||||
xmEnvDirs :: IO Directories
|
||||
xmEnvDirs = do
|
||||
let xmEnvs = Directories{ dataDir = "XMONAD_DATA_DIR"
|
||||
, cfgDir = "XMONAD_CONFIG_DIR"
|
||||
, cacheDir = "XMONAD_CACHE_DIR"
|
||||
}
|
||||
maybe empty pure . sequenceA =<< traverse getEnv xmEnvs
|
||||
|
||||
-- | Simple wrapper around @findFirstDirOf@ that allows the primary
|
||||
-- path to be specified by an environment variable.
|
||||
findFirstDirWithEnv :: MonadIO m => String -> [IO FilePath] -> m FilePath
|
||||
findFirstDirWithEnv envName paths = do
|
||||
envPath' <- io (getEnv envName)
|
||||
-- | Check whether the config file or a build script is in the
|
||||
-- @~\/.xmonad@ directory
|
||||
xmDirs :: IO Directories
|
||||
xmDirs = do
|
||||
xmDir <- getAppUserDataDirectory "xmonad"
|
||||
conf <- doesFileExist $ xmDir </> "xmonad.hs"
|
||||
build <- doesFileExist $ xmDir </> "build"
|
||||
|
||||
case envPath' of
|
||||
Nothing -> findFirstDirOf paths
|
||||
Just envPath -> findFirstDirOf (return envPath:paths)
|
||||
-- Place *everything* in ~/.xmonad if yes
|
||||
guard $ conf || build
|
||||
pure Directories{ dataDir = xmDir, cfgDir = xmDir, cacheDir = xmDir }
|
||||
|
||||
-- | Helper function to retrieve the various XDG directories.
|
||||
-- This has been based on the implementation shipped with GHC version 8.0.1 or
|
||||
-- higher. Put here to preserve compatibility with older GHC versions.
|
||||
getXDGDirectory :: XDGDirectory -> FilePath -> IO FilePath
|
||||
getXDGDirectory xdgDir suffix =
|
||||
normalise . (</> suffix) <$>
|
||||
case xdgDir of
|
||||
XDGData -> get "XDG_DATA_HOME" ".local/share"
|
||||
XDGConfig -> get "XDG_CONFIG_HOME" ".config"
|
||||
XDGCache -> get "XDG_CACHE_HOME" ".cache"
|
||||
-- | Use XDG directories as a fallback
|
||||
xdgDirs :: IO Directories
|
||||
xdgDirs =
|
||||
for Directories{ dataDir = XdgData, cfgDir = XdgConfig, cacheDir = XdgCache }
|
||||
$ \dir -> do d <- getXdgDirectory dir "xmonad"
|
||||
d <$ createDirectoryIfMissing True d
|
||||
|
||||
-- | Return the path to the xmonad configuration directory.
|
||||
getXMonadDir :: X String
|
||||
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
|
||||
get name fallback = do
|
||||
env <- lookupEnv name
|
||||
case env of
|
||||
Nothing -> fallback'
|
||||
Just path
|
||||
| isRelative path -> fallback'
|
||||
| otherwise -> return path
|
||||
where
|
||||
fallback' = (</> fallback) <$> getHomeDirectory
|
||||
data XDGDirectory = XDGData | XDGConfig | XDGCache
|
||||
buildScript = buildScriptFileName dirs
|
||||
stackYaml = stackYamlFileName dirs
|
||||
|
||||
-- | Get the name of the file used to store the xmonad window state.
|
||||
stateFileName :: (Functor m, MonadIO m) => m FilePath
|
||||
stateFileName = (</> "xmonad.state") <$> getXMonadDataDir
|
||||
tryScript = do
|
||||
guard =<< doesFileExist buildScript
|
||||
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
|
||||
-- any of the following apply:
|
||||
tryStack = do
|
||||
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 $
|
||||
bracket (openFile (errFileName dirs) WriteMode) hClose $ \err -> do
|
||||
let run = runProc (cfgDir dirs) err
|
||||
case method of
|
||||
CompileGhc ->
|
||||
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 @lib@ directory (under the configuration directory).
|
||||
-- * the xmonad executable is older than @xmonad.hs@ or any file in
|
||||
-- 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,
|
||||
-- and any files in the aforementioned @lib@ directory.
|
||||
@@ -590,106 +733,21 @@ stateFileName = (</> "xmonad.state") <$> getXMonadDataDir
|
||||
--
|
||||
-- 'False' is returned if there are compilation errors.
|
||||
--
|
||||
recompile :: MonadIO m => Bool -> m Bool
|
||||
recompile force = io $ do
|
||||
cfgdir <- getXMonadDir
|
||||
datadir <- getXMonadDataDir
|
||||
let binn = "xmonad-"++arch++"-"++os
|
||||
bin = datadir </> binn
|
||||
err = datadir </> "xmonad.errors"
|
||||
src = cfgdir </> "xmonad.hs"
|
||||
lib = cfgdir </> "lib"
|
||||
buildscript = cfgdir </> "build"
|
||||
|
||||
libTs <- mapM getModTime . Prelude.filter isSource =<< allFiles lib
|
||||
srcT <- getModTime src
|
||||
binT <- getModTime bin
|
||||
|
||||
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
|
||||
recompile :: MonadIO m => Directories -> Bool -> m Bool
|
||||
recompile dirs force = io $ do
|
||||
method <- detectCompile dirs
|
||||
willCompile <- if force
|
||||
then True <$ trace "XMonad recompiling (forced)."
|
||||
else shouldCompile dirs method
|
||||
if willCompile
|
||||
then do
|
||||
-- temporarily disable SIGCHLD ignoring:
|
||||
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:
|
||||
status <- compile dirs method
|
||||
if status == ExitSuccess
|
||||
then trace "XMonad recompilation process exited with success!"
|
||||
else do
|
||||
ghcErr <- readFile err
|
||||
let msg = unlines $
|
||||
["Error detected while loading xmonad configuration file: " ++ src]
|
||||
++ lines (if null ghcErr then show status else ghcErr)
|
||||
++ ["","Please check the file for errors."]
|
||||
-- nb, the ordering of printing, then forking, is crucial due to
|
||||
-- lazy evaluation
|
||||
hPutStrLn stderr msg
|
||||
forkProcess $ executeFile "xmessage" True ["-default", "okay", replaceUnicode msg] Nothing
|
||||
return ()
|
||||
return (status == ExitSuccess)
|
||||
else return True
|
||||
where getModTime f = E.catch (Just <$> getModificationTime f) (\(SomeException _) -> return Nothing)
|
||||
isSource = flip elem [".hs",".lhs",".hsc"] . takeExtension
|
||||
isExecutable f = E.catch (executable <$> getPermissions f) (\(SomeException _) -> return False)
|
||||
allFiles t = do
|
||||
let prep = map (t</>) . Prelude.filter (`notElem` [".",".."])
|
||||
cs <- prep <$> E.catch (getDirectoryContents t) (\(SomeException _) -> return [])
|
||||
ds <- filterM doesDirectoryExist cs
|
||||
concat . ((cs \\ ds):) <$> mapM allFiles ds
|
||||
-- Replace some of the unicode symbols GHC uses in its output
|
||||
replaceUnicode = map $ \c -> case c of
|
||||
'\8226' -> '*' -- •
|
||||
'\8216' -> '`' -- ‘
|
||||
'\8217' -> '`' -- ’
|
||||
_ -> c
|
||||
compileGHC bin dir errHandle =
|
||||
runProcess "ghc" ["--make"
|
||||
, "xmonad.hs"
|
||||
, "-i"
|
||||
, "-ilib"
|
||||
, "-fforce-recomp"
|
||||
, "-main-is", "main"
|
||||
, "-v0"
|
||||
, "-o", bin
|
||||
] (Just dir) Nothing Nothing Nothing (Just errHandle)
|
||||
compileScript bin dir script errHandle =
|
||||
runProcess script [bin] (Just dir) Nothing Nothing Nothing (Just errHandle)
|
||||
then checkCompileWarnings dirs
|
||||
else compileFailed dirs status
|
||||
pure $ status == ExitSuccess
|
||||
else
|
||||
pure True
|
||||
|
||||
-- | Conditionally run an action, using a @Maybe a@ to decide.
|
||||
whenJust :: Monad m => Maybe a -> (a -> m ()) -> m ()
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances, DeriveDataTypeable #-}
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances, DeriveDataTypeable, LambdaCase, MultiWayIf #-}
|
||||
|
||||
-- --------------------------------------------------------------------------
|
||||
-- |
|
||||
@@ -8,7 +8,7 @@
|
||||
--
|
||||
-- Maintainer : spencerjanssen@gmail.com
|
||||
-- Stability : unstable
|
||||
-- Portability : not portable, Typeable deriving, mtl, posix
|
||||
-- Portability : not portable, mtl, posix
|
||||
--
|
||||
-- The collection of core layouts.
|
||||
--
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
module XMonad.Layout (
|
||||
Full(..), Tall(..), Mirror(..),
|
||||
Resize(..), IncMasterN(..), Choose, (|||), ChangeLayout(..),
|
||||
Resize(..), IncMasterN(..), Choose(..), (|||), CLR(..), ChangeLayout(..), JumpToLayout(..),
|
||||
mirrorRect, splitVertically,
|
||||
splitHorizontally, splitHorizontallyBy, splitVerticallyBy,
|
||||
|
||||
@@ -27,6 +27,7 @@ module XMonad.Layout (
|
||||
import XMonad.Core
|
||||
|
||||
import Graphics.X11 (Rectangle(..))
|
||||
import Graphics.X11.Xlib.Extras ( Event(DestroyWindowEvent) )
|
||||
import qualified XMonad.StackSet as W
|
||||
import Control.Arrow ((***), second)
|
||||
import Control.Monad
|
||||
@@ -35,10 +36,10 @@ import Data.Maybe (fromMaybe)
|
||||
------------------------------------------------------------------------
|
||||
|
||||
-- | 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.
|
||||
data IncMasterN = IncMasterN !Int deriving Typeable
|
||||
data IncMasterN = IncMasterN !Int
|
||||
|
||||
instance Message Resize
|
||||
instance Message IncMasterN
|
||||
@@ -77,7 +78,7 @@ instance LayoutClass Tall a where
|
||||
-- algorithm.
|
||||
--
|
||||
-- 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.
|
||||
tile
|
||||
:: Rational -- ^ @frac@, what proportion of the screen to devote to the master area
|
||||
@@ -131,23 +132,53 @@ mirrorRect (Rectangle rx ry rw rh) = Rectangle ry rx rh rw
|
||||
-- LayoutClass selection manager
|
||||
-- Layouts that transition between other layouts
|
||||
|
||||
-- | Messages to change the current layout.
|
||||
data ChangeLayout = FirstLayout | NextLayout deriving (Eq, Show, Typeable)
|
||||
-- | Messages to change the current layout. Also see 'JumpToLayout'.
|
||||
data ChangeLayout = FirstLayout | NextLayout deriving (Eq, Show)
|
||||
|
||||
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
|
||||
(|||) :: l a -> r a -> Choose l r a
|
||||
(|||) = Choose L
|
||||
(|||) = Choose CL
|
||||
infixr 5 |||
|
||||
|
||||
-- | A layout that allows users to switch between various layout options.
|
||||
data Choose l r a = Choose LR (l a) (r a) deriving (Read, Show)
|
||||
data Choose l r a = Choose CLR (l a) (r a) deriving (Read, Show)
|
||||
|
||||
-- | Are we on the left or right sub-layout?
|
||||
data LR = L | R deriving (Read, Show, Eq)
|
||||
-- | Choose the current sub-layout (left or right) in 'Choose'.
|
||||
data CLR = CL | CR deriving (Read, Show, Eq)
|
||||
|
||||
data NextNoWrap = NextNoWrap deriving (Eq, Show, Typeable)
|
||||
data NextNoWrap = NextNoWrap deriving (Eq, Show)
|
||||
instance Message NextNoWrap
|
||||
|
||||
-- | A small wrapper around handleMessage, as it is tedious to write
|
||||
@@ -159,26 +190,26 @@ handle l m = handleMessage l (SomeMessage m)
|
||||
-- new structure if any fields have changed, and performs any necessary cleanup
|
||||
-- on newly non-visible layouts.
|
||||
choose :: (LayoutClass l a, LayoutClass r a)
|
||||
=> Choose l r a-> LR -> Maybe (l a) -> Maybe (r a) -> X (Maybe (Choose l r a))
|
||||
=> Choose 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 l r) d' ml mr = f lr
|
||||
where
|
||||
(l', r') = (fromMaybe l ml, fromMaybe r mr)
|
||||
lr = case (d, d') of
|
||||
(L, R) -> (hide l' , return r')
|
||||
(R, L) -> (return l', hide r' )
|
||||
(_, _) -> (return l', return r')
|
||||
(CL, CR) -> (hide l' , return r')
|
||||
(CR, CL) -> (return l', hide r' )
|
||||
(_ , _ ) -> (return l', return r')
|
||||
f (x,y) = fmap Just $ liftM2 (Choose d') x y
|
||||
hide x = fmap (fromMaybe x) $ handle x Hide
|
||||
|
||||
instance (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a where
|
||||
runLayout (W.Workspace i (Choose L l r) ms) =
|
||||
fmap (second . fmap $ flip (Choose L) r) . runLayout (W.Workspace i l ms)
|
||||
runLayout (W.Workspace i (Choose R l r) ms) =
|
||||
fmap (second . fmap $ Choose R l) . runLayout (W.Workspace i r ms)
|
||||
runLayout (W.Workspace i (Choose CL l r) ms) =
|
||||
fmap (second . fmap $ flip (Choose CL) r) . runLayout (W.Workspace i l ms)
|
||||
runLayout (W.Workspace i (Choose CR l r) ms) =
|
||||
fmap (second . fmap $ Choose CR l) . runLayout (W.Workspace i r ms)
|
||||
|
||||
description (Choose L l _) = description l
|
||||
description (Choose R _ r) = description r
|
||||
description (Choose CL l _) = description l
|
||||
description (Choose CR _ r) = description r
|
||||
|
||||
handleMessage lr m | Just NextLayout <- fromMessage m = do
|
||||
mlr' <- handle lr NextNoWrap
|
||||
@@ -186,25 +217,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 =
|
||||
case d of
|
||||
L -> do
|
||||
CL -> do
|
||||
ml <- handle l NextNoWrap
|
||||
case ml of
|
||||
Just _ -> choose c L ml Nothing
|
||||
Nothing -> choose c R Nothing =<< handle r FirstLayout
|
||||
Just _ -> choose c CL ml Nothing
|
||||
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 =
|
||||
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 =
|
||||
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
|
||||
ml' <- case d of
|
||||
L -> handleMessage l m
|
||||
R -> return Nothing
|
||||
CL -> handleMessage l m
|
||||
CR -> return Nothing
|
||||
mr' <- case d of
|
||||
L -> return Nothing
|
||||
R -> handleMessage r m
|
||||
CL -> return Nothing
|
||||
CR -> handleMessage r m
|
||||
choose c d ml' mr'
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts #-}
|
||||
{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, NamedFieldPuns #-}
|
||||
----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Main
|
||||
@@ -16,7 +16,7 @@
|
||||
module XMonad.Main (xmonad, launch) where
|
||||
|
||||
import System.Locale.SetLocale
|
||||
import qualified Control.Exception.Extensible as E
|
||||
import qualified Control.Exception as E
|
||||
import Data.Bits
|
||||
import Data.List ((\\))
|
||||
import Data.Function
|
||||
@@ -24,7 +24,7 @@ import qualified Data.Map as M
|
||||
import qualified Data.Set as S
|
||||
import Control.Monad.Reader
|
||||
import Control.Monad.State
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Maybe (fromMaybe, isJust)
|
||||
import Data.Monoid (getAll)
|
||||
|
||||
import Graphics.X11.Xlib hiding (refreshKeyboardMapping)
|
||||
@@ -39,7 +39,7 @@ import XMonad.Operations
|
||||
import System.IO
|
||||
import System.Directory
|
||||
import System.Info
|
||||
import System.Environment
|
||||
import System.Environment (getArgs, getProgName, withArgs)
|
||||
import System.Posix.Process (executeFile)
|
||||
import System.Exit (exitFailure)
|
||||
import System.FilePath
|
||||
@@ -48,6 +48,7 @@ import Paths_xmonad (version)
|
||||
import Data.Version (showVersion)
|
||||
|
||||
import Graphics.X11.Xinerama (compiledWithXinerama)
|
||||
import Graphics.X11.Xrandr (xrrQueryExtension, xrrUpdateConfiguration)
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
@@ -59,17 +60,17 @@ xmonad :: (LayoutClass l Window, Read (l Window)) => XConfig l -> IO ()
|
||||
xmonad conf = do
|
||||
installSignalHandlers -- important to ignore SIGCHLD to avoid zombies
|
||||
|
||||
dirs <- getDirectories
|
||||
let launch' args = do
|
||||
catchIO buildLaunch
|
||||
conf' @ XConfig { layoutHook = Layout l }
|
||||
catchIO (buildLaunch dirs)
|
||||
conf'@XConfig { layoutHook = Layout l }
|
||||
<- handleExtraArgs conf args conf{ layoutHook = Layout (layoutHook conf) }
|
||||
withArgs [] $ launch (conf' { layoutHook = l })
|
||||
withArgs [] $ launch (conf' { layoutHook = l }) dirs
|
||||
|
||||
args <- getArgs
|
||||
case args of
|
||||
("--resume": ws : xs : args') -> migrateState ws xs >> launch' args'
|
||||
["--help"] -> usage
|
||||
["--recompile"] -> recompile True >>= flip unless exitFailure
|
||||
["--recompile"] -> recompile dirs True >>= flip unless exitFailure
|
||||
["--restart"] -> sendRestart
|
||||
["--version"] -> putStrLn $ unwords shortVersion
|
||||
["--verbose-version"] -> putStrLn . unwords $ shortVersion ++ longVersion
|
||||
@@ -90,7 +91,7 @@ usage = do
|
||||
"Options:" :
|
||||
" --help Print this message" :
|
||||
" --version Print the version number" :
|
||||
" --recompile Recompile your ~/.xmonad/xmonad.hs" :
|
||||
" --recompile Recompile your xmonad.hs" :
|
||||
" --replace Replace the running window manager with xmonad" :
|
||||
" --restart Request a running xmonad process to restart" :
|
||||
[]
|
||||
@@ -111,21 +112,21 @@ usage = do
|
||||
--
|
||||
-- * Missing XMonad\/XMonadContrib modules due to ghc upgrade
|
||||
--
|
||||
buildLaunch :: IO ()
|
||||
buildLaunch = do
|
||||
buildLaunch :: Directories -> IO ()
|
||||
buildLaunch dirs = do
|
||||
whoami <- getProgName
|
||||
let compiledConfig = "xmonad-"++arch++"-"++os
|
||||
let bin = binFileName dirs
|
||||
let compiledConfig = takeFileName bin
|
||||
unless (whoami == compiledConfig) $ do
|
||||
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
|
||||
, " but the compiled configuration should be called "
|
||||
, show compiledConfig
|
||||
]
|
||||
recompile False
|
||||
dir <- getXMonadDataDir
|
||||
recompile dirs False
|
||||
args <- getArgs
|
||||
executeFile (dir </> compiledConfig) False args Nothing
|
||||
executeFile bin False args Nothing
|
||||
|
||||
sendRestart :: IO ()
|
||||
sendRestart = do
|
||||
@@ -134,7 +135,7 @@ sendRestart = do
|
||||
xmonad_restart <- internAtom dpy "XMONAD_RESTART" False
|
||||
allocaXEvent $ \e -> do
|
||||
setEventType e clientMessage
|
||||
setClientMessageEvent e rw xmonad_restart 32 0 currentTime
|
||||
setClientMessageEvent' e rw xmonad_restart 32 []
|
||||
sendEvent dpy rw False structureNotifyMask e
|
||||
sync dpy False
|
||||
|
||||
@@ -166,8 +167,8 @@ sendReplace = do
|
||||
-- function instead of 'xmonad'. You probably also want to have a key
|
||||
-- binding to the 'XMonad.Operations.restart` function that restarts
|
||||
-- your custom binary with the resume flag set to @True@.
|
||||
launch :: (LayoutClass l Window, Read (l Window)) => XConfig l -> IO ()
|
||||
launch initxmc = do
|
||||
launch :: (LayoutClass l Window, Read (l Window)) => XConfig l -> Directories -> IO ()
|
||||
launch initxmc drs = do
|
||||
-- setup locale information from environment
|
||||
setLocale LC_ALL (Just "")
|
||||
-- ignore SIGPIPE and SIGCHLD
|
||||
@@ -216,7 +217,9 @@ launch initxmc = do
|
||||
, buttonActions = mouseBindings xmc xmc
|
||||
, mouseFocused = False
|
||||
, mousePosition = Nothing
|
||||
, currentEvent = Nothing }
|
||||
, currentEvent = Nothing
|
||||
, directories = drs
|
||||
}
|
||||
|
||||
st = XState
|
||||
{ windowset = initialWinset
|
||||
@@ -231,7 +234,7 @@ launch initxmc = do
|
||||
runX cf st $ do
|
||||
-- check for serialized state in a file.
|
||||
serializedSt <- do
|
||||
path <- stateFileName
|
||||
path <- asks $ stateFileName . directories
|
||||
exists <- io (doesFileExist path)
|
||||
if exists then readStateFile initxmc else return Nothing
|
||||
|
||||
@@ -260,8 +263,11 @@ launch initxmc = do
|
||||
|
||||
userCode $ startupHook initxmc
|
||||
|
||||
rrData <- io $ xrrQueryExtension dpy
|
||||
let rrUpdate = when (isJust rrData) . void . xrrUpdateConfiguration
|
||||
|
||||
-- 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)
|
||||
|
||||
return ()
|
||||
where
|
||||
@@ -310,10 +316,15 @@ handle (MapRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
|
||||
|
||||
-- window destroyed, 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
|
||||
modify (\s -> s { mapped = S.delete w (mapped 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
|
||||
-- it is synthetic or we are not expecting an unmap notification from a window.
|
||||
@@ -472,8 +483,11 @@ grabKeys = do
|
||||
-- build a map from keysyms to lists of keysyms (doing what
|
||||
-- XGetKeyboardMapping would do if the X11 package bound it)
|
||||
syms <- forM allCodes $ \code -> io (keycodeToKeysym dpy code 0)
|
||||
let keysymMap = M.fromListWith (++) (zip syms [[code] | code <- allCodes])
|
||||
keysymToKeycodes sym = M.findWithDefault [] sym keysymMap
|
||||
let keysymMap' = M.fromListWith (++) (zip syms [[code] | code <- allCodes])
|
||||
-- keycodeToKeysym returns noSymbol for all unbound keycodes, and we don't
|
||||
-- want to grab those whenever someone accidentally uses def :: KeySym
|
||||
let keysymMap = M.delete noSymbol keysymMap'
|
||||
let keysymToKeycodes sym = M.findWithDefault [] sym keysymMap
|
||||
forM_ (M.keys ks) $ \(mask,sym) ->
|
||||
forM_ (keysymToKeycodes sym) $ \kc ->
|
||||
mapM_ (grab kc . (mask .|.)) =<< extraModifiers
|
||||
|
@@ -21,8 +21,8 @@ module XMonad.ManageHook where
|
||||
import XMonad.Core
|
||||
import Graphics.X11.Xlib.Extras
|
||||
import Graphics.X11.Xlib (Display, Window, internAtom, wM_NAME)
|
||||
import Control.Exception.Extensible (bracket, SomeException(..))
|
||||
import qualified Control.Exception.Extensible as E
|
||||
import Control.Exception (bracket, SomeException(..))
|
||||
import qualified Control.Exception as E
|
||||
import Control.Monad.Reader
|
||||
import Data.Maybe
|
||||
import Data.Monoid
|
||||
@@ -61,11 +61,15 @@ infixr 3 <&&>, <||>
|
||||
|
||||
-- | '&&' lifted to a 'Monad'.
|
||||
(<&&>) :: Monad m => m Bool -> m Bool -> m Bool
|
||||
(<&&>) = liftM2 (&&)
|
||||
(<&&>) x y = ifM x y (pure False)
|
||||
|
||||
-- | '||' lifted to a 'Monad'.
|
||||
(<||>) :: 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.
|
||||
title :: Query String
|
||||
|
@@ -1,5 +1,4 @@
|
||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||
{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
|
||||
{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, PatternGuards #-}
|
||||
-- --------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Operations
|
||||
@@ -8,31 +7,66 @@
|
||||
--
|
||||
-- Maintainer : dons@cse.unsw.edu.au
|
||||
-- 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,
|
||||
|
||||
-- * Manage Windows
|
||||
windows, refresh, rescreen, modifyWindowSet, windowBracket, windowBracket_, clearEvents, getCleanedScreenInfo,
|
||||
withFocused, withUnfocused,
|
||||
|
||||
-- * Keyboard and Mouse
|
||||
cleanMask, extraModifiers,
|
||||
mouseDrag, mouseMoveWindow, mouseResizeWindow,
|
||||
setButtonGrab, setFocusX,
|
||||
|
||||
-- * 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.Layout (Full(..))
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import Data.Maybe
|
||||
import Data.Monoid (Endo(..))
|
||||
import Data.Monoid (Endo(..),Any(..))
|
||||
import Data.List (nub, (\\), find)
|
||||
import Data.Bits ((.|.), (.&.), complement, testBit)
|
||||
import Data.Function (on)
|
||||
import Data.Ratio
|
||||
import qualified Data.Map as M
|
||||
import qualified Data.Set as S
|
||||
|
||||
import Control.Applicative((<$>), (<*>))
|
||||
import Control.Arrow (second)
|
||||
import Control.Monad.Reader
|
||||
import Control.Monad.State
|
||||
import qualified Control.Exception.Extensible as C
|
||||
import qualified Control.Exception as C
|
||||
|
||||
import System.IO
|
||||
import System.Directory
|
||||
@@ -42,9 +76,10 @@ import Graphics.X11.Xinerama (getScreenInfo)
|
||||
import Graphics.X11.Xlib.Extras
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- |
|
||||
-- Window manager operations
|
||||
-- manage. Add a new window to be managed in the current workspace.
|
||||
|
||||
-- |
|
||||
-- Add a new window to be managed in the current workspace.
|
||||
-- Bring it into focus.
|
||||
--
|
||||
-- Whether the window is already managed, or not, it is mapped, has its
|
||||
@@ -71,7 +106,7 @@ manage w = whenX (not <$> isClient w) $ withDisplay $ \d -> do
|
||||
g <- appEndo <$> userCodeDef (Endo id) (runQuery mh w)
|
||||
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.
|
||||
--
|
||||
unmanage :: Window -> X ()
|
||||
@@ -91,7 +126,7 @@ killWindow w = withDisplay $ \d -> do
|
||||
io $ if wmdelt `elem` protocols
|
||||
then allocaXEvent $ \ev -> do
|
||||
setEventType ev clientMessage
|
||||
setClientMessageEvent ev w wmprot 32 wmdelt 0
|
||||
setClientMessageEvent ev w wmprot 32 wmdelt currentTime
|
||||
sendEvent d w False noEventMask ev
|
||||
else killClient d w >> return ()
|
||||
|
||||
@@ -102,7 +137,7 @@ kill = withFocused killWindow
|
||||
-- ---------------------------------------------------------------------
|
||||
-- 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 f = do
|
||||
XState { windowset = old } <- get
|
||||
@@ -176,20 +211,41 @@ windows f = do
|
||||
unless isMouseFocused $ clearEvents enterWindowMask
|
||||
asks (logHook . config) >>= userCodeDef ()
|
||||
|
||||
-- | Modify the @WindowSet@ in state with no special handling.
|
||||
modifyWindowSet :: (WindowSet -> WindowSet) -> X ()
|
||||
modifyWindowSet f = modify $ \xst -> xst { windowset = f (windowset xst) }
|
||||
|
||||
-- | Perform an @X@ action and check its return value against a predicate p.
|
||||
-- If p holds, unwind changes to the @WindowSet@ and replay them using @windows@.
|
||||
windowBracket :: (a -> Bool) -> X a -> X a
|
||||
windowBracket p action = withWindowSet $ \old -> do
|
||||
a <- action
|
||||
when (p a) . withWindowSet $ \new -> do
|
||||
modifyWindowSet $ \_ -> old
|
||||
windows $ \_ -> new
|
||||
return a
|
||||
|
||||
-- | 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.
|
||||
scaleRationalRect :: Rectangle -> W.RationalRect -> Rectangle
|
||||
scaleRationalRect (Rectangle sx sy sw sh) (W.RationalRect rx ry rw rh)
|
||||
= Rectangle (sx + scale sw rx) (sy + scale sh ry) (scale sw rw) (scale sh rh)
|
||||
where scale s r = floor (toRational s * r)
|
||||
|
||||
-- | setWMState. set the WM_STATE property
|
||||
-- | Set a window's WM_STATE property.
|
||||
setWMState :: Window -> Int -> X ()
|
||||
setWMState w v = withDisplay $ \dpy -> do
|
||||
a <- atom_WM_STATE
|
||||
io $ changeProperty32 dpy w a a propModeReplace [fromIntegral v, fromIntegral none]
|
||||
|
||||
-- | Set the border color using the window's color map, if possible,
|
||||
-- otherwise fallback to the color in @Pixel@.
|
||||
-- | Set the border color using the window's color map, if possible;
|
||||
-- otherwise fall back to the color in @Pixel@.
|
||||
setWindowBorderWithFallback :: Display -> Window -> String -> Pixel -> X ()
|
||||
setWindowBorderWithFallback dpy w color basic = io $
|
||||
C.handle fallback $ do
|
||||
@@ -201,7 +257,7 @@ setWindowBorderWithFallback dpy w color basic = io $
|
||||
fallback e = do hPrint stderr e >> hFlush stderr
|
||||
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 w = whenX (gets (S.member w . mapped)) $ withDisplay $ \d -> do
|
||||
cMask <- asks $ clientMask . config
|
||||
@@ -214,15 +270,15 @@ hide w = whenX (gets (S.member w . mapped)) $ withDisplay $ \d -> do
|
||||
modify (\s -> s { waitingUnmap = M.insertWith (+) w 1 (waitingUnmap s)
|
||||
, mapped = S.delete w (mapped s) })
|
||||
|
||||
-- | reveal. Show a window by mapping it and setting Normal
|
||||
-- this is harmless if the window was already visible
|
||||
-- | Show a window by mapping it and setting Normal.
|
||||
-- This is harmless if the window was already visible.
|
||||
reveal :: Window -> X ()
|
||||
reveal w = withDisplay $ \d -> do
|
||||
setWMState w normalState
|
||||
io $ mapWindow d w
|
||||
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 w = asks normalBorder >>= \nb -> withDisplay $ \d -> do
|
||||
setWMState w iconicState
|
||||
@@ -233,7 +289,7 @@ setInitialProperties w = asks normalBorder >>= \nb -> withDisplay $ \d -> do
|
||||
-- required by the border setting in 'windows'
|
||||
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.
|
||||
--
|
||||
-- This is our 'view' operation (MVC), in that it pretty prints our model
|
||||
@@ -242,7 +298,7 @@ setInitialProperties w = asks normalBorder >>= \nb -> withDisplay $ \d -> do
|
||||
refresh :: X ()
|
||||
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 mask = withDisplay $ \d -> io $ do
|
||||
sync d False
|
||||
@@ -250,8 +306,8 @@ clearEvents mask = withDisplay $ \d -> io $ do
|
||||
more <- checkMaskEvent d mask p
|
||||
when more again -- beautiful
|
||||
|
||||
-- | tileWindow. Moves and resizes w such that it fits inside the given
|
||||
-- rectangle, including its border.
|
||||
-- | Move and resize @w@ such that it fits inside the given rectangle,
|
||||
-- including its border.
|
||||
tileWindow :: Window -> Rectangle -> X ()
|
||||
tileWindow w r = withDisplay $ \d -> withWindowAttributes d w $ \wa -> do
|
||||
-- give all windows at least 1x1 pixels
|
||||
@@ -278,13 +334,13 @@ containedIn r1@(Rectangle x1 y1 w1 h1) r2@(Rectangle x2 y2 w2 h2)
|
||||
nubScreens :: [Rectangle] -> [Rectangle]
|
||||
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.
|
||||
getCleanedScreenInfo :: MonadIO m => Display -> m [Rectangle]
|
||||
getCleanedScreenInfo = io . fmap nubScreens . getScreenInfo
|
||||
|
||||
-- | rescreen. The screen configuration may have changed (due to
|
||||
-- xrandr), update the state and refresh the screen, and reset the gap.
|
||||
-- | The screen configuration may have changed (due to -- xrandr),
|
||||
-- update the state and refresh the screen, and reset the gap.
|
||||
rescreen :: X ()
|
||||
rescreen = do
|
||||
xinesc <- withDisplay getCleanedScreenInfo
|
||||
@@ -298,7 +354,7 @@ rescreen = do
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
|
||||
-- | 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 grab w = do
|
||||
pointerMode <- asks $ \c -> if clickJustFocuses (config c)
|
||||
@@ -371,36 +427,42 @@ setFocusX w = withWindowSet $ \ws -> do
|
||||
-- Message handling
|
||||
|
||||
-- | Throw a message to the current 'LayoutClass' possibly modifying how we
|
||||
-- layout the windows, then refresh.
|
||||
-- layout the windows, in which case changes are handled through a refresh.
|
||||
sendMessage :: Message a => a -> X ()
|
||||
sendMessage a = do
|
||||
sendMessage a = windowBracket_ $ do
|
||||
w <- W.workspace . W.current <$> gets windowset
|
||||
ml' <- handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing
|
||||
whenJust ml' $ \l' ->
|
||||
windows $ \ws -> ws { W.current = (W.current ws)
|
||||
modifyWindowSet $ \ws -> ws { W.current = (W.current ws)
|
||||
{ W.workspace = (W.workspace $ W.current ws)
|
||||
{ W.layout = l' }}}
|
||||
return (Any $ isJust ml')
|
||||
|
||||
-- | Send a message to all layouts, without refreshing.
|
||||
broadcastMessage :: Message a => a -> X ()
|
||||
broadcastMessage a = withWindowSet $ \ws -> do
|
||||
let c = W.workspace . W.current $ ws
|
||||
v = map W.workspace . W.visible $ ws
|
||||
h = W.hidden ws
|
||||
mapM_ (sendMessageWithNoRefresh a) (c : v ++ h)
|
||||
-- this is O(n²), but we can't really fix this as there's code in
|
||||
-- xmonad-contrib that touches the windowset during handleMessage
|
||||
-- (returning Nothing for changes to not get overwritten), so we
|
||||
-- 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.
|
||||
sendMessageWithNoRefresh :: Message a => a -> W.Workspace WorkspaceId (Layout Window) Window -> X ()
|
||||
sendMessageWithNoRefresh :: Message a => a -> WindowSpace -> X ()
|
||||
sendMessageWithNoRefresh a w =
|
||||
handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing >>=
|
||||
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 i ml = whenJust ml $ \l ->
|
||||
runOnWorkspaces $ \ww -> return $ if W.tag ww == i then ww { W.layout = l} else ww
|
||||
|
||||
-- | Set the layout of the currently viewed workspace
|
||||
-- | Set the layout of the currently viewed workspace.
|
||||
setLayout :: Layout Window -> X ()
|
||||
setLayout l = do
|
||||
ss@(W.StackSet { W.current = c@(W.Screen { W.workspace = ws })}) <- gets windowset
|
||||
@@ -410,7 +472,7 @@ setLayout l = do
|
||||
------------------------------------------------------------------------
|
||||
-- Utilities
|
||||
|
||||
-- | Return workspace visible on screen 'sc', or 'Nothing'.
|
||||
-- | Return workspace visible on screen @sc@, or 'Nothing'.
|
||||
screenWorkspace :: ScreenId -> X (Maybe WorkspaceId)
|
||||
screenWorkspace sc = withWindowSet $ return . W.lookupWorkspace sc
|
||||
|
||||
@@ -418,7 +480,14 @@ screenWorkspace sc = withWindowSet $ return . W.lookupWorkspace sc
|
||||
withFocused :: (Window -> X ()) -> X ()
|
||||
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 w = withWindowSet $ return . W.member w
|
||||
|
||||
@@ -429,13 +498,13 @@ extraModifiers = do
|
||||
nlm <- gets numberlockMask
|
||||
return [0, nlm, lockMask, nlm .|. lockMask ]
|
||||
|
||||
-- | Strip numlock\/capslock from a mask
|
||||
-- | Strip numlock\/capslock from a mask.
|
||||
cleanMask :: KeyMask -> X KeyMask
|
||||
cleanMask km = do
|
||||
nlm <- gets numberlockMask
|
||||
return (complement (nlm .|. lockMask) .&. km)
|
||||
|
||||
-- | Get the 'Pixel' value for a named color
|
||||
-- | Get the 'Pixel' value for a named color.
|
||||
initColor :: Display -> String -> IO (Maybe Pixel)
|
||||
initColor dpy c = C.handle (\(C.SomeException _) -> return Nothing) $
|
||||
(Just . color_pixel . fst) <$> allocNamedColor dpy colormap c
|
||||
@@ -460,7 +529,7 @@ writeStateToFile = do
|
||||
wsData = W.mapLayout show . windowset
|
||||
extState = catMaybes . map maybeShow . M.toList . extensibleState
|
||||
|
||||
path <- stateFileName
|
||||
path <- asks $ stateFileName . directories
|
||||
stateData <- gets (\s -> StateFile (wsData s) (extState s))
|
||||
catchIO (writeFile path $ show stateData)
|
||||
|
||||
@@ -468,7 +537,7 @@ writeStateToFile = do
|
||||
-- return that state. The state file is removed after reading it.
|
||||
readStateFile :: (LayoutClass l Window, Read (l Window)) => XConfig l -> X (Maybe XState)
|
||||
readStateFile xmc = do
|
||||
path <- stateFileName
|
||||
path <- asks $ stateFileName . directories
|
||||
|
||||
-- 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.
|
||||
@@ -501,23 +570,8 @@ readStateFile xmc = do
|
||||
readStrict :: Handle -> IO String
|
||||
readStrict h = hGetContents h >>= \s -> length s `seq` return s
|
||||
|
||||
-- | Migrate state from a previously running xmonad instance that used
|
||||
-- the older @--resume@ technique.
|
||||
{-# DEPRECATED migrateState "will be removed some point in the future." #-}
|
||||
migrateState :: (Functor m, MonadIO m) => String -> String -> m ()
|
||||
migrateState ws xs = do
|
||||
io (putStrLn "WARNING: --resume is no longer supported.")
|
||||
whenJust stateData $ \s -> do
|
||||
path <- stateFileName
|
||||
catchIO (writeFile path $ show s)
|
||||
where
|
||||
stateData = StateFile <$> maybeRead ws <*> maybeRead xs
|
||||
maybeRead s = case reads s of
|
||||
[(x, "")] -> Just x
|
||||
_ -> Nothing
|
||||
|
||||
-- | @restart name resume@. Attempt to restart xmonad by executing the program
|
||||
-- @name@. If @resume@ is 'True', restart with the current window state.
|
||||
-- | @restart name resume@ attempts to restart xmonad by executing the program
|
||||
-- @name@. If @resume@ is 'True', restart with the current window state.
|
||||
-- When executing another window manager, @resume@ should be 'False'.
|
||||
restart :: String -> Bool -> X ()
|
||||
restart prog resume = do
|
||||
@@ -527,10 +581,10 @@ restart prog resume = do
|
||||
catchIO (executeFile prog True [] Nothing)
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- | Floating layer support
|
||||
-- Floating layer support
|
||||
|
||||
-- | 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 w =
|
||||
catchX go $ do
|
||||
@@ -544,13 +598,25 @@ floatLocation w =
|
||||
ws <- gets windowset
|
||||
wa <- io $ getWindowAttributes d w
|
||||
let bw = (fromIntegral . wa_border_width) wa
|
||||
sc <- fromMaybe (W.current ws) <$> pointScreen (fi $ wa_x wa) (fi $ wa_y wa)
|
||||
point_sc <- pointScreen (fi $ wa_x wa) (fi $ wa_y wa)
|
||||
managed <- isClient w
|
||||
|
||||
let sr = screenRect . W.screenDetail $ sc
|
||||
rr = W.RationalRect ((fi (wa_x wa) - fi (rect_x sr)) % fi (rect_width sr))
|
||||
((fi (wa_y wa) - fi (rect_y sr)) % fi (rect_height sr))
|
||||
(fi (wa_width wa + bw*2) % fi (rect_width sr))
|
||||
(fi (wa_height wa + bw*2) % fi (rect_height sr))
|
||||
-- ignore pointScreen for new windows unless it's the current
|
||||
-- screen, otherwise the float's relative size is computed against
|
||||
-- a different screen and the float ends up with the wrong size
|
||||
let sr_eq = (==) `on` fmap (screenRect . W.screenDetail)
|
||||
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)
|
||||
|
||||
@@ -602,10 +668,9 @@ mouseDrag f done = do
|
||||
clearEvents pointerMotionMask
|
||||
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 w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
io $ raiseWindow d w
|
||||
wa <- io $ getWindowAttributes d w
|
||||
(_, _, _, ox', oy', _, _, _) <- io $ queryPointer d w
|
||||
let ox = fromIntegral ox'
|
||||
@@ -617,10 +682,9 @@ mouseMoveWindow w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
)
|
||||
(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 w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
io $ raiseWindow d w
|
||||
wa <- io $ getWindowAttributes d w
|
||||
sh <- io $ getWMNormalHints d w
|
||||
io $ warpPointer d none w 0 0 0 0 (fromIntegral (wa_width wa)) (fromIntegral (wa_height wa))
|
||||
@@ -633,8 +697,9 @@ mouseResizeWindow w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
(float w)
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- | Support for window size hints
|
||||
-- Support for window size hints
|
||||
|
||||
-- | An alias for a (width, height) pair
|
||||
type D = (Dimension, Dimension)
|
||||
|
||||
-- | Given a window, build an adjuster function that will reduce the given
|
||||
@@ -662,7 +727,7 @@ applySizeHintsContents :: Integral a => SizeHints -> (a, a) -> D
|
||||
applySizeHintsContents sh (w, 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' sh =
|
||||
maybe id applyMaxSizeHint (sh_max_size sh)
|
||||
|
@@ -1,4 +1,5 @@
|
||||
{-# LANGUAGE PatternGuards #-}
|
||||
{-# LANGUAGE DeriveFunctor #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
@@ -52,6 +53,8 @@ module XMonad.StackSet (
|
||||
) where
|
||||
|
||||
import Prelude hiding (filter)
|
||||
import Control.Applicative.Backwards (Backwards (Backwards, forwards))
|
||||
import Data.Foldable (foldr, toList)
|
||||
import Data.Maybe (listToMaybe,isJust,fromMaybe)
|
||||
import qualified Data.List as L (deleteBy,find,splitAt,filter,nub)
|
||||
import Data.List ( (\\) )
|
||||
@@ -85,7 +88,7 @@ import qualified Data.Map as M (Map,insert,delete,empty)
|
||||
-- continuation reified as a data structure.
|
||||
--
|
||||
-- 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
|
||||
-- the old structure as possible.
|
||||
--
|
||||
@@ -151,7 +154,7 @@ data Workspace i l a = Workspace { tag :: !i, layout :: l, stack :: Maybe (Stac
|
||||
deriving (Show, Read, Eq)
|
||||
|
||||
-- | A structure for window geometries
|
||||
data RationalRect = RationalRect Rational Rational Rational Rational
|
||||
data RationalRect = RationalRect !Rational !Rational !Rational !Rational
|
||||
deriving (Show, Read, Eq)
|
||||
|
||||
-- |
|
||||
@@ -175,8 +178,19 @@ data RationalRect = RationalRect Rational Rational Rational Rational
|
||||
data Stack a = Stack { focus :: !a -- focused thing in this set
|
||||
, up :: [a] -- clowns to the left
|
||||
, 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
|
||||
abort :: String -> a
|
||||
|
21
stack.yaml
21
stack.yaml
@@ -1,7 +1,22 @@
|
||||
resolver: lts-7.19
|
||||
resolver: lts-16.22
|
||||
|
||||
packages:
|
||||
- ./
|
||||
- ./
|
||||
|
||||
extra-deps:
|
||||
- X11-1.8
|
||||
- X11-1.10
|
||||
|
||||
flags:
|
||||
xmonad:
|
||||
# stack doesn't support automatic flags
|
||||
# https://cabal.readthedocs.io/en/latest/cabal-package.html#resolution-of-conditions-and-flags
|
||||
# https://github.com/commercialhaskell/stack/issues/1313#issuecomment-157259270
|
||||
quickcheck-classes: false
|
||||
|
||||
nix:
|
||||
packages:
|
||||
- zlib
|
||||
- xorg.libX11
|
||||
- xorg.libXrandr
|
||||
- xorg.libXScrnSaver
|
||||
- xorg.libXext
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import Test.QuickCheck
|
||||
|
||||
-- Our QC instances and properties.
|
||||
-- Our QC instances and properties:
|
||||
import Instances
|
||||
import Properties.Delete
|
||||
import Properties.Failure
|
||||
@@ -196,6 +196,5 @@ tests =
|
||||
,("pointWithin", property prop_point_within)
|
||||
,("pointWithin mirror", property prop_point_within_mirror)
|
||||
|
||||
]
|
||||
|
||||
|
||||
] <>
|
||||
prop_laws_Stack
|
||||
|
@@ -18,7 +18,7 @@ prop_delete x =
|
||||
where _ = x :: T
|
||||
|
||||
-- delete is reversible with 'insert'.
|
||||
-- It is the identiy, except for the 'master', which is reset on insert and delete.
|
||||
-- It is the identity, except for the 'master', which is reset on insert and delete.
|
||||
--
|
||||
prop_delete_insert (x :: T) =
|
||||
case peek x of
|
||||
|
@@ -2,7 +2,7 @@ module Properties.Failure where
|
||||
|
||||
import XMonad.StackSet hiding (filter)
|
||||
|
||||
import qualified Control.Exception.Extensible as C
|
||||
import qualified Control.Exception as C
|
||||
import System.IO.Unsafe
|
||||
import Data.List (isPrefixOf)
|
||||
|
||||
|
@@ -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
|
||||
-- the desired range
|
||||
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
|
||||
in and [ noOverflows (*) x (y+a), noOverflows (*) (x+b) y ]
|
||||
==> f (x,y) == (x,y)
|
||||
|
||||
where pos = choose (0, 65535)
|
||||
mul a b = toInteger (a*b) /= toInteger a * toInteger b
|
||||
|
||||
prop_point_within r @ (Rectangle x y w h) =
|
||||
prop_point_within r@(Rectangle x y w h) =
|
||||
forAll ((,) <$>
|
||||
choose (0, fromIntegral w - 1) <*>
|
||||
choose (0, fromIntegral h - 1)) $
|
||||
|
@@ -1,3 +1,5 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
module Properties.Stack where
|
||||
|
||||
@@ -9,6 +11,14 @@ import qualified XMonad.StackSet as S (filter)
|
||||
|
||||
import Data.Maybe
|
||||
|
||||
#ifdef VERSION_quickcheck_classes
|
||||
import Data.Proxy
|
||||
import Test.QuickCheck.Classes (
|
||||
Laws (lawsTypeclass, lawsProperties), Proxy1 (Proxy1),
|
||||
foldableLaws, traversableLaws,
|
||||
)
|
||||
#endif
|
||||
|
||||
|
||||
-- The list returned by index should be the same length as the actual
|
||||
-- windows kept in the zipper
|
||||
@@ -49,3 +59,25 @@ prop_differentiate xs =
|
||||
if null xs then differentiate xs == Nothing
|
||||
else (differentiate xs) == Just (Stack (head xs) [] (tail xs))
|
||||
where _ = xs :: [Int]
|
||||
|
||||
|
||||
#ifdef VERSION_quickcheck_classes
|
||||
-- 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 ]
|
||||
#else
|
||||
prop_laws_Stack = []
|
||||
#endif
|
||||
|
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
|
||||
-- of known key-bindings is inserted automatically from "Config.hs". That
|
||||
-- document is then rendered with Pandoc as "man/xmonad.1" and
|
||||
-- "man/xmonad.1.html".
|
||||
-- Reads markdown (man/xmonad.1.markdown) from stdin, subtitutes
|
||||
-- ___KEYBINDINGS___ for key-binding definitions generated from
|
||||
-- src/XMonad/Config.hs, prints result to stdout.
|
||||
--
|
||||
-- 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.List
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.IO as TIO
|
||||
import Text.Pandoc
|
||||
import Text.Regex.Posix
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
keybindings <- guessBindings
|
||||
|
||||
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"
|
||||
interact $ unlines . replace "___KEYBINDINGS___" keybindings . lines
|
||||
|
||||
-- | The format for the docstrings in "Config.hs" takes the following form:
|
||||
--
|
||||
@@ -49,7 +22,7 @@ main = do
|
||||
-- -- 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
|
||||
-- from the rest of the line. For example:
|
||||
--
|
||||
@@ -61,25 +34,33 @@ main = do
|
||||
|
||||
guessBindings :: IO String
|
||||
guessBindings = do
|
||||
buf <- readFile "./src/XMonad/Config.hs"
|
||||
return (intercalate "\n\n" (map markdownDefn (allBindings buf)))
|
||||
buf <- readFile "./src/XMonad/Config.hs"
|
||||
return (intercalate "\n\n" (map markdownDefn (allBindings buf)))
|
||||
|
||||
allBindings :: String -> [(String, String)]
|
||||
allBindings xs = map (binding . map trim) (xs =~ "(.*)--(.*)%!(.*)")
|
||||
|
||||
binding :: [String] -> (String, String)
|
||||
binding [ _, bindingLine, "", desc ] = (guessKeys bindingLine, desc)
|
||||
binding [ _, _, keyCombo, desc ] = (keyCombo, desc)
|
||||
binding x = error ("binding: called with unexpected argument " ++ show x)
|
||||
|
||||
guessKeys :: String -> String
|
||||
guessKeys line =
|
||||
case keys of
|
||||
[key] -> concat $ intersperse "-" (modifiers ++ [map toLower key])
|
||||
_ -> error ("guessKeys: unexpected number of keys " ++ show keys)
|
||||
allBindings = concatMap parseLine . lines
|
||||
where
|
||||
modifiers = map (!!1) (line =~ "(mod|shift|control)Mask")
|
||||
(_, _, _, keys) = line =~ "xK_([_[:alnum:]]+)" :: (String, String, String, [String])
|
||||
parseLine :: 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?
|
||||
markdownDefn :: (String, String) -> String
|
||||
|
78
xmonad.cabal
78
xmonad.cabal
@@ -1,5 +1,5 @@
|
||||
name: xmonad
|
||||
version: 0.14.2
|
||||
version: 0.17.0
|
||||
synopsis: A tiling window manager
|
||||
description: xmonad is a tiling window manager for X. Windows are arranged
|
||||
automatically to tile the screen without gaps or overlap, maximising
|
||||
@@ -27,39 +27,34 @@ author: Spencer Janssen, Don Stewart, Adam Vogt, David Roundy, Jason
|
||||
Ondřej Súkup, Paul Hebble, Shachaf Ben-Kiki, Siim Põder, Tim McIver,
|
||||
Trevor Elliott, Wouter Swierstra, Conrad Irwin, Tim Thelion
|
||||
maintainer: xmonad@haskell.org
|
||||
tested-with: GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.3, GHC == 8.6.1
|
||||
tested-with: GHC == 8.4.4 || == 8.6.5 || == 8.8.4 || == 8.10.4 || == 9.0.1
|
||||
category: System
|
||||
homepage: http://xmonad.org
|
||||
bug-reports: https://github.com/xmonad/xmonad/issues
|
||||
build-type: Simple
|
||||
extra-source-files: README.md
|
||||
CHANGES.md
|
||||
CONFIG
|
||||
STYLE
|
||||
tests/*.hs
|
||||
tests/Properties/*.hs
|
||||
tests/Properties/Layout/*.hs
|
||||
CONTRIBUTING.md
|
||||
INSTALL.md
|
||||
MAINTAINERS.md
|
||||
TUTORIAL.md
|
||||
man/xmonad.1.markdown
|
||||
man/xmonad.1
|
||||
man/xmonad.1.html
|
||||
man/xmonad.hs
|
||||
util/GenerateManpage.hs
|
||||
util/hpcReport.sh
|
||||
cabal-version: >= 1.8
|
||||
cabal-version: 1.12
|
||||
|
||||
source-repository head
|
||||
type: git
|
||||
location: https://github.com/xmonad/xmonad
|
||||
|
||||
flag testing
|
||||
flag pedantic
|
||||
description: Be pedantic (-Werror and the like)
|
||||
default: False
|
||||
manual: True
|
||||
description: Testing mode, only build minimal components
|
||||
|
||||
flag generatemanpage
|
||||
default: False
|
||||
manual: True
|
||||
description: Build the tool for generating the man page
|
||||
flag quickcheck-classes
|
||||
|
||||
library
|
||||
exposed-modules: XMonad
|
||||
@@ -72,36 +67,40 @@ library
|
||||
XMonad.StackSet
|
||||
other-modules: Paths_xmonad
|
||||
hs-source-dirs: src
|
||||
build-depends: base >= 4.9 && < 5
|
||||
, X11 >= 1.8 && < 1.10
|
||||
build-depends: base >= 4.11 && < 5
|
||||
, X11 >= 1.10 && < 1.11
|
||||
, containers
|
||||
, data-default
|
||||
, data-default-class
|
||||
, directory
|
||||
, extensible-exceptions
|
||||
, filepath
|
||||
, mtl
|
||||
, process
|
||||
, setlocale
|
||||
, time
|
||||
, transformers >= 0.3
|
||||
, unix
|
||||
, utf8-string >= 0.3 && < 1.1
|
||||
ghc-options: -funbox-strict-fields -Wall -fno-warn-unused-do-bind
|
||||
ghc-options: -funbox-strict-fields -Wall -Wno-unused-do-bind
|
||||
default-language: Haskell2010
|
||||
|
||||
if flag(testing)
|
||||
buildable: False
|
||||
-- Keep this in sync with the oldest version in 'tested-with'
|
||||
if impl(ghc > 8.4.4)
|
||||
ghc-options: -Wno-unused-imports
|
||||
|
||||
if flag(pedantic)
|
||||
ghc-options: -Werror
|
||||
|
||||
executable xmonad
|
||||
main-is: Main.hs
|
||||
build-depends: base, X11, mtl, unix, xmonad
|
||||
ghc-options: -Wall -fno-warn-unused-do-bind
|
||||
build-depends: base, xmonad
|
||||
ghc-options: -Wall -Wno-unused-do-bind
|
||||
default-language: Haskell2010
|
||||
|
||||
executable generatemanpage
|
||||
main-is: GenerateManpage.hs
|
||||
hs-source-dirs: util
|
||||
-- Keep this in sync with the oldest version in 'tested-with'
|
||||
if impl(ghc > 8.4.4)
|
||||
ghc-options: -Wno-unused-imports
|
||||
|
||||
if flag(generatemanpage)
|
||||
build-depends: base, pandoc >= 2, regex-posix, text
|
||||
else
|
||||
buildable: False
|
||||
if flag(pedantic)
|
||||
ghc-options: -Werror
|
||||
|
||||
test-suite properties
|
||||
type: exitcode-stdio-1.0
|
||||
@@ -124,4 +123,17 @@ test-suite properties
|
||||
Properties.Workspace
|
||||
Utils
|
||||
hs-source-dirs: tests
|
||||
build-depends: base, QuickCheck >= 2, X11, containers, extensible-exceptions, xmonad
|
||||
build-depends: base
|
||||
, QuickCheck >= 2
|
||||
, X11
|
||||
, containers
|
||||
, xmonad
|
||||
default-language: Haskell2010
|
||||
|
||||
if flag(quickcheck-classes) && impl(ghc > 8.5)
|
||||
-- no quickcheck-classes in LTS-12
|
||||
-- GHC 8.4 and lower needs too much boilerplate (Eq1, Show1, …)
|
||||
build-depends: quickcheck-classes >= 0.4.3
|
||||
|
||||
if flag(pedantic)
|
||||
ghc-options: -Werror
|
||||
|
Reference in New Issue
Block a user