Compare commits

..

No commits in common. "master" and "v0.17.1" have entirely different histories.

31 changed files with 530 additions and 1124 deletions

View File

@ -1,6 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

View File

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Clone project - name: Clone project
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Install dependencies - name: Install dependencies
run: | run: |
@ -26,8 +26,8 @@ jobs:
- name: Commit/push if changed - name: Commit/push if changed
run: | run: |
set -ex set -ex
git config user.name 'github-actions[bot]' git config user.name github-actions
git config user.email '41898282+github-actions[bot]@users.noreply.github.com' git config user.email github-actions@github.com
git diff --quiet --exit-code && exit git diff --quiet --exit-code && exit
git commit -a -m 'man: Update' git commit -a -m 'man: Update'
git push git push

View File

@ -37,43 +37,33 @@ set in GitHub repository secrets.
jobs: jobs:
linux: linux:
name: Haskell-CI - Linux - ${{ matrix.compiler }} name: Haskell-CI - Linux - ${{ matrix.compiler }}
@@ -33,6 +40,7 @@ @@ -31,6 +38,7 @@
compilerVersion: 9.8.4 compilerVersion: 9.0.2
setup-method: ghcup setup-method: ghcup
allow-failure: false allow-failure: false
+ upload: true + upload: true
- compiler: ghc-9.6.7 - compiler: ghc-8.10.7
compilerKind: ghc compilerKind: ghc
compilerVersion: 9.6.7 compilerVersion: 8.10.7
@@ -257,6 +265,10 @@ @@ -209,8 +217,80 @@
${CABAL} -vnormal check
- name: haddock - name: haddock
run: | run: |
$CABAL v2-haddock --disable-documentation $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all - $CABAL v2-haddock $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all
+ - name: haddock for hackage
+ if: matrix.upload
+ run: |
+ $CABAL v2-haddock $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH --haddock-for-hackage --builddir $GITHUB_WORKSPACE/haddock all + $CABAL v2-haddock $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH --haddock-for-hackage --builddir $GITHUB_WORKSPACE/haddock all
- name: unconstrained build - name: unconstrained build
run: | run: |
rm -f cabal.project.local rm -f cabal.project.local
@@ -267,3 +279,80 @@ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
with: + - name: upload artifacts (sdist)
key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
path: ~/.cabal/store
+ # must be separate artifacts because GitHub Actions are still broken:
+ # https://github.com/actions/upload-artifact/issues/441
+ # https://github.com/actions/upload-artifact/issues/457
+ - name: upload artifact (sdist)
+ if: matrix.upload + if: matrix.upload
+ uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v2
+ with: + with:
+ name: sdist
+ path: ${{ github.workspace }}/sdist/*.tar.gz + path: ${{ github.workspace }}/sdist/*.tar.gz
+ - name: upload artifact (haddock) + - name: upload artifacts (haddock)
+ if: matrix.upload + if: matrix.upload
+ uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v2
+ with: + with:
+ name: haddock
+ path: ${{ github.workspace }}/haddock/*-docs.tar.gz + path: ${{ github.workspace }}/haddock/*-docs.tar.gz
+ - name: hackage upload (candidate) + - name: hackage upload (candidate)
+ if: matrix.upload && github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' + if: matrix.upload && github.event_name == 'workflow_dispatch' && github.event.inputs.version != ''

View File

@ -8,9 +8,9 @@
# #
# For more information, see https://github.com/haskell-CI/haskell-ci # For more information, see https://github.com/haskell-CI/haskell-ci
# #
# version: 0.19.20250506 # version: 0.14.3
# #
# REGENDATA ("0.19.20250506",["github","cabal.project"]) # REGENDATA ("0.14.3",["github","cabal.project"])
# #
name: Haskell-CI name: Haskell-CI
on: on:
@ -26,44 +26,18 @@ on:
jobs: jobs:
linux: linux:
name: Haskell-CI - Linux - ${{ matrix.compiler }} name: Haskell-CI - Linux - ${{ matrix.compiler }}
runs-on: ubuntu-24.04 runs-on: ubuntu-18.04
timeout-minutes: timeout-minutes:
60 60
container: container:
image: buildpack-deps:jammy image: buildpack-deps:bionic
continue-on-error: ${{ matrix.allow-failure }} continue-on-error: ${{ matrix.allow-failure }}
strategy: strategy:
matrix: matrix:
include: include:
- compiler: ghc-9.12.2 - compiler: ghc-9.2.2
compilerKind: ghc compilerKind: ghc
compilerVersion: 9.12.2 compilerVersion: 9.2.2
setup-method: ghcup
allow-failure: false
- compiler: ghc-9.10.2
compilerKind: ghc
compilerVersion: 9.10.2
setup-method: ghcup
allow-failure: false
- compiler: ghc-9.8.4
compilerKind: ghc
compilerVersion: 9.8.4
setup-method: ghcup
allow-failure: false
upload: true
- compiler: ghc-9.6.7
compilerKind: ghc
compilerVersion: 9.6.7
setup-method: ghcup
allow-failure: false
- compiler: ghc-9.4.8
compilerKind: ghc
compilerVersion: 9.4.8
setup-method: ghcup
allow-failure: false
- compiler: ghc-9.2.8
compilerKind: ghc
compilerVersion: 9.2.8
setup-method: ghcup setup-method: ghcup
allow-failure: false allow-failure: false
- compiler: ghc-9.0.2 - compiler: ghc-9.0.2
@ -71,6 +45,7 @@ jobs:
compilerVersion: 9.0.2 compilerVersion: 9.0.2
setup-method: ghcup setup-method: ghcup
allow-failure: false allow-failure: false
upload: true
- compiler: ghc-8.10.7 - compiler: ghc-8.10.7
compilerKind: ghc compilerKind: ghc
compilerVersion: 8.10.7 compilerVersion: 8.10.7
@ -79,39 +54,41 @@ jobs:
- compiler: ghc-8.8.4 - compiler: ghc-8.8.4
compilerKind: ghc compilerKind: ghc
compilerVersion: 8.8.4 compilerVersion: 8.8.4
setup-method: ghcup setup-method: hvr-ppa
allow-failure: false allow-failure: false
- compiler: ghc-8.6.5 - compiler: ghc-8.6.5
compilerKind: ghc compilerKind: ghc
compilerVersion: 8.6.5 compilerVersion: 8.6.5
setup-method: ghcup setup-method: hvr-ppa
allow-failure: false
- compiler: ghc-8.4.4
compilerKind: ghc
compilerVersion: 8.4.4
setup-method: hvr-ppa
allow-failure: false allow-failure: false
fail-fast: false fail-fast: false
steps: steps:
- name: apt-get install - name: apt
run: | run: |
apt-get update apt-get update
apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common libtinfo5 apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common libtinfo5
apt-get install -y libx11-dev libxext-dev libxinerama-dev libxrandr-dev libxss-dev if [ "${{ matrix.setup-method }}" = ghcup ]; then
- name: Install GHCup mkdir -p "$HOME/.ghcup/bin"
run: | curl -sL https://downloads.haskell.org/ghcup/0.1.17.5/x86_64-linux-ghcup-0.1.17.5 > "$HOME/.ghcup/bin/ghcup"
mkdir -p "$HOME/.ghcup/bin" chmod a+x "$HOME/.ghcup/bin/ghcup"
curl -sL https://downloads.haskell.org/ghcup/0.1.50.1/x86_64-linux-ghcup-0.1.50.1 > "$HOME/.ghcup/bin/ghcup" "$HOME/.ghcup/bin/ghcup" install ghc "$HCVER"
chmod a+x "$HOME/.ghcup/bin/ghcup" "$HOME/.ghcup/bin/ghcup" install cabal 3.6.2.0
- name: Install cabal-install apt-get update
run: | apt-get install -y libx11-dev libxext-dev libxinerama-dev libxrandr-dev libxss-dev
"$HOME/.ghcup/bin/ghcup" install cabal 3.14.2.0 || (cat "$HOME"/.ghcup/logs/*.* && false) else
echo "CABAL=$HOME/.ghcup/bin/cabal-3.14.2.0 -vnormal+nowrap" >> "$GITHUB_ENV" apt-add-repository -y 'ppa:hvr/ghc'
- name: Install GHC (GHCup) apt-get update
if: matrix.setup-method == 'ghcup' apt-get install -y "$HCNAME" libx11-dev libxext-dev libxinerama-dev libxrandr-dev libxss-dev
run: | mkdir -p "$HOME/.ghcup/bin"
"$HOME/.ghcup/bin/ghcup" install ghc "$HCVER" || (cat "$HOME"/.ghcup/logs/*.* && false) curl -sL https://downloads.haskell.org/ghcup/0.1.17.5/x86_64-linux-ghcup-0.1.17.5 > "$HOME/.ghcup/bin/ghcup"
HC=$("$HOME/.ghcup/bin/ghcup" whereis ghc "$HCVER") chmod a+x "$HOME/.ghcup/bin/ghcup"
HCPKG=$(echo "$HC" | sed 's#ghc$#ghc-pkg#') "$HOME/.ghcup/bin/ghcup" install cabal 3.6.2.0
HADDOCK=$(echo "$HC" | sed 's#ghc$#haddock#') fi
echo "HC=$HC" >> "$GITHUB_ENV"
echo "HCPKG=$HCPKG" >> "$GITHUB_ENV"
echo "HADDOCK=$HADDOCK" >> "$GITHUB_ENV"
env: env:
HCKIND: ${{ matrix.compilerKind }} HCKIND: ${{ matrix.compilerKind }}
HCNAME: ${{ matrix.compiler }} HCNAME: ${{ matrix.compiler }}
@ -122,12 +99,28 @@ jobs:
echo "LANG=C.UTF-8" >> "$GITHUB_ENV" echo "LANG=C.UTF-8" >> "$GITHUB_ENV"
echo "CABAL_DIR=$HOME/.cabal" >> "$GITHUB_ENV" echo "CABAL_DIR=$HOME/.cabal" >> "$GITHUB_ENV"
echo "CABAL_CONFIG=$HOME/.cabal/config" >> "$GITHUB_ENV" echo "CABAL_CONFIG=$HOME/.cabal/config" >> "$GITHUB_ENV"
HCDIR=/opt/$HCKIND/$HCVER
if [ "${{ matrix.setup-method }}" = ghcup ]; then
HC=$HOME/.ghcup/bin/$HCKIND-$HCVER
echo "HC=$HC" >> "$GITHUB_ENV"
echo "HCPKG=$HOME/.ghcup/bin/$HCKIND-pkg-$HCVER" >> "$GITHUB_ENV"
echo "HADDOCK=$HOME/.ghcup/bin/haddock-$HCVER" >> "$GITHUB_ENV"
echo "CABAL=$HOME/.ghcup/bin/cabal-3.6.2.0 -vnormal+nowrap" >> "$GITHUB_ENV"
else
HC=$HCDIR/bin/$HCKIND
echo "HC=$HC" >> "$GITHUB_ENV"
echo "HCPKG=$HCDIR/bin/$HCKIND-pkg" >> "$GITHUB_ENV"
echo "HADDOCK=$HCDIR/bin/haddock" >> "$GITHUB_ENV"
echo "CABAL=$HOME/.ghcup/bin/cabal-3.6.2.0 -vnormal+nowrap" >> "$GITHUB_ENV"
fi
HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\d+)\.(\d+)\.(\d+)(\.(\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))') 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 "HCNUMVER=$HCNUMVER" >> "$GITHUB_ENV"
echo "ARG_TESTS=--enable-tests" >> "$GITHUB_ENV" echo "ARG_TESTS=--enable-tests" >> "$GITHUB_ENV"
echo "ARG_BENCH=--enable-benchmarks" >> "$GITHUB_ENV" echo "ARG_BENCH=--enable-benchmarks" >> "$GITHUB_ENV"
echo "HEADHACKAGE=false" >> "$GITHUB_ENV" echo "HEADHACKAGE=false" >> "$GITHUB_ENV"
echo "ARG_COMPILER=--$HCKIND --with-compiler=$HC" >> "$GITHUB_ENV" echo "ARG_COMPILER=--$HCKIND --with-compiler=$HC" >> "$GITHUB_ENV"
echo "GHCJSARITH=0" >> "$GITHUB_ENV"
env: env:
HCKIND: ${{ matrix.compilerKind }} HCKIND: ${{ matrix.compilerKind }}
HCNAME: ${{ matrix.compiler }} HCNAME: ${{ matrix.compiler }}
@ -167,17 +160,28 @@ jobs:
- name: update cabal index - name: update cabal index
run: | run: |
$CABAL v2-update -v $CABAL v2-update -v
- name: cache (tools)
uses: actions/cache@v2
with:
key: ${{ runner.os }}-${{ matrix.compiler }}-tools-c0dbbd39
path: ~/.haskell-ci-tools
- name: install cabal-plan - name: install cabal-plan
run: | run: |
mkdir -p $HOME/.cabal/bin mkdir -p $HOME/.cabal/bin
curl -sL https://github.com/haskell-hvr/cabal-plan/releases/download/v0.7.3.0/cabal-plan-0.7.3.0-x86_64-linux.xz > cabal-plan.xz 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 'f62ccb2971567a5f638f2005ad3173dba14693a45154c1508645c52289714cb2 cabal-plan.xz' | sha256sum -c - echo 'de73600b1836d3f55e32d80385acc055fd97f60eaa0ab68a755302685f5d81bc cabal-plan.xz' | sha256sum -c -
xz -d < cabal-plan.xz > $HOME/.cabal/bin/cabal-plan xz -d < cabal-plan.xz > $HOME/.cabal/bin/cabal-plan
rm -f cabal-plan.xz rm -f cabal-plan.xz
chmod a+x $HOME/.cabal/bin/cabal-plan chmod a+x $HOME/.cabal/bin/cabal-plan
cabal-plan --version cabal-plan --version
- name: install hlint
run: |
if [ $((HCNUMVER >= 90000 && HCNUMVER < 90200)) -ne 0 ] ; then HLINTVER=$(cd /tmp && (${CABAL} v2-install -v $ARG_COMPILER --dry-run hlint --constraint='hlint >=3.4 && <3.5' | perl -ne 'if (/\bhlint-(\d+(\.\d+)*)\b/) { print "$1"; last; }')); echo "HLint version $HLINTVER" ; fi
if [ $((HCNUMVER >= 90000 && HCNUMVER < 90200)) -ne 0 ] ; then if [ ! -e $HOME/.haskell-ci-tools/hlint-$HLINTVER/hlint ]; then echo "Downloading HLint version $HLINTVER"; mkdir -p $HOME/.haskell-ci-tools; curl --write-out 'Status Code: %{http_code} Redirects: %{num_redirects} Total time: %{time_total} Total Dsize: %{size_download}\n' --silent --location --output $HOME/.haskell-ci-tools/hlint-$HLINTVER.tar.gz "https://github.com/ndmitchell/hlint/releases/download/v$HLINTVER/hlint-$HLINTVER-x86_64-linux.tar.gz"; tar -xzv -f $HOME/.haskell-ci-tools/hlint-$HLINTVER.tar.gz -C $HOME/.haskell-ci-tools; fi ; fi
if [ $((HCNUMVER >= 90000 && HCNUMVER < 90200)) -ne 0 ] ; then mkdir -p $CABAL_DIR/bin && ln -sf "$HOME/.haskell-ci-tools/hlint-$HLINTVER/hlint" $CABAL_DIR/bin/hlint ; fi
if [ $((HCNUMVER >= 90000 && HCNUMVER < 90200)) -ne 0 ] ; then hlint --version ; fi
- name: checkout - name: checkout
uses: actions/checkout@v4 uses: actions/checkout@v2
with: with:
path: source path: source
- name: initial cabal.project for sdist - name: initial cabal.project for sdist
@ -209,15 +213,15 @@ jobs:
package xmonad package xmonad
flags: +pedantic flags: +pedantic
EOF EOF
$HCPKG list --simple-output --names-only | perl -ne 'for (split /\s+/) { print "constraints: any.$_ installed\n" unless /^(xmonad)$/; }' >> cabal.project.local $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
cat cabal.project.local cat cabal.project.local
- name: dump install plan - name: dump install plan
run: | run: |
$CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dry-run all $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dry-run all
cabal-plan cabal-plan
- name: restore cache - name: cache
uses: actions/cache/restore@v4 uses: actions/cache@v2
with: with:
key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }} key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
path: ~/.cabal/store path: ~/.cabal/store
@ -235,41 +239,30 @@ jobs:
- name: tests - name: tests
run: | run: |
$CABAL v2-test $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --test-show-details=direct $CABAL v2-test $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --test-show-details=direct
- name: hlint
run: |
if [ $((HCNUMVER >= 90000 && HCNUMVER < 90200)) -ne 0 ] ; then (cd ${PKGDIR_xmonad} && hlint -h ${GITHUB_WORKSPACE}/source/.hlint.yaml -XHaskell2010 src) ; fi
if [ $((HCNUMVER >= 90000 && HCNUMVER < 90200)) -ne 0 ] ; then (cd ${PKGDIR_xmonad} && hlint -h ${GITHUB_WORKSPACE}/source/.hlint.yaml -XHaskell2010 .) ; fi
- name: cabal check - name: cabal check
run: | run: |
cd ${PKGDIR_xmonad} || false cd ${PKGDIR_xmonad} || false
${CABAL} -vnormal check ${CABAL} -vnormal check
- name: haddock - name: haddock
run: |
$CABAL v2-haddock --disable-documentation $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all
- name: haddock for hackage
if: matrix.upload
run: | run: |
$CABAL v2-haddock $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH --haddock-for-hackage --builddir $GITHUB_WORKSPACE/haddock all $CABAL v2-haddock $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH --haddock-for-hackage --builddir $GITHUB_WORKSPACE/haddock all
- name: unconstrained build - name: unconstrained build
run: | run: |
rm -f cabal.project.local rm -f cabal.project.local
$CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
- name: save cache - name: upload artifacts (sdist)
if: always()
uses: actions/cache/save@v4
with:
key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
path: ~/.cabal/store
# must be separate artifacts because GitHub Actions are still broken:
# https://github.com/actions/upload-artifact/issues/441
# https://github.com/actions/upload-artifact/issues/457
- name: upload artifact (sdist)
if: matrix.upload if: matrix.upload
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v2
with: with:
name: sdist
path: ${{ github.workspace }}/sdist/*.tar.gz path: ${{ github.workspace }}/sdist/*.tar.gz
- name: upload artifact (haddock) - name: upload artifacts (haddock)
if: matrix.upload if: matrix.upload
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v2
with: with:
name: haddock
path: ${{ github.workspace }}/haddock/*-docs.tar.gz path: ${{ github.workspace }}/haddock/*-docs.tar.gz
- name: hackage upload (candidate) - name: hackage upload (candidate)
if: matrix.upload && github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' if: matrix.upload && github.event_name == 'workflow_dispatch' && github.event.inputs.version != ''

View File

@ -1,22 +0,0 @@
name: hlint
on:
push:
pull_request:
jobs:
hlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 'Set up HLint'
uses: haskell-actions/hlint-setup@v2
with:
version: '3.5'
- name: 'Run HLint'
uses: haskell-actions/hlint-run@v2
with:
path: '.'
fail-on: status

View File

@ -12,10 +12,14 @@ jobs:
contents: read contents: read
steps: steps:
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@v31 uses: cachix/install-nix-action@v13
with: with:
github_access_token: ${{ secrets.GITHUB_TOKEN }} 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 - name: Clone project
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Build - name: Build
run: nix build --print-build-logs run: nix build --print-build-logs

View File

@ -13,16 +13,16 @@ jobs:
steps: steps:
- name: Clone project - name: Clone project
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Setup Haskell - name: Setup Haskell
uses: haskell-actions/setup@v2 uses: haskell/actions/setup@v1
with: with:
# packdeps doesn't build with newer as of 2021-10 # packdeps doesn't build with newer as of 2021-10
ghc-version: '8.8' ghc-version: '8.8'
- name: Install packdeps - name: Install packdeps
run: | run: |
set -ex set -ex
cd # go somewhere without a cabal.project echo "$HOME/.cabal/bin" >> $GITHUB_PATH
cabal install packdeps cabal install packdeps
- name: Check package bounds (all) - name: Check package bounds (all)
continue-on-error: true continue-on-error: true
@ -40,9 +40,10 @@ jobs:
*.cabal *.cabal
workflow-keepalive: workflow-keepalive:
if: github.event_name == 'schedule'
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
actions: write
steps: steps:
- uses: liskin/gh-workflow-keepalive@v1 - name: Re-enable workflow
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh api -X PUT repos/${{ github.repository }}/actions/workflows/packdeps.yml/enable

View File

@ -12,23 +12,30 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- resolver: lts-14 # GHC 8.6 - resolver: lts-12
- resolver: lts-16 # GHC 8.8 ghc: 8.4.4
- resolver: lts-18 # GHC 8.10 - resolver: lts-14
- resolver: lts-19 # GHC 9.0 ghc: 8.6.5
- resolver: lts-20 # GHC 9.2 - resolver: lts-16
- resolver: lts-21 # GHC 9.4 ghc: 8.8.4
- resolver: lts-22 # GHC 9.6 - resolver: lts-18
- resolver: lts-23 # GHC 9.8 ghc: 8.10.7
- resolver: lts-19
ghc: 9.0.2
steps: steps:
- name: Clone project - name: Clone project
uses: actions/checkout@v4 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 - name: Install C dependencies
run: | run: |
set -ex set -ex
sudo apt update -y
sudo apt install -y \ sudo apt install -y \
libx11-dev \ libx11-dev \
libxext-dev \ libxext-dev \
@ -37,6 +44,14 @@ jobs:
libxss-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 - name: Refresh caches once a month
id: cache-date id: cache-date
# GHA writes caches on the first miss and then never updates them again; # GHA writes caches on the first miss and then never updates them again;
@ -44,16 +59,16 @@ jobs:
# date is prefixed with an epoch number to let us manually refresh the # date is prefixed with an epoch number to let us manually refresh the
# cache when needed. This is a workaround for https://github.com/actions/cache/issues/2 # cache when needed. This is a workaround for https://github.com/actions/cache/issues/2
run: | run: |
date +date=1-%Y-%m >> $GITHUB_OUTPUT echo "::set-output name=date::1-$(date +%Y-%m)"
- name: Cache Haskell package metadata - name: Cache Haskell package metadata
uses: actions/cache@v4 uses: actions/cache@v2
with: with:
path: ~/.stack/pantry path: ~/.stack/pantry
key: stack-pantry-${{ runner.os }}-${{ steps.cache-date.outputs.date }} key: stack-pantry-${{ runner.os }}-${{ steps.cache-date.outputs.date }}
- name: Cache Haskell dependencies - name: Cache Haskell dependencies
uses: actions/cache@v4 uses: actions/cache@v2
with: with:
path: | path: |
~/.stack/* ~/.stack/*

3
.gitignore vendored
View File

@ -27,6 +27,3 @@ tags
/cabal.sandbox.config /cabal.sandbox.config
/dist-newstyle/ /dist-newstyle/
/dist/ /dist/
# nix artifacts
result

View File

@ -35,5 +35,4 @@ Valery V. Vorotyntsev <valery.vv@gmail.com>
Vanessa McHale <vamchale@gmail.com> <vanessa.mchale@reconfigure.io> Vanessa McHale <vamchale@gmail.com> <vanessa.mchale@reconfigure.io>
Wirt Wolff <wirtwolff@gmail.com> Wirt Wolff <wirtwolff@gmail.com>
Tony Zorman <soliditsallgood@mailbox.org> <50166980+slotThe@users.noreply.github.com> slotThe <soliditsallgood@mailbox.org> <50166980+slotThe@users.noreply.github.com>
Tony Zorman <soliditsallgood@mailbox.org>

View File

@ -1,64 +1,6 @@
# Change Log / Release Notes # Change Log / Release Notes
## _unreleased_ ## 0.17.1 (September 3, 2021)
### Breaking Changes
* Use `cabal` for `--recompile` if there is a `.cabal` file in the config
directory and none of `build`, `stack.yaml`, `flake.nix`, nor `default.nix`
exist.
### Enhancements
### Bug Fixes
### Other
PR #404 (see last change in 0.17.1) has been reverted, because the affected
compilers are (hopefully) no longer being used.
All 9.0 releases of GHC, plus 9.2.1 and 9.2.2 have the join point bug.
Note that 9.0.x is known to also have GC issues and is officially deprecated,
and the only 9.2 release that should be used is 9.2.8. Additionally, GHC HQ
doesn't support releases before 9.6.6.
## 0.18.0 (February 3, 2024)
### Breaking Changes
* Dropped support for GHC 8.4.
### Enhancements
* Exported `sendRestart` and `sendReplace` from `XMonad.Operations`.
* Exported `buildLaunch` from `XMonad.Main`.
* `Tall` does not draw windows with zero area.
* `XMonad.Operations.floatLocation` now applies size hints. This means windows
will snap to these hints as soon as they're floated (mouse move, keybinding).
Previously that only happened on mouse resize.
* Recompilation now detects `flake.nix` and `default.nix` (can be a
symlink) and switches to using `nix build` as appropriate.
* Added `unGrab` to `XMonad.Operations`; this releases XMonad's passive
keyboard grab, so other applications (like `scrot`) can do their
thing.
### Bug Fixes
* Duplicated floats (e.g. from X.A.CopyToAll) no longer escape to inactive
screens.
## 0.17.2 (April 2, 2023)
### Bug Fixes
* Fixed the build with GHC 9.6.
## 0.17.1 (September 3, 2022)
### Enhancements ### Enhancements
@ -66,9 +8,6 @@ doesn't support releases before 9.6.6.
* Exported `cacheNumlockMask` and `mkGrabs` from `XMonad.Operations`. * Exported `cacheNumlockMask` and `mkGrabs` from `XMonad.Operations`.
* Added `willFloat` function to `XMonad.ManageHooks` to detect whether the
(about to be) managed window will be a floating window or not.
### Bug Fixes ### Bug Fixes
* Fixed border color of windows with alpha channel. Now all windows have the * Fixed border color of windows with alpha channel. Now all windows have the
@ -126,6 +65,9 @@ doesn't support releases before 9.6.6.
* Added `withUnfocused` function to `XMonad.Operations`, allowing for `X` * Added `withUnfocused` function to `XMonad.Operations`, allowing for `X`
operations to be applied to unfocused windows. operations to be applied to unfocused windows.
* Added `willFloat` function to `XMonad.ManageHooks` to detect whether the
(about to be) managed window will be a floating window or not
[these build scripts]: https://github.com/xmonad/xmonad-testing/tree/master/build-scripts [these build scripts]: https://github.com/xmonad/xmonad-testing/tree/master/build-scripts
### Bug Fixes ### Bug Fixes

View File

@ -101,7 +101,7 @@ This will give you the latest `HEAD`; if you want you can also check
out a tagged release, e.g.: out a tagged release, e.g.:
``` console ``` console
$ git clone --branch v0.17.2 https://github.com/xmonad/xmonad $ git clone --branch v0.17.1 https://github.com/xmonad/xmonad
$ git clone --branch v0.17.1 https://github.com/xmonad/xmonad-contrib $ git clone --branch v0.17.1 https://github.com/xmonad/xmonad-contrib
``` ```
@ -123,9 +123,7 @@ Unless you already know which one you prefer, use Stack, which is easier.
#### Install Stack #### Install Stack
Probably one of the best ways to get [stack] is to use [GHCup], which is the main Haskell installer according to language's official [website][GHCup] and community [survey]. GHCup is [widely available] and is considered less error prone than other installation options. The easiest way to get [stack] is probably via your system's package
You can also use your system's package
manager: manager:
``` console ``` console
@ -147,6 +145,9 @@ stack via its [documentation][stack]):
$ curl -sSL https://get.haskellstack.org/ | sh $ 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 #### Create a New Project
Let's create a stack project. Since we're already in the correct Let's create a stack project. Since we're already in the correct
@ -221,9 +222,7 @@ be that you don't have the required C libraries installed. See
#### Install cabal-install #### Install cabal-install
Probably one of the best ways to get [cabal-install] is to use [GHCup], which is the main Haskell installer according to language's official [website][GHCup] and community [survey]. GHCup is [widely available] and is considered less error prone than other installation options. The easiest way to get [cabal-install] is probably via your system's package
You can also use your system's package
manager: manager:
``` console ``` console
@ -232,22 +231,22 @@ $ sudo dnf install cabal-install # Fedora
$ sudo pacman -S cabal-install # Arch $ sudo pacman -S cabal-install # Arch
``` ```
See also <https://www.haskell.org/cabal/#install-upgrade>. 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 #### Create a New Project
If you want to use `xmonad` or `xmonad-contrib` from git, you will need a Let's create a cabal project. Since we're already in the correct
`cabal.project` file. If you want to use both from [Hackage][], you should directory (`~/.config/xmonad`) with `xmonad` and `xmonad-contrib`
skip this step. subdirectories, we'll instruct cabal to use them. Create a file named
`cabal.project` containing:
Create a file named `cabal.project` containing:
``` ```
packages: */*.cabal packages: */*.cabal
``` ```
(If you do this step without using [git] checkouts, you will get an error from (If you skip this step, cabal will use the latest releases from [Hackage][]
cabal in the next step. Simply remove `cabal.project` and try again.) instead.)
#### Install Everything #### Install Everything
@ -256,7 +255,7 @@ libraries and then build the xmonad binary:
``` console ``` console
$ cabal update $ cabal update
$ cabal install --package-env=$HOME/.config/xmonad --lib base xmonad xmonad-contrib $ cabal install --package-env=$HOME/.config/xmonad --lib xmonad xmonad-contrib
$ cabal install --package-env=$HOME/.config/xmonad xmonad $ cabal install --package-env=$HOME/.config/xmonad xmonad
``` ```
@ -394,9 +393,7 @@ executable will also be within that directory and not in
[git]: https://git-scm.com/ [git]: https://git-scm.com/
[stack]: https://docs.haskellstack.org/en/stable/README/ [stack]: https://docs.haskellstack.org/en/stable/README/
[cabal-install]: https://www.haskell.org/cabal/ [cabal-install]: https://www.haskell.org/cabal/
[GHCup]: https://www.haskell.org/ghcup/ [ghcup]: https://www.haskell.org/ghcup/
[survey]: https://taylor.fausak.me/2022/11/18/haskell-survey-results/
[widely available]: https://www.haskell.org/ghcup/install/#supported-platforms
[what xmonad would do]: https://github.com/xmonad/xmonad/blob/master/src/XMonad/Core.hs#L659-L667 [what xmonad would do]: https://github.com/xmonad/xmonad/blob/master/src/XMonad/Core.hs#L659-L667
[Hackage]: https://hackage.haskell.org/ [Hackage]: https://hackage.haskell.org/
[scripts/build]: https://github.com/xmonad/xmonad-contrib/blob/master/scripts/build [scripts/build]: https://github.com/xmonad/xmonad-contrib/blob/master/scripts/build

View File

@ -117,12 +117,7 @@ When the time comes to release another version of xmonad and xmonad-contrib:
See [old announcements][old-announce] ([even older][older-announce]) for inspiration. See [old announcements][old-announce] ([even older][older-announce]) for inspiration.
7. Trigger xmonad-docs build to generate and persist docs for the just 7. Bump version for development (add `.9`) and prepare fresh sections in
released version:
- https://github.com/xmonad/xmonad-docs/actions/workflows/stack.yml
8. Bump version for development (add `.9`) and prepare fresh sections in
[`CHANGES.md`](CHANGES.md). [`CHANGES.md`](CHANGES.md).
[packdeps]: https://hackage.haskell.org/package/packdeps [packdeps]: https://hackage.haskell.org/package/packdeps

View File

@ -6,9 +6,9 @@
<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://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> <a href="https://haskell.org/"><img alt="Made in Haskell" src="https://img.shields.io/badge/Made%20in-Haskell-%235e5086?logo=haskell"></a>
<br> <br>
<a href="https://github.com/xmonad/xmonad/actions/workflows/stack.yml"><img alt="Stack" src="https://img.shields.io/github/actions/workflow/status/xmonad/xmonad/stack.yml?label=Stack&logo=githubactions&logoColor=white"></a> <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/actions/workflow/status/xmonad/xmonad/haskell-ci.yml?label=Cabal&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/actions/workflow/status/xmonad/xmonad/nix.yml?label=Nix&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> <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://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> <a href="https://opencollective.com/xmonad"><img alt="Open Collective" src="https://img.shields.io/opencollective/all/xmonad?label=Open%20Collective&logo=opencollective"></a>

View File

@ -38,13 +38,13 @@ package manager, you will need to `xmonad --recompile` _every time_ a
Haskell dependency is updated—else xmonad may fail to start when you Haskell dependency is updated—else xmonad may fail to start when you
want to log in! want to log in!
We're going to assume xmonad version `>= 0.17.0` and xmonad-contrib We're going to assume xmonad version `0.17.0` and xmonad-contrib version
version `>= 0.17.0` here, though most of these steps should work with `0.17.0` here, though most of these steps should work with older
older versions as well. When we get to the relevant parts, will point versions as well. When we get to the relevant parts, will point you to
you to alternatives that work with at least xmonad version `0.15` and alternatives that work with at least xmonad version `0.15` and
xmonad-contrib version `0.16`. This will usually be accompanied by big xmonad-contrib version `0.16`. This will usually be accompanied by a
warning labels for the respective version bounds, so don't worry about big "_IF YOU ARE ON A VERSION `< 0.17.0`_", so don't worry about missing
missing it! it!
Throughout the tutorial we will use, for keybindings, a syntax very akin Throughout the tutorial we will use, for keybindings, a syntax very akin
to the [GNU Emacs conventions] for the same thing—so `C-x` means "hold to the [GNU Emacs conventions] for the same thing—so `C-x` means "hold
@ -77,11 +77,6 @@ a live xmonad session in some capacity. If you have set up your
`~/.xinitrc` as directed in the xmonad guided tour, you should be good `~/.xinitrc` as directed in the xmonad guided tour, you should be good
to go! If not, just smack an `exec xmonad` at the bottom of that file. to go! If not, just smack an `exec xmonad` at the bottom of that file.
In particular, it might be a good idea to set a wallpaper beforehand.
Otherwise, when switching workspaces or closing windows, you might start
seeing "shadows" of windows that were there before, unable to interact
with them.
## Installing Xmobar ## Installing Xmobar
What we need to do now—provided we want to use a bar at all—is to What we need to do now—provided we want to use a bar at all—is to
@ -113,8 +108,6 @@ utility modules we will use. At the very top of the file, write
import XMonad import XMonad
import XMonad.Util.EZConfig import XMonad.Util.EZConfig
-- NOTE: Only needed for versions < 0.18.0! For 0.18.0 and up, this is
-- already included in the XMonad import and will give you a warning!
import XMonad.Util.Ungrab import XMonad.Util.Ungrab
``` ```
@ -270,7 +263,7 @@ myLayout = tiled ||| Mirror tiled ||| Full
``` ```
The so-called `where`-clause above simply consists of local declarations The so-called `where`-clause above simply consists of local declarations
that might clutter things up were they all declared at the top-level that might clutter things up where they all declared at the top-level
like this like this
``` haskell ``` haskell
@ -386,16 +379,12 @@ effect (and some applications, like chromium, will misbehave and need
some [Hacks] to make this work), we will also add the relevant function some [Hacks] to make this work), we will also add the relevant function
to get "proper" fullscreen behaviour here. to get "proper" fullscreen behaviour here.
---
_IF YOU ARE ON A VERSION `< 0.17.0`_: The `ewmhFullscreen` function does _IF YOU ARE ON A VERSION `< 0.17.0`_: The `ewmhFullscreen` function does
not exist in these versions. Instead of it, you can try to add not exist in these versions. Instead of it, you can try to add
`fullscreenEventHook` to your `handleEventHook` to achieve similar `fullscreenEventHook` to your `handleEventHook` to achieve similar
functionality (how to do this is explained in the documentation of functionality (how to do this is explained in the documentation of
[XMonad.Hooks.EwmhDesktops]). [XMonad.Hooks.EwmhDesktops]).
---
To use the two combinators, we compose them with the `xmonad` function To use the two combinators, we compose them with the `xmonad` function
in the following way: in the following way:
@ -441,7 +430,7 @@ Much better!
## Make XMonad and Xmobar Talk to Each Other ## Make XMonad and Xmobar Talk to Each Other
Onto the main dish. First, we have to import the necessary modules. Onto the main dish. First, we have to import the necessary modules.
Add the following to your list of imports Add the following to your list of imports:
``` haskell ``` haskell
import XMonad.Hooks.DynamicLog import XMonad.Hooks.DynamicLog
@ -449,27 +438,23 @@ import XMonad.Hooks.StatusBar
import XMonad.Hooks.StatusBar.PP import XMonad.Hooks.StatusBar.PP
``` ```
and replace your `main` function above with: _IF YOU ARE ON A VERSION `< 0.17.0`_: The `XMonad.Hooks.StatusBar` and
`XMonad.Hooks.StatusBar.PP` modules don't exist yet. You can find
everything you need in the `XMonad.Hooks.DynamicLog` module, so remove
these two imports.
Replace your `main` function above with:
``` haskell ``` haskell
main :: IO () main :: IO ()
main = xmonad $ ewmhFullscreen $ ewmh $ xmobarProp $ myConfig main = xmonad $ ewmhFullscreen $ ewmh $ xmobarProp $ myConfig
``` ```
--- _IF YOU ARE ON A VERSION `< 0.17.0`_: The `xmobarProp` function does not
exist in these versions. Instead of it, use `xmobar` via
_IF YOU ARE ON A VERSION `< 0.17.0`_: The `XMonad.Hooks.StatusBar` and `main = xmonad . ewmh =<< xmobar myConfig` and carefully read the part
`XMonad.Hooks.StatusBar.PP` modules don't exist yet. You can find about pipes later on (`xmobar` uses pipes to make xmobar talk to
everything you need in the `XMonad.Hooks.DynamicLog` module, so remove xmonad).
these two imports.
Further, the `xmobarProp` function does not exist in older versions.
Instead of it, use `xmobar` via `main = xmonad . ewmh =<< xmobar
myConfig` and carefully read the part about pipes later on (`xmobar`
uses pipes to make xmobar talk to xmonad). Do note the lack of
`ewmhFullscreen`, as explained above!
---
As a quick side-note, we could have also written As a quick side-note, we could have also written
@ -555,15 +540,11 @@ when things are not being read! For this reason we have to use
(this is useful, for example, for [XMonad.Util.ClickableWorkspaces], (this is useful, for example, for [XMonad.Util.ClickableWorkspaces],
which is a new feature in `0.17.0`). which is a new feature in `0.17.0`).
---
_IF YOU ARE ON A VERSION `< 0.17.0`_: As discussed above, the `xmobar` _IF YOU ARE ON A VERSION `< 0.17.0`_: As discussed above, the `xmobar`
function uses pipes, so you actually do want to use the `StdinReader`. function uses pipes, so you actually do want to use the `StdinReader`.
Simply replace _all_ occurences of `XMonadLog` with `StdinReader` Simply replace _all_ occurences of `XMonadLog` with `StdinReader`
below (don't forget the template!) below (don't forget the template!)
---
## Configuring Xmobar ## Configuring Xmobar
Now, before this will work, we have to configure xmobar. Here's a nice Now, before this will work, we have to configure xmobar. Here's a nice
@ -679,8 +660,6 @@ main = xmonad
$ myConfig $ myConfig
``` ```
---
_IF YOU ARE ON A VERSION `< 0.17.0`_: `xmobar` has a similar definition, _IF YOU ARE ON A VERSION `< 0.17.0`_: `xmobar` has a similar definition,
relying on `statusBar` alone: `xmobar = statusBar "xmobar" xmobarPP relying on `statusBar` alone: `xmobar = statusBar "xmobar" xmobarPP
toggleStrutsKey`. Sadly, the `defToggleStrutsKey` function is not yet toggleStrutsKey`. Sadly, the `defToggleStrutsKey` function is not yet
@ -689,6 +668,7 @@ _IF YOU ARE ON A VERSION `< 0.17.0`_: `xmobar` has a similar definition,
``` haskell ``` haskell
main :: IO () main :: IO ()
main = xmonad main = xmonad
. ewmhFullscreen
. ewmh . ewmh
=<< statusBar "xmobar" def toggleStrutsKey myConfig =<< statusBar "xmobar" def toggleStrutsKey myConfig
where where
@ -696,8 +676,6 @@ main = xmonad
toggleStrutsKey XConfig{ modMask = m } = (m, xK_b) toggleStrutsKey XConfig{ modMask = m } = (m, xK_b)
``` ```
---
The `defToggleStrutsKey` here is just the key with which you can toggle The `defToggleStrutsKey` here is just the key with which you can toggle
the bar; it is bound to `M-b`. If you want to change this, you can also the bar; it is bound to `M-b`. If you want to change this, you can also
define your own: define your own:
@ -794,8 +772,6 @@ myXmobarPP = def
lowWhite = xmobarColor "#bbbbbb" "" lowWhite = xmobarColor "#bbbbbb" ""
``` ```
---
_IF YOU ARE ON A VERSION `< 0.17`_: Both `logTitles` and `xmobarBorder` _IF YOU ARE ON A VERSION `< 0.17`_: Both `logTitles` and `xmobarBorder`
are not available yet, so you will have to remove them. As an are not available yet, so you will have to remove them. As an
alternative to `xmobarBorder`, a common way to "mark" the currently alternative to `xmobarBorder`, a common way to "mark" the currently
@ -803,8 +779,6 @@ _IF YOU ARE ON A VERSION `< 0.17`_: Both `logTitles` and `xmobarBorder`
`ppCurrent = wrap (blue "[") (blue "]")` and see if you like it. Also `ppCurrent = wrap (blue "[") (blue "]")` and see if you like it. Also
read the bit about `ppOrder` further down! read the bit about `ppOrder` further down!
---
That's a lot! But don't worry, take a deep breath and remind yourself That's a lot! But don't worry, take a deep breath and remind yourself
of what you read above in the documentation of the [PP record]. Even if of what you read above in the documentation of the [PP record]. Even if
you haven't read the documentation yet, most of the fields should be you haven't read the documentation yet, most of the fields should be
@ -908,10 +882,10 @@ apt-get install nm-applet feh xfce4-power-manager
First, configure xscreensaver how you like it with the First, configure xscreensaver how you like it with the
`xscreensaver-demo` command. Now, we will set these things up in `xscreensaver-demo` command. Now, we will set these things up in
`~/.xinitrc`. If you want to use XMonad with a desktop environment, see `~/.xinitrc` (we could also do most of this in xmonad's `startupHook`,
[Basic Desktop Environment Integration] for how to do this. For a but `~/.xinitrc` is perhaps more standard). If you want to use xmonad
version using XMonad's built in functionality instead, see the [next with a desktop environment, see [Basic Desktop Environment Integration]
section][using-the-startupHook]. for how to do this.
Your `~/.xinitrc` may wind up looking like this: Your `~/.xinitrc` may wind up looking like this:
@ -959,38 +933,6 @@ Mission accomplished!
Of course substitute the wallpaper for one of your own. If you like the Of course substitute the wallpaper for one of your own. If you like the
one used above, you can find it [here](https://i.imgur.com/9MQHuZx.png). one used above, you can find it [here](https://i.imgur.com/9MQHuZx.png).
### Using the `startupHook`
Instead of the `.xinitrc` file, one can also use XMonad's built in
`startupHook` in order to auto start programs. The
[XMonad.Util.SpawnOnce] library is perfect for this use case, allowing
programs to start only once, and not with every invocation of `M-q`!
This requires but a small change in `myConfig`:
``` haskell
-- import XMonad.Util.SpawnOnce (spawnOnce)
myConfig = def
{ modMask = mod4Mask -- Rebind Mod to the Super key
, layoutHook = myLayout -- Use custom layouts
, startupHook = myStartupHook
}
`additionalKeysP`
[ ("M-S-z", spawn "xscreensaver-command -lock")
, ("M-C-s", unGrab *> spawn "scrot -s" )
, ("M-f" , spawn "firefox" )
]
myStartupHook :: X ()
myStartupHook = do
spawnOnce "trayer --edge top --align right --SetDockType true \
\--SetPartialStrut true --expand true --width 10 \
\--transparent true --tint 0x5f5f5f --height 18"
spawnOnce "feh --bg-fill --no-fehbg ~/.wallpapers/haskell-red-noise.png"
-- … and so on …
```
## Final Touches ## Final Touches
There may be some programs that you don't want xmonad to tile. The There may be some programs that you don't want xmonad to tile. The
@ -1068,9 +1010,6 @@ import XMonad.Hooks.StatusBar.PP
import XMonad.Util.EZConfig import XMonad.Util.EZConfig
import XMonad.Util.Loggers import XMonad.Util.Loggers
-- NOTE: Importing XMonad.Util.Ungrab is only necessary for versions
-- < 0.18.0! For 0.18.0 and up, this is already included in the
-- XMonad import and will generate a warning instead!
import XMonad.Util.Ungrab import XMonad.Util.Ungrab
import XMonad.Layout.Magnifier import XMonad.Layout.Magnifier
@ -1299,22 +1238,21 @@ either :)
[Basic Desktop Environment Integration]: https://wiki.haskell.org/Xmonad/Basic_Desktop_Environment_Integration [Basic Desktop Environment Integration]: https://wiki.haskell.org/Xmonad/Basic_Desktop_Environment_Integration
[Hacks]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Util-Hacks.html [Hacks]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Util-Hacks.html
[INSTALL.md]: INSTALL.md
[PP record]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Hooks-DynamicLog.html#t:PP [PP record]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Hooks-DynamicLog.html#t:PP
[INSTALL.md]: INSTALL.md
[XMonad.Config]: https://github.com/xmonad/xmonad/blob/master/src/XMonad/Config.hs [XMonad.Config]: https://github.com/xmonad/xmonad/blob/master/src/XMonad/Config.hs
[XMonad.ManageHook]: https://xmonad.github.io/xmonad-docs/xmonad/XMonad-ManageHook.html
[XMonad.Util.Loggers]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Util-Loggers.html
[XMonad.Util.EZConfig]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Util-EZConfig.html
[XMonad.Layout.Renamed]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Layout-Renamed.html
[XMonad.Layout.Magnifier]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Layout-Magnifier.html
[XMonad.Doc.Contributing]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Doc-Configuring.html [XMonad.Doc.Contributing]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Doc-Configuring.html
[XMonad.Hooks.EwmhDesktops]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Hooks-EwmhDesktops.html [XMonad.Hooks.EwmhDesktops]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Hooks-EwmhDesktops.html
[XMonad.Hooks.ManageHelpers]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Hooks-ManageHelpers.html
[XMonad.Layout.Magnifier]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Layout-Magnifier.html
[XMonad.Layout.Renamed]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Layout-Renamed.html
[XMonad.Layout.ThreeColumns]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Layout-ThreeColumns.html [XMonad.Layout.ThreeColumns]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Layout-ThreeColumns.html
[XMonad.ManageHook]: https://xmonad.github.io/xmonad-docs/xmonad/XMonad-ManageHook.html [XMonad.Hooks.ManageHelpers]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Hooks-ManageHelpers.html
[XMonad.Util.ClickableWorkspaces]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Util-ClickableWorkspaces.html [XMonad.Util.ClickableWorkspaces]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Util-ClickableWorkspaces.html
[XMonad.Util.EZConfig]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Util-EZConfig.html
[XMonad.Util.Loggers]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Util-Loggers.html
[XMonad.Util.SpawnOnce]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Util-SpawnOnce.html
[xmobar]: https://codeberg.org/xmobar/xmobar [xmobar]: https://xmobar.org/
[battery]: https://codeberg.org/xmobar/xmobar/src/branch/master/doc/plugins.org#batteryp-dirs-args-refreshrate [battery]: https://codeberg.org/xmobar/xmobar/src/branch/master/doc/plugins.org#batteryp-dirs-args-refreshrate
[xmobar.hs]: https://codeberg.org/xmobar/xmobar/src/branch/master/etc/xmobar.hs [xmobar.hs]: https://codeberg.org/xmobar/xmobar/src/branch/master/etc/xmobar.hs
[Wikipedia page]: https://en.wikipedia.org/wiki/ICAO_airport_code#Prefixes [Wikipedia page]: https://en.wikipedia.org/wiki/ICAO_airport_code#Prefixes

View File

@ -5,6 +5,11 @@ apt:
libxrandr-dev libxrandr-dev
libxss-dev libxss-dev
hlint: True
hlint-job: 9.0.2
hlint-yaml: .hlint.yaml
hlint-version: ==3.4.*
github-patches: github-patches:
.github/workflows/haskell-ci-hackage.patch .github/workflows/haskell-ci-hackage.patch
@ -12,6 +17,3 @@ raw-project
optimization: False optimization: False
package xmonad package xmonad
flags: +pedantic flags: +pedantic
-- avoid --haddock-all which overwrites *-docs.tar.gz with tests docs
haddock-components: libs

View File

@ -2,9 +2,9 @@
# See xmonad-contrib/NIX.md for an overview of module usage. # See xmonad-contrib/NIX.md for an overview of module usage.
{ {
inputs = { inputs = {
flake-utils.url = "github:numtide/flake-utils"; flake-utils.url = github:numtide/flake-utils;
git-ignore-nix.url = "github:hercules-ci/gitignore.nix/master"; git-ignore-nix.url = github:hercules-ci/gitignore.nix/master;
unstable.url = "github:NixOS/nixpkgs/nixos-unstable"; unstable.url = github:NixOS/nixpkgs/nixos-unstable;
}; };
outputs = { self, flake-utils, nixpkgs, unstable, git-ignore-nix }: outputs = { self, flake-utils, nixpkgs, unstable, git-ignore-nix }:
let let
@ -15,26 +15,17 @@
else [ "haskell" "packages" compiler ] else [ "haskell" "packages" compiler ]
); );
fromHOL = hol: comp: final: prev: with prev.lib; with attrsets; fromHOL = hol: comp: final: prev: with prev.lib; with attrsets;
let setAttrByPath (hpath comp)
path = hpath comp; ((getAttrFromPath (hpath comp) prev).override (old: {
root = head path;
branch = tail path;
hpkgs' = (getAttrFromPath path prev).override (old: {
overrides = composeExtensions (old.overrides or (_: _: {})) overrides = composeExtensions (old.overrides or (_: _: {}))
(hol final prev); (hol final prev);
}); }));
in {
${root} = recursiveUpdate prev.${root} (setAttrByPath branch hpkgs');
};
hoverlay = final: prev: hself: hsuper: hoverlay = final: prev: hself: hsuper:
with prev.haskell.lib.compose; { with prev.haskell.lib.compose; {
xmonad = hself.callCabal2nix "xmonad" xmonad = hself.callCabal2nix "xmonad"
(git-ignore-nix.lib.gitignoreSource ./.) { }; (git-ignore-nix.lib.gitignoreSource ./.) { };
}; };
defComp = if builtins.pathExists ./comp.nix overlay = fromHOL hoverlay { };
then import ./comp.nix
else { };
overlay = fromHOL hoverlay defComp;
overlays = [ overlay ]; overlays = [ overlay ];
nixosModule = { config, pkgs, lib, ... }: with lib; with attrsets; nixosModule = { config, pkgs, lib, ... }: with lib; with attrsets;
let let
@ -46,7 +37,7 @@
enable = mkEnableOption "flake"; enable = mkEnableOption "flake";
prefix = mkOption { prefix = mkOption {
default = null; default = null;
type = nullOr str; type = nullOr string;
example = literalExpression "\"unstable\""; example = literalExpression "\"unstable\"";
description = '' description = ''
Specify a nested alternative <literal>pkgs</literal> by attrName. Specify a nested alternative <literal>pkgs</literal> by attrName.
@ -54,7 +45,7 @@
}; };
compiler = mkOption { compiler = mkOption {
default = null; default = null;
type = nullOr str; type = nullOr string;
example = literalExpression "\"ghc922\""; example = literalExpression "\"ghc922\"";
description = '' description = ''
Which compiler to build xmonad with. Which compiler to build xmonad with.
@ -73,32 +64,12 @@
nixosModules = [ nixosModule ]; nixosModules = [ nixosModule ];
in flake-utils.lib.eachDefaultSystem (system: in flake-utils.lib.eachDefaultSystem (system:
let pkgs = import nixpkgs { inherit system overlays; }; let pkgs = import nixpkgs { inherit system overlays; };
hpkg = pkgs.lib.attrsets.getAttrFromPath (hpath defComp) pkgs;
modifyDevShell =
if builtins.pathExists ./develop.nix
then import ./develop.nix
else _: x: x;
in in
rec { rec {
devShell = hpkg.shellFor (modifyDevShell pkgs { devShell = pkgs.haskellPackages.shellFor {
packages = p: [ p.xmonad ]; packages = p: [ p.xmonad ];
}); };
defaultPackage = hpkg.xmonad; defaultPackage = pkgs.haskellPackages.xmonad;
# An auxiliary NixOS module that modernises the standard xmonad NixOS module
# and wrapper script used, replacing them with versions from unstable.
# Currently, due to the NIX_GHC --> XMONAD_GHC env var change, this is
# necessary in order for Mod-q recompilation to work out-of-the-box.
modernise =
let
xmonadModFile = "services/x11/window-managers/xmonad.nix";
unpkgs = import unstable { inherit system; };
replaceWrapper = _: _:
{ xmonad-with-packages = unpkgs.xmonad-with-packages; };
in {
disabledModules = [ xmonadModFile ];
imports = [ (unstable + "/nixos/modules/" + xmonadModFile) ];
nixpkgs.overlays = [ replaceWrapper ];
};
}) // { }) // {
inherit hoverlay overlay overlays nixosModule nixosModules; inherit hoverlay overlay overlays nixosModule nixosModules;
lib = { inherit hpath fromHOL; }; lib = { inherit hpath fromHOL; };

View File

@ -1,24 +1,10 @@
.\" Automatically generated by Pandoc 3.1.3 .\" Automatically generated by Pandoc 2.5
.\" .\"
.\" Define V font for inline verbatim, using C font in formats
.\" that render this, and otherwise B font.
.ie "\f[CB]x\f[]"x" \{\
. ftr V B
. ftr VI BI
. ftr VB B
. ftr VBI BI
.\}
.el \{\
. ftr V CR
. ftr VI CI
. ftr VB CB
. ftr VBI CBI
.\}
.TH "XMONAD" "1" "27 October 2021" "Tiling Window Manager" "" .TH "XMONAD" "1" "27 October 2021" "Tiling Window Manager" ""
.hy .hy
.SH Name .SH Name
.PP .PP
xmonad - Tiling Window Manager xmonad \- Tiling Window Manager
.SH Description .SH Description
.PP .PP
\f[I]xmonad\f[R] 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
@ -50,27 +36,27 @@ featureful window manager in less than 1200 lines of code, with an
emphasis on correctness and robustness. emphasis on correctness and robustness.
Internal properties of the window manager are checked using a Internal properties of the window manager are checked using a
combination of static guarantees provided by the type system, and combination of static guarantees provided by the type system, and
type-based automated testing. type\-based automated testing.
A benefit of this is that the code is simple to understand, and easy to A benefit of this is that the code is simple to understand, and easy to
modify. modify.
.SH Usage .SH Usage
.PP .PP
\f[I]xmonad\f[R] places each window into a \[lq]workspace\[rq]. \f[I]xmonad\f[R] places each window into a \[lq]workspace\[rq].
Each workspace can have any number of windows, which you can cycle Each workspace can have any number of windows, which you can cycle
though with mod-j and mod-k. though with mod\-j and mod\-k.
Windows are either displayed full screen, tiled horizontally, or tiled Windows are either displayed full screen, tiled horizontally, or tiled
vertically. vertically.
You can toggle the layout mode with mod-space, which will cycle through You can toggle the layout mode with mod\-space, which will cycle through
the available modes. the available modes.
.PP .PP
You can switch to workspace N with mod-N. You can switch to workspace N with mod\-N.
For example, to switch to workspace 5, you would press mod-5. For example, to switch to workspace 5, you would press mod\-5.
Similarly, you can move the current window to another workspace with Similarly, you can move the current window to another workspace with
mod-shift-N. mod\-shift\-N.
.PP .PP
When running with multiple monitors (Xinerama), each screen has exactly When running with multiple monitors (Xinerama), each screen has exactly
1 workspace visible. 1 workspace visible.
mod-{w,e,r} switch the focus between screens, while shift-mod-{w,e,r} mod\-{w,e,r} switch the focus between screens, while shift\-mod\-{w,e,r}
move the current window to that screen. move the current window to that screen.
When \f[I]xmonad\f[R] starts, workspace 1 is on screen 1, workspace 2 is When \f[I]xmonad\f[R] starts, workspace 1 is on screen 1, workspace 2 is
on screen 2, etc. on screen 2, etc.
@ -81,115 +67,115 @@ and visible workspaces are swapped.
xmonad has several flags which you may pass to the executable. xmonad has several flags which you may pass to the executable.
These flags are: These flags are:
.TP .TP
\[en]recompile .B \[en]recompile
Recompiles your \f[I]xmonad.hs\f[R] configuration Recompiles your \f[I]xmonad.hs\f[R] configuration
.TP .TP
\[en]restart .B \[en]restart
Causes the currently running \f[I]xmonad\f[R] process to restart Causes the currently running \f[I]xmonad\f[R] process to restart
.TP .TP
\[en]replace .B \[en]replace
Replace the current window manager with xmonad Replace the current window manager with xmonad
.TP .TP
\[en]version .B \[en]version
Display version of \f[I]xmonad\f[R] Display version of \f[I]xmonad\f[R]
.TP .TP
\[en]verbose-version .B \[en]verbose\-version
Display detailed version of \f[I]xmonad\f[R] Display detailed version of \f[I]xmonad\f[R]
.SS Default keyboard bindings .SS Default keyboard bindings
.TP .TP
mod-shift-return .B mod\-shift\-return
Launch terminal Launch terminal
.TP .TP
mod-p .B mod\-p
Launch dmenu Launch dmenu
.TP .TP
mod-shift-p .B mod\-shift\-p
Launch gmrun Launch gmrun
.TP .TP
mod-shift-c .B mod\-shift\-c
Close the focused window Close the focused window
.TP .TP
mod-space .B mod\-space
Rotate through the available layout algorithms Rotate through the available layout algorithms
.TP .TP
mod-shift-space .B mod\-shift\-space
Reset the layouts on the current workspace to default Reset the layouts on the current workspace to default
.TP .TP
mod-n .B mod\-n
Resize viewed windows to the correct size Resize viewed windows to the correct size
.TP .TP
mod-tab .B mod\-tab
Move focus to the next window Move focus to the next window
.TP .TP
mod-shift-tab .B mod\-shift\-tab
Move focus to the previous window Move focus to the previous window
.TP .TP
mod-j .B mod\-j
Move focus to the next window Move focus to the next window
.TP .TP
mod-k .B mod\-k
Move focus to the previous window Move focus to the previous window
.TP .TP
mod-m .B mod\-m
Move focus to the master window Move focus to the master window
.TP .TP
mod-return .B mod\-return
Swap the focused window and the master window Swap the focused window and the master window
.TP .TP
mod-shift-j .B mod\-shift\-j
Swap the focused window with the next window Swap the focused window with the next window
.TP .TP
mod-shift-k .B mod\-shift\-k
Swap the focused window with the previous window Swap the focused window with the previous window
.TP .TP
mod-h .B mod\-h
Shrink the master area Shrink the master area
.TP .TP
mod-l .B mod\-l
Expand the master area Expand the master area
.TP .TP
mod-t .B mod\-t
Push window back into tiling Push window back into tiling
.TP .TP
mod-comma .B mod\-comma
Increment the number of windows in the master area Increment the number of windows in the master area
.TP .TP
mod-period .B mod\-period
Deincrement the number of windows in the master area Deincrement the number of windows in the master area
.TP .TP
mod-shift-q .B mod\-shift\-q
Quit xmonad Quit xmonad
.TP .TP
mod-q .B mod\-q
Restart xmonad Restart xmonad
.TP .TP
mod-shift-slash .B mod\-shift\-slash
Run xmessage with a summary of the default keybindings (useful for Run xmessage with a summary of the default keybindings (useful for
beginners) beginners)
.TP .TP
mod-question .B mod\-question
Run xmessage with a summary of the default keybindings (useful for Run xmessage with a summary of the default keybindings (useful for
beginners) beginners)
.TP .TP
mod-[1..9] .B mod\-[1..9]
Switch to workspace N Switch to workspace N
.TP .TP
mod-shift-[1..9] .B mod\-shift\-[1..9]
Move client to workspace N Move client to workspace N
.TP .TP
mod-{w,e,r} .B mod\-{w,e,r}
Switch to physical/Xinerama screens 1, 2, or 3 Switch to physical/Xinerama screens 1, 2, or 3
.TP .TP
mod-shift-{w,e,r} .B mod\-shift\-{w,e,r}
Move client to screen 1, 2, or 3 Move client to screen 1, 2, or 3
.TP .TP
mod-button1 .B mod\-button1
Set the window to floating mode and move by dragging Set the window to floating mode and move by dragging
.TP .TP
mod-button2 .B mod\-button2
Raise the window to the top of the stack Raise the window to the top of the stack
.TP .TP
mod-button3 .B mod\-button3
Set the window to floating mode and resize by dragging Set the window to floating mode and resize by dragging
.SH Examples .SH Examples
.PP .PP
@ -202,26 +188,27 @@ exec xmonad
.SH Customization .SH Customization
.PP .PP
xmonad is customized in your \f[I]xmonad.hs\f[R], and then restarted xmonad is customized in your \f[I]xmonad.hs\f[R], and then restarted
with mod-q. with mod\-q.
You can choose where your configuration file lives by You can choose where your configuration file lives by
.IP "1." 3 .IP "1." 3
Setting \f[V]XMONAD_DATA_DIR,\f[R] \f[V]XMONAD_CONFIG_DIR\f[R], and Setting \f[C]XMONAD_DATA_DIR,\f[R] \f[C]XMONAD_CONFIG_DIR\f[R], and
\f[V]XMONAD_CACHE_DIR\f[R]; \f[I]xmonad.hs\f[R] is then expected to be \f[C]XMONAD_CACHE_DIR\f[R]; \f[I]xmonad.hs\f[R] is then expected to be
in \f[V]XMONAD_CONFIG_DIR\f[R]. in \f[C]XMONAD_CONFIG_DIR\f[R].
.IP "2." 3 .IP "2." 3
Creating \f[I]xmonad.hs\f[R] in \f[I]\[ti]/.xmonad\f[R]. Creating \f[I]xmonad.hs\f[R] in \f[I]\[ti]/.xmonad\f[R].
.IP "3." 3 .IP "3." 3
Creating \f[I]xmonad.hs\f[R] in \f[V]XDG_CONFIG_HOME\f[R]. Creating \f[I]xmonad.hs\f[R] in \f[C]XDG_CONFIG_HOME\f[R].
Note that, in this case, xmonad will use \f[V]XDG_DATA_HOME\f[R] and Note that, in this case, xmonad will use \f[C]XDG_DATA_HOME\f[R] and
\f[V]XDG_CACHE_HOME\f[R] for its data and cache directory respectively. \f[C]XDG_CACHE_HOME\f[R] for its data and cache directory respectively.
.PP .PP
You can find many extensions to the core feature set in the xmonad- You can find many extensions to the core feature set in the xmonad\-
contrib package, available through your package manager or from contrib package, available through your package manager or from
xmonad.org (https://xmonad.org). xmonad.org (https://xmonad.org).
.SS Modular Configuration .SS Modular Configuration
.PP .PP
As of \f[I]xmonad-0.9\f[R], any additional Haskell modules may be placed As of \f[I]xmonad\-0.9\f[R], any additional Haskell modules may be
in \f[I]\[ti]/.xmonad/lib/\f[R] are available in GHC\[cq]s searchpath. placed in \f[I]\[ti]/.xmonad/lib/\f[R] are available in GHC\[cq]s
searchpath.
Hierarchical modules are supported: for example, the file Hierarchical modules are supported: for example, the file
\f[I]\[ti]/.xmonad/lib/XMonad/Stack/MyAdditions.hs\f[R] could contain: \f[I]\[ti]/.xmonad/lib/XMonad/Stack/MyAdditions.hs\f[R] could contain:
.IP .IP
@ -233,7 +220,7 @@ module XMonad.Stack.MyAdditions (function1) where
.fi .fi
.PP .PP
Your xmonad.hs may then import XMonad.Stack.MyAdditions as if that Your xmonad.hs may then import XMonad.Stack.MyAdditions as if that
module was contained within xmonad or xmonad-contrib. module was contained within xmonad or xmonad\-contrib.
.SH Bugs .SH Bugs
.PP .PP
Probably. Probably.

View File

@ -7,227 +7,74 @@
<meta name="author" content="" /> <meta name="author" content="" />
<meta name="dcterms.date" content="2021-10-27" /> <meta name="dcterms.date" content="2021-10-27" />
<title>XMONAD(1) Tiling Window Manager</title> <title>XMONAD(1) Tiling Window Manager</title>
<style> <style type="text/css">
html { code{white-space: pre-wrap;}
color: #1a1a1a; span.smallcaps{font-variant: small-caps;}
background-color: #fdfdfd; span.underline{text-decoration: underline;}
} div.column{display: inline-block; vertical-align: top; width: 50%;}
body { </style>
margin: 0 auto; <style type="text/css">
max-width: 36em; a.sourceLine { display: inline-block; line-height: 1.25; }
padding-left: 50px; a.sourceLine { pointer-events: none; color: inherit; text-decoration: inherit; }
padding-right: 50px; a.sourceLine:empty { height: 1.2em; }
padding-top: 50px; .sourceCode { overflow: visible; }
padding-bottom: 50px; code.sourceCode { white-space: pre; position: relative; }
hyphens: auto; div.sourceCode { margin: 1em 0; }
overflow-wrap: break-word; pre.sourceCode { margin: 0; }
text-rendering: optimizeLegibility; @media screen {
font-kerning: normal; div.sourceCode { overflow: auto; }
} }
@media (max-width: 600px) { @media print {
body { code.sourceCode { white-space: pre-wrap; }
font-size: 0.9em; a.sourceLine { text-indent: -1em; padding-left: 1em; }
padding: 12px; }
} pre.numberSource a.sourceLine
h1 { { position: relative; left: -4em; }
font-size: 1.8em; pre.numberSource a.sourceLine::before
} { content: attr(title);
} position: relative; left: -1em; text-align: right; vertical-align: baseline;
@media print { border: none; pointer-events: all; display: inline-block;
html { -webkit-touch-callout: none; -webkit-user-select: none;
background-color: white; -khtml-user-select: none; -moz-user-select: none;
} -ms-user-select: none; user-select: none;
body { padding: 0 4px; width: 4em;
background-color: transparent; color: #aaaaaa;
color: black; }
font-size: 12pt; pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
} div.sourceCode
p, h2, h3 { { }
orphans: 3; @media screen {
widows: 3; a.sourceLine::before { text-decoration: underline; }
} }
h2, h3, h4 { code span.al { color: #ff0000; font-weight: bold; } /* Alert */
page-break-after: avoid; code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
} code span.at { color: #7d9029; } /* Attribute */
} code span.bn { color: #40a070; } /* BaseN */
p { code span.bu { } /* BuiltIn */
margin: 1em 0; code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
} code span.ch { color: #4070a0; } /* Char */
a { code span.cn { color: #880000; } /* Constant */
color: #1a1a1a; code span.co { color: #60a0b0; font-style: italic; } /* Comment */
} code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
a:visited { code span.do { color: #ba2121; font-style: italic; } /* Documentation */
color: #1a1a1a; code span.dt { color: #902000; } /* DataType */
} code span.dv { color: #40a070; } /* DecVal */
img { code span.er { color: #ff0000; font-weight: bold; } /* Error */
max-width: 100%; code span.ex { } /* Extension */
} code span.fl { color: #40a070; } /* Float */
h1, h2, h3, h4, h5, h6 { code span.fu { color: #06287e; } /* Function */
margin-top: 1.4em; code span.im { } /* Import */
} code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
h5, h6 { code span.kw { color: #007020; font-weight: bold; } /* Keyword */
font-size: 1em; code span.op { color: #666666; } /* Operator */
font-style: italic; code span.ot { color: #007020; } /* Other */
} code span.pp { color: #bc7a00; } /* Preprocessor */
h6 { code span.sc { color: #4070a0; } /* SpecialChar */
font-weight: normal; code span.ss { color: #bb6688; } /* SpecialString */
} code span.st { color: #4070a0; } /* String */
ol, ul { code span.va { color: #19177c; } /* Variable */
padding-left: 1.7em; code span.vs { color: #4070a0; } /* VerbatimString */
margin-top: 1em; code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
}
li > ol, li > ul {
margin-top: 0;
}
blockquote {
margin: 1em 0 1em 1.7em;
padding-left: 1em;
border-left: 2px solid #e6e6e6;
color: #606060;
}
code {
font-family: Menlo, Monaco, Consolas, 'Lucida Console', monospace;
font-size: 85%;
margin: 0;
hyphens: manual;
}
pre {
margin: 1em 0;
overflow: auto;
}
pre code {
padding: 0;
overflow: visible;
overflow-wrap: normal;
}
.sourceCode {
background-color: transparent;
overflow: visible;
}
hr {
background-color: #1a1a1a;
border: none;
height: 1px;
margin: 1em 0;
}
table {
margin: 1em 0;
border-collapse: collapse;
width: 100%;
overflow-x: auto;
display: block;
font-variant-numeric: lining-nums tabular-nums;
}
table caption {
margin-bottom: 0.75em;
}
tbody {
margin-top: 0.5em;
border-top: 1px solid #1a1a1a;
border-bottom: 1px solid #1a1a1a;
}
th {
border-top: 1px solid #1a1a1a;
padding: 0.25em 0.5em 0.25em 0.5em;
}
td {
padding: 0.125em 0.5em 0.25em 0.5em;
}
header {
margin-bottom: 4em;
text-align: center;
}
#TOC li {
list-style: none;
}
#TOC ul {
padding-left: 1.3em;
}
#TOC > ul {
padding-left: 0;
}
#TOC a:not(:hover) {
text-decoration: none;
}
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
/* The extra [class] is a hack that increases specificity enough to
override a similar rule in reveal.js */
ul.task-list[class]{list-style: none;}
ul.task-list li input[type="checkbox"] {
font-size: inherit;
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
}
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
/* CSS for syntax highlighting */
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { color: #008000; } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { color: #008000; font-weight: bold; } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style> </style>
</head> </head>
<body> <body>
@ -236,255 +83,165 @@
<p class="author"></p> <p class="author"></p>
<p class="date">27 October 2021</p> <p class="date">27 October 2021</p>
</header> </header>
<nav id="TOC" role="doc-toc"> <nav id="TOC">
<ul> <ul>
<li><a href="#name" id="toc-name">Name</a></li> <li><a href="#name">Name</a></li>
<li><a href="#description" id="toc-description">Description</a></li> <li><a href="#description">Description</a></li>
<li><a href="#usage" id="toc-usage">Usage</a> <li><a href="#usage">Usage</a><ul>
<ul> <li><a href="#flags">Flags</a></li>
<li><a href="#flags" id="toc-flags">Flags</a></li> <li><a href="#default-keyboard-bindings">Default keyboard bindings</a></li>
<li><a href="#default-keyboard-bindings"
id="toc-default-keyboard-bindings">Default keyboard bindings</a></li>
</ul></li> </ul></li>
<li><a href="#examples" id="toc-examples">Examples</a></li> <li><a href="#examples">Examples</a></li>
<li><a href="#customization" id="toc-customization">Customization</a> <li><a href="#customization">Customization</a><ul>
<ul> <li><a href="#modular-configuration">Modular Configuration</a></li>
<li><a href="#modular-configuration"
id="toc-modular-configuration">Modular Configuration</a></li>
</ul></li> </ul></li>
<li><a href="#bugs" id="toc-bugs">Bugs</a></li> <li><a href="#bugs">Bugs</a></li>
</ul> </ul>
</nav> </nav>
<h1 id="name">Name</h1> <h1 id="name">Name</h1>
<p>xmonad - Tiling Window Manager</p> <p>xmonad - Tiling Window Manager</p>
<h1 id="description">Description</h1> <h1 id="description">Description</h1>
<p><em>xmonad</em> is a minimalist tiling window manager for X, written <p><em>xmonad</em> is a minimalist tiling window manager for X, written in Haskell. Windows are managed using automatic layout algorithms, which can be dynamically reconfigured. At any time windows are arranged so as to maximize the use of screen real estate. All features of the window manager are accessible purely from the keyboard: a mouse is entirely optional. <em>xmonad</em> is configured in Haskell, and custom layout algorithms may be implemented by the user in config files. A principle of <em>xmonad</em> is predictability: the user should know in advance precisely the window arrangement that will result from any action.</p>
in Haskell. Windows are managed using automatic layout algorithms, which <p>By default, <em>xmonad</em> provides three layout algorithms: tall, wide and fullscreen. In tall or wide mode, windows are tiled and arranged to prevent overlap and maximize screen use. Sets of windows are grouped together on virtual screens, and each screen retains its own layout, which may be reconfigured dynamically. Multiple physical monitors are supported via Xinerama, allowing simultaneous display of a number of screens.</p>
can be dynamically reconfigured. At any time windows are arranged so as <p>By utilizing the expressivity of a modern functional language with a rich static type system, <em>xmonad</em> provides a complete, featureful window manager in less than 1200 lines of code, with an emphasis on correctness and robustness. Internal properties of the window manager are checked using a combination of static guarantees provided by the type system, and type-based automated testing. A benefit of this is that the code is simple to understand, and easy to modify.</p>
to maximize the use of screen real estate. All features of the window
manager are accessible purely from the keyboard: a mouse is entirely
optional. <em>xmonad</em> is configured in Haskell, and custom layout
algorithms may be implemented by the user in config files. A principle
of <em>xmonad</em> is predictability: the user should know in advance
precisely the window arrangement that will result from any action.</p>
<p>By default, <em>xmonad</em> provides three layout algorithms: tall,
wide and fullscreen. In tall or wide mode, windows are tiled and
arranged to prevent overlap and maximize screen use. Sets of windows are
grouped together on virtual screens, and each screen retains its own
layout, which may be reconfigured dynamically. Multiple physical
monitors are supported via Xinerama, allowing simultaneous display of a
number of screens.</p>
<p>By utilizing the expressivity of a modern functional language with a
rich static type system, <em>xmonad</em> provides a complete, featureful
window manager in less than 1200 lines of code, with an emphasis on
correctness and robustness. Internal properties of the window manager
are checked using a combination of static guarantees provided by the
type system, and type-based automated testing. A benefit of this is that
the code is simple to understand, and easy to modify.</p>
<h1 id="usage">Usage</h1> <h1 id="usage">Usage</h1>
<p><em>xmonad</em> places each window into a “workspace”. Each workspace <p><em>xmonad</em> places each window into a “workspace”. Each workspace can have any number of windows, which you can cycle though with mod-j and mod-k. Windows are either displayed full screen, tiled horizontally, or tiled vertically. You can toggle the layout mode with mod-space, which will cycle through the available modes.</p>
can have any number of windows, which you can cycle though with mod-j <p>You can switch to workspace N with mod-N. For example, to switch to workspace 5, you would press mod-5. Similarly, you can move the current window to another workspace with mod-shift-N.</p>
and mod-k. Windows are either displayed full screen, tiled horizontally, <p>When running with multiple monitors (Xinerama), each screen has exactly 1 workspace visible. mod-{w,e,r} switch the focus between screens, while shift-mod-{w,e,r} move the current window to that screen. When <em>xmonad</em> starts, workspace 1 is on screen 1, workspace 2 is on screen 2, etc. When switching workspaces to one that is already visible, the current and visible workspaces are swapped.</p>
or tiled vertically. You can toggle the layout mode with mod-space,
which will cycle through the available modes.</p>
<p>You can switch to workspace N with mod-N. For example, to switch to
workspace 5, you would press mod-5. Similarly, you can move the current
window to another workspace with mod-shift-N.</p>
<p>When running with multiple monitors (Xinerama), each screen has
exactly 1 workspace visible. mod-{w,e,r} switch the focus between
screens, while shift-mod-{w,e,r} move the current window to that screen.
When <em>xmonad</em> starts, workspace 1 is on screen 1, workspace 2 is
on screen 2, etc. When switching workspaces to one that is already
visible, the current and visible workspaces are swapped.</p>
<h2 id="flags">Flags</h2> <h2 id="flags">Flags</h2>
<p>xmonad has several flags which you may pass to the executable. These <p>xmonad has several flags which you may pass to the executable. These flags are:</p>
flags are:</p>
<dl> <dl>
<dt>recompile</dt> <dt>recompile</dt>
<dd> <dd>Recompiles your <em>xmonad.hs</em> configuration
Recompiles your <em>xmonad.hs</em> configuration
</dd> </dd>
<dt>restart</dt> <dt>restart</dt>
<dd> <dd>Causes the currently running <em>xmonad</em> process to restart
Causes the currently running <em>xmonad</em> process to restart
</dd> </dd>
<dt>replace</dt> <dt>replace</dt>
<dd> <dd>Replace the current window manager with xmonad
Replace the current window manager with xmonad
</dd> </dd>
<dt>version</dt> <dt>version</dt>
<dd> <dd>Display version of <em>xmonad</em>
Display version of <em>xmonad</em>
</dd> </dd>
<dt>verbose-version</dt> <dt>verbose-version</dt>
<dd> <dd>Display detailed version of <em>xmonad</em>
Display detailed version of <em>xmonad</em>
</dd> </dd>
</dl> </dl>
<h2 id="default-keyboard-bindings">Default keyboard bindings</h2> <h2 id="default-keyboard-bindings">Default keyboard bindings</h2>
<dl> <dl>
<dt>mod-shift-return</dt> <dt>mod-shift-return</dt>
<dd> <dd>Launch terminal
Launch terminal
</dd> </dd>
<dt>mod-p</dt> <dt>mod-p</dt>
<dd> <dd>Launch dmenu
Launch dmenu
</dd> </dd>
<dt>mod-shift-p</dt> <dt>mod-shift-p</dt>
<dd> <dd>Launch gmrun
Launch gmrun
</dd> </dd>
<dt>mod-shift-c</dt> <dt>mod-shift-c</dt>
<dd> <dd>Close the focused window
Close the focused window
</dd> </dd>
<dt>mod-space</dt> <dt>mod-space</dt>
<dd> <dd>Rotate through the available layout algorithms
Rotate through the available layout algorithms
</dd> </dd>
<dt>mod-shift-space</dt> <dt>mod-shift-space</dt>
<dd> <dd>Reset the layouts on the current workspace to default
Reset the layouts on the current workspace to default
</dd> </dd>
<dt>mod-n</dt> <dt>mod-n</dt>
<dd> <dd>Resize viewed windows to the correct size
Resize viewed windows to the correct size
</dd> </dd>
<dt>mod-tab</dt> <dt>mod-tab</dt>
<dd> <dd>Move focus to the next window
Move focus to the next window
</dd> </dd>
<dt>mod-shift-tab</dt> <dt>mod-shift-tab</dt>
<dd> <dd>Move focus to the previous window
Move focus to the previous window
</dd> </dd>
<dt>mod-j</dt> <dt>mod-j</dt>
<dd> <dd>Move focus to the next window
Move focus to the next window
</dd> </dd>
<dt>mod-k</dt> <dt>mod-k</dt>
<dd> <dd>Move focus to the previous window
Move focus to the previous window
</dd> </dd>
<dt>mod-m</dt> <dt>mod-m</dt>
<dd> <dd>Move focus to the master window
Move focus to the master window
</dd> </dd>
<dt>mod-return</dt> <dt>mod-return</dt>
<dd> <dd>Swap the focused window and the master window
Swap the focused window and the master window
</dd> </dd>
<dt>mod-shift-j</dt> <dt>mod-shift-j</dt>
<dd> <dd>Swap the focused window with the next window
Swap the focused window with the next window
</dd> </dd>
<dt>mod-shift-k</dt> <dt>mod-shift-k</dt>
<dd> <dd>Swap the focused window with the previous window
Swap the focused window with the previous window
</dd> </dd>
<dt>mod-h</dt> <dt>mod-h</dt>
<dd> <dd>Shrink the master area
Shrink the master area
</dd> </dd>
<dt>mod-l</dt> <dt>mod-l</dt>
<dd> <dd>Expand the master area
Expand the master area
</dd> </dd>
<dt>mod-t</dt> <dt>mod-t</dt>
<dd> <dd>Push window back into tiling
Push window back into tiling
</dd> </dd>
<dt>mod-comma</dt> <dt>mod-comma</dt>
<dd> <dd>Increment the number of windows in the master area
Increment the number of windows in the master area
</dd> </dd>
<dt>mod-period</dt> <dt>mod-period</dt>
<dd> <dd>Deincrement the number of windows in the master area
Deincrement the number of windows in the master area
</dd> </dd>
<dt>mod-shift-q</dt> <dt>mod-shift-q</dt>
<dd> <dd>Quit xmonad
Quit xmonad
</dd> </dd>
<dt>mod-q</dt> <dt>mod-q</dt>
<dd> <dd>Restart xmonad
Restart xmonad
</dd> </dd>
<dt>mod-shift-slash</dt> <dt>mod-shift-slash</dt>
<dd> <dd>Run xmessage with a summary of the default keybindings (useful for beginners)
Run xmessage with a summary of the default keybindings (useful for
beginners)
</dd> </dd>
<dt>mod-question</dt> <dt>mod-question</dt>
<dd> <dd>Run xmessage with a summary of the default keybindings (useful for beginners)
Run xmessage with a summary of the default keybindings (useful for
beginners)
</dd> </dd>
<dt>mod-[1..9]</dt> <dt>mod-[1..9]</dt>
<dd> <dd>Switch to workspace N
Switch to workspace N
</dd> </dd>
<dt>mod-shift-[1..9]</dt> <dt>mod-shift-[1..9]</dt>
<dd> <dd>Move client to workspace N
Move client to workspace N
</dd> </dd>
<dt>mod-{w,e,r}</dt> <dt>mod-{w,e,r}</dt>
<dd> <dd>Switch to physical/Xinerama screens 1, 2, or 3
Switch to physical/Xinerama screens 1, 2, or 3
</dd> </dd>
<dt>mod-shift-{w,e,r}</dt> <dt>mod-shift-{w,e,r}</dt>
<dd> <dd>Move client to screen 1, 2, or 3
Move client to screen 1, 2, or 3
</dd> </dd>
<dt>mod-button1</dt> <dt>mod-button1</dt>
<dd> <dd>Set the window to floating mode and move by dragging
Set the window to floating mode and move by dragging
</dd> </dd>
<dt>mod-button2</dt> <dt>mod-button2</dt>
<dd> <dd>Raise the window to the top of the stack
Raise the window to the top of the stack
</dd> </dd>
<dt>mod-button3</dt> <dt>mod-button3</dt>
<dd> <dd>Set the window to floating mode and resize by dragging
Set the window to floating mode and resize by dragging
</dd> </dd>
</dl> </dl>
<h1 id="examples">Examples</h1> <h1 id="examples">Examples</h1>
<p>To use xmonad as your window manager add to your <em>~/.xinitrc</em> <p>To use xmonad as your window manager add to your <em>~/.xinitrc</em> file:</p>
file:</p>
<blockquote> <blockquote>
<p>exec xmonad</p> <p>exec xmonad</p>
</blockquote> </blockquote>
<h1 id="customization">Customization</h1> <h1 id="customization">Customization</h1>
<p>xmonad is customized in your <em>xmonad.hs</em>, and then restarted <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>
with mod-q. You can choose where your configuration file lives by</p>
<ol type="1"> <ol type="1">
<li>Setting <code>XMONAD_DATA_DIR,</code> <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>
<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 <em>~/.xmonad</em>.</li>
<li>Creating <em>xmonad.hs</em> in <code>XDG_CONFIG_HOME</code>. Note <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>
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> </ol>
<p>You can find many extensions to the core feature set in the xmonad- <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>
contrib package, available through your package manager or from <a
href="https://xmonad.org">xmonad.org</a>.</p>
<h2 id="modular-configuration">Modular Configuration</h2> <h2 id="modular-configuration">Modular Configuration</h2>
<p>As of <em>xmonad-0.9</em>, any additional Haskell modules may be <p>As of <em>xmonad-0.9</em>, any additional Haskell modules may be placed in <em>~/.xmonad/lib/</em> are available in GHCs searchpath. Hierarchical modules are supported: for example, the file <em>~/.xmonad/lib/XMonad/Stack/MyAdditions.hs</em> could contain:</p>
placed in <em>~/.xmonad/lib/</em> are available in GHCs searchpath. <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>
Hierarchical modules are supported: for example, the file <a class="sourceLine" id="cb1-2" title="2"> function1 <span class="ot">=</span> <span class="fu">error</span> <span class="st">&quot;function1: Not implemented yet!&quot;</span></a></code></pre></div>
<em>~/.xmonad/lib/XMonad/Stack/MyAdditions.hs</em> could contain:</p> <p>Your xmonad.hs may then import XMonad.Stack.MyAdditions as if that module was contained within xmonad or xmonad-contrib.</p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">module</span> <span class="dt">XMonad.Stack.MyAdditions</span> (function1) <span class="kw">where</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> function1 <span class="ot">=</span> <span class="fu">error</span> <span class="st">&quot;function1: Not implemented yet!&quot;</span></span></code></pre></div>
<p>Your xmonad.hs may then import XMonad.Stack.MyAdditions as if that
module was contained within xmonad or xmonad-contrib.</p>
<h1 id="bugs">Bugs</h1> <h1 id="bugs">Bugs</h1>
<p>Probably. If you find any, please report them to the <a <p>Probably. If you find any, please report them to the <a href="https://github.com/xmonad/xmonad/issues">bugtracker</a></p>
href="https://github.com/xmonad/xmonad/issues">bugtracker</a></p>
</body> </body>
</html> </html>

View File

@ -1,6 +1,5 @@
{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-orphans #-}
{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- | -- |
-- Module : XMonad.Config -- Module : XMonad.Config

View File

@ -1,4 +1,3 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-}
@ -7,8 +6,6 @@
{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE ViewPatterns #-}
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- | -- |
@ -34,7 +31,7 @@ module XMonad.Core (
StateExtension(..), ExtensionClass(..), ConfExtension(..), StateExtension(..), ExtensionClass(..), ConfExtension(..),
runX, catchX, userCode, userCodeDef, io, catchIO, installSignalHandlers, uninstallSignalHandlers, runX, catchX, userCode, userCodeDef, io, catchIO, installSignalHandlers, uninstallSignalHandlers,
withDisplay, withWindowSet, isRoot, runOnWorkspaces, withDisplay, withWindowSet, isRoot, runOnWorkspaces,
getAtom, spawn, spawnPID, xfork, xmessage, recompile, trace, whenJust, whenX, ifM, getAtom, spawn, spawnPID, xfork, xmessage, recompile, trace, whenJust, whenX,
getXMonadDir, getXMonadCacheDir, getXMonadDataDir, stateFileName, binFileName, getXMonadDir, getXMonadCacheDir, getXMonadDataDir, stateFileName, binFileName,
atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, atom_WM_TAKE_FOCUS, withWindowAttributes, atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, atom_WM_TAKE_FOCUS, withWindowAttributes,
ManageHook, Query(..), runQuery, Directories'(..), Directories, getDirectories, ManageHook, Query(..), runQuery, Directories'(..), Directories, getDirectories,
@ -47,17 +44,14 @@ import Control.Exception (fromException, try, bracket_, throw, finally, SomeExce
import qualified Control.Exception as E import qualified Control.Exception as E
import Control.Applicative ((<|>), empty) import Control.Applicative ((<|>), empty)
import Control.Monad.Fail import Control.Monad.Fail
import Control.Monad.Fix (fix)
import Control.Monad.State import Control.Monad.State
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad (filterM, guard, void, when) import Control.Monad (void)
import Data.Char (isSpace)
import Data.Semigroup import Data.Semigroup
import Data.Traversable (for) import Data.Traversable (for)
import Data.Time.Clock (UTCTime) import Data.Time.Clock (UTCTime)
import Data.Default.Class import Data.Default.Class
import System.Environment (lookupEnv) import System.Environment (lookupEnv)
import Data.List (isInfixOf, intercalate, (\\))
import System.FilePath import System.FilePath
import System.IO import System.IO
import System.Info import System.Info
@ -72,8 +66,8 @@ import System.Exit
import Graphics.X11.Xlib import Graphics.X11.Xlib
import Graphics.X11.Xlib.Extras (getWindowAttributes, WindowAttributes, Event) import Graphics.X11.Xlib.Extras (getWindowAttributes, WindowAttributes, Event)
import Data.Typeable import Data.Typeable
import Data.List (isInfixOf, (\\))
import Data.Maybe (isJust,fromMaybe) import Data.Maybe (isJust,fromMaybe)
import Data.Monoid (Ap(..))
import qualified Data.Map as M import qualified Data.Map as M
import qualified Data.Set as S import qualified Data.Set as S
@ -169,7 +163,12 @@ newtype ScreenDetail = SD { screenRect :: Rectangle }
-- --
newtype X a = X (ReaderT XConf (StateT XState IO) a) newtype X a = X (ReaderT XConf (StateT XState IO) a)
deriving (Functor, Applicative, Monad, MonadFail, MonadIO, MonadState XState, MonadReader XConf) deriving (Functor, Applicative, Monad, MonadFail, MonadIO, MonadState XState, MonadReader XConf)
deriving (Semigroup, Monoid) via Ap X a
instance Semigroup a => Semigroup (X a) where
(<>) = liftM2 (<>)
instance (Monoid a) => Monoid (X a) where
mempty = pure mempty
instance Default a => Default (X a) where instance Default a => Default (X a) where
def = return def def = return def
@ -177,11 +176,16 @@ instance Default a => Default (X a) where
type ManageHook = Query (Endo WindowSet) type ManageHook = Query (Endo WindowSet)
newtype Query a = Query (ReaderT Window X a) newtype Query a = Query (ReaderT Window X a)
deriving (Functor, Applicative, Monad, MonadReader Window, MonadIO) deriving (Functor, Applicative, Monad, MonadReader Window, MonadIO)
deriving (Semigroup, Monoid) via Ap Query a
runQuery :: Query a -> Window -> X a runQuery :: Query a -> Window -> X a
runQuery (Query m) = runReaderT m runQuery (Query m) = runReaderT m
instance Semigroup a => Semigroup (Query a) where
(<>) = liftM2 (<>)
instance Monoid a => Monoid (Query a) where
mempty = pure mempty
instance Default a => Default (Query a) where instance Default a => Default (Query a) where
def = return def def = return def
@ -417,13 +421,9 @@ data StateExtension =
data ConfExtension = forall a. Typeable a => ConfExtension a data ConfExtension = forall a. Typeable a => ConfExtension a
-- --------------------------------------------------------------------- -- ---------------------------------------------------------------------
-- General utilities -- | General utilities
--
-- | If-then-else lifted to a 'Monad'. -- Lift an 'IO' action into the 'X' monad
ifM :: Monad m => m Bool -> m a -> m a -> m a
ifM mb t f = mb >>= \b -> if b then t else f
-- | Lift an 'IO' action into the 'X' monad
io :: MonadIO m => IO a -> m a io :: MonadIO m => IO a -> m a
io = liftIO io = liftIO
@ -451,11 +451,7 @@ xfork x = io . forkProcess . finally nullStdin $ do
x x
where where
nullStdin = do nullStdin = do
#if MIN_VERSION_unix(2,8,0)
fd <- openFd "/dev/null" ReadOnly defaultFileFlags
#else
fd <- openFd "/dev/null" ReadOnly Nothing defaultFileFlags fd <- openFd "/dev/null" ReadOnly Nothing defaultFileFlags
#endif
dupTo fd stdInput dupTo fd stdInput
closeFd fd closeFd fd
@ -582,32 +578,21 @@ srcFileName, libFileName :: Directories -> FilePath
srcFileName Directories{ cfgDir } = cfgDir </> "xmonad.hs" srcFileName Directories{ cfgDir } = cfgDir </> "xmonad.hs"
libFileName Directories{ cfgDir } = cfgDir </> "lib" libFileName Directories{ cfgDir } = cfgDir </> "lib"
buildScriptFileName, stackYamlFileName, nixFlakeFileName, nixDefaultFileName :: Directories -> FilePath buildScriptFileName, stackYamlFileName :: Directories -> FilePath
buildScriptFileName Directories{ cfgDir } = cfgDir </> "build" buildScriptFileName Directories{ cfgDir } = cfgDir </> "build"
stackYamlFileName Directories{ cfgDir } = cfgDir </> "stack.yaml" stackYamlFileName Directories{ cfgDir } = cfgDir </> "stack.yaml"
nixFlakeFileName Directories{ cfgDir } = cfgDir </> "flake.nix"
nixDefaultFileName Directories{ cfgDir } = cfgDir </> "default.nix"
-- | Compilation method for xmonad configuration. -- | Compilation method for xmonad configuration.
data Compile data Compile = CompileGhc | CompileStackGhc FilePath | CompileScript FilePath
= CompileGhc
| CompileCabal
| CompileStackGhc FilePath
| CompileNixFlake
| CompileNixDefault
| CompileScript FilePath
deriving (Show) deriving (Show)
-- | Detect compilation method by looking for known file names in xmonad -- | Detect compilation method by looking for known file names in xmonad
-- configuration directory. -- configuration directory.
detectCompile :: Directories -> IO Compile detectCompile :: Directories -> IO Compile
detectCompile dirs = detectCompile dirs = tryScript <|> tryStack <|> useGhc
tryScript <|> tryStack <|> tryNixFlake <|> tryNixDefault <|> tryCabal <|> useGhc
where where
buildScript = buildScriptFileName dirs buildScript = buildScriptFileName dirs
stackYaml = stackYamlFileName dirs stackYaml = stackYamlFileName dirs
flakeNix = nixFlakeFileName dirs
defaultNix = nixDefaultFileName dirs
tryScript = do tryScript = do
guard =<< doesFileExist buildScript guard =<< doesFileExist buildScript
@ -621,58 +606,18 @@ detectCompile dirs =
trace $ "Suggested resolution to use it: chmod u+x " <> show buildScript trace $ "Suggested resolution to use it: chmod u+x " <> show buildScript
empty empty
tryNixFlake = do
guard =<< doesFileExist flakeNix
canonNixFlake <- canonicalizePath flakeNix
trace $ "XMonad will use nix flake at " <> show canonNixFlake <> " to recompile"
pure CompileNixFlake
tryNixDefault = do
guard =<< doesFileExist defaultNix
canonNixDefault <- canonicalizePath defaultNix
trace $ "XMonad will use nix file at " <> show canonNixDefault <> " to recompile"
pure CompileNixDefault
tryStack = do tryStack = do
guard =<< doesFileExist stackYaml guard =<< doesFileExist stackYaml
canonStackYaml <- canonicalizePath stackYaml canonStackYaml <- canonicalizePath stackYaml
trace $ "XMonad will use stack ghc --stack-yaml " <> show canonStackYaml <> " to recompile." trace $ "XMonad will use stack ghc --stack-yaml " <> show canonStackYaml <> " to recompile."
pure $ CompileStackGhc canonStackYaml pure $ CompileStackGhc canonStackYaml
tryCabal = let cwd = cfgDir dirs in listCabalFiles cwd >>= \ case
[] -> do
empty
[name] -> do
trace $ "XMonad will use " <> show name <> " to recompile."
pure CompileCabal
_ : _ : _ -> do
trace $ "XMonad will not use cabal, because there are multiple cabal files in " <> show cwd <> "."
empty
useGhc = do useGhc = do
trace $ "XMonad will use ghc to recompile, because none of " trace $ "XMonad will use ghc to recompile, because neither "
<> intercalate ", " <> show buildScript <> " nor " <> show stackYaml <> " exists."
[ show buildScript
, show stackYaml
, show flakeNix
, show defaultNix
] <> " nor a suitable .cabal file exist."
pure CompileGhc pure CompileGhc
listCabalFiles :: FilePath -> IO [FilePath] isExecutable f = E.catch (executable <$> getPermissions f) (\(SomeException _) -> return False)
listCabalFiles dir = map (dir </>) . Prelude.filter isCabalFile <$> listFiles dir
isCabalFile :: FilePath -> Bool
isCabalFile file = case splitExtension file of
(name, ".cabal") -> not (null name)
_ -> False
listFiles :: FilePath -> IO [FilePath]
listFiles dir = getDirectoryContents dir >>= filterM (doesFileExist . (dir </>))
-- | Determine whether or not the file found at the provided filepath is executable.
isExecutable :: FilePath -> IO Bool
isExecutable f = E.catch (executable <$> getPermissions f) (\(SomeException _) -> return False)
-- | Should we recompile xmonad configuration? Is it newer than the compiled -- | Should we recompile xmonad configuration? Is it newer than the compiled
-- binary? -- binary?
@ -691,15 +636,12 @@ shouldCompile dirs CompileGhc = do
cs <- prep <$> E.catch (getDirectoryContents t) (\(SomeException _) -> return []) cs <- prep <$> E.catch (getDirectoryContents t) (\(SomeException _) -> return [])
ds <- filterM doesDirectoryExist cs ds <- filterM doesDirectoryExist cs
concat . ((cs \\ ds):) <$> mapM allFiles ds concat . ((cs \\ ds):) <$> mapM allFiles ds
shouldCompile _ CompileCabal = return True
shouldCompile dirs CompileStackGhc{} = do shouldCompile dirs CompileStackGhc{} = do
stackYamlT <- getModTime (stackYamlFileName dirs) stackYamlT <- getModTime (stackYamlFileName dirs)
binT <- getModTime (binFileName dirs) binT <- getModTime (binFileName dirs)
if binT < stackYamlT if binT < stackYamlT
then True <$ trace "XMonad recompiling because some files have changed." then True <$ trace "XMonad recompiling because some files have changed."
else shouldCompile dirs CompileGhc else shouldCompile dirs CompileGhc
shouldCompile _dirs CompileNixFlake{} = True <$ trace "XMonad recompiling because flake recompilation is being used."
shouldCompile _dirs CompileNixDefault{} = True <$ trace "XMonad recompiling because nix recompilation is being used."
shouldCompile _dirs CompileScript{} = shouldCompile _dirs CompileScript{} =
True <$ trace "XMonad recompiling because a custom build script is being used." True <$ trace "XMonad recompiling because a custom build script is being used."
@ -711,31 +653,17 @@ compile :: Directories -> Compile -> IO ExitCode
compile dirs method = compile dirs method =
bracket_ uninstallSignalHandlers installSignalHandlers $ bracket_ uninstallSignalHandlers installSignalHandlers $
withFile (errFileName dirs) WriteMode $ \err -> do withFile (errFileName dirs) WriteMode $ \err -> do
let run = runProc err let run = runProc (cfgDir dirs) err
case method of case method of
CompileGhc -> do CompileGhc -> do
ghc <- fromMaybe "ghc" <$> lookupEnv "XMONAD_GHC" ghc <- fromMaybe "ghc" <$> lookupEnv "XMONAD_GHC"
run ghc ghcArgs run ghc ghcArgs
CompileCabal -> run "cabal" ["build"] .&&. copyBinary
where
copyBinary :: IO ExitCode
copyBinary = readProc err "cabal" ["-v0", "list-bin", "."] >>= \ case
Left status -> return status
Right (trim -> path) -> copyBinaryFrom path
CompileStackGhc stackYaml -> CompileStackGhc stackYaml ->
run "stack" ["build", "--silent", "--stack-yaml", stackYaml] .&&. run "stack" ["build", "--silent", "--stack-yaml", stackYaml] .&&.
run "stack" ("ghc" : "--stack-yaml" : stackYaml : "--" : ghcArgs) run "stack" ("ghc" : "--stack-yaml" : stackYaml : "--" : ghcArgs)
CompileNixFlake ->
run "nix" ["build"] >>= andCopyFromResultDir
CompileNixDefault ->
run "nix-build" [] >>= andCopyFromResultDir
CompileScript script -> CompileScript script ->
run script [binFileName dirs] run script [binFileName dirs]
where where
cwd :: FilePath
cwd = cfgDir dirs
ghcArgs :: [String]
ghcArgs = [ "--make" ghcArgs = [ "--make"
, "xmonad.hs" , "xmonad.hs"
, "-i" -- only look in @lib@ , "-i" -- only look in @lib@
@ -747,51 +675,13 @@ compile dirs method =
, "-o", binFileName dirs , "-o", binFileName dirs
] ]
andCopyFromResultDir :: ExitCode -> IO ExitCode
andCopyFromResultDir exitCode = do
if exitCode == ExitSuccess then copyFromResultDir else return exitCode
findM :: (Monad m, Foldable t) => (a -> m Bool) -> t a -> m (Maybe a)
findM p = foldr (\x -> ifM (p x) (pure $ Just x)) (pure Nothing)
catchAny :: IO a -> (SomeException -> IO a) -> IO a
catchAny = E.catch
copyFromResultDir :: IO ExitCode
copyFromResultDir = do
let binaryDirectory = cfgDir dirs </> "result" </> "bin"
binFiles <- map (binaryDirectory </>) <$> catchAny (listDirectory binaryDirectory) (\_ -> return [])
mfilepath <- findM isExecutable binFiles
case mfilepath of
Just filepath -> copyBinaryFrom filepath
Nothing -> return $ ExitFailure 1
copyBinaryFrom :: FilePath -> IO ExitCode
copyBinaryFrom filepath = copyFile filepath (binFileName dirs) >> return ExitSuccess
-- waitForProcess =<< System.Process.runProcess, but without closing the err handle -- waitForProcess =<< System.Process.runProcess, but without closing the err handle
runProc :: Handle -> String -> [String] -> IO ExitCode runProc cwd err exe args = do
runProc err exe args = do
(Nothing, Nothing, Nothing, h) <- createProcess_ "runProc" =<< mkProc err exe args
waitForProcess h
readProc :: Handle -> String -> [String] -> IO (Either ExitCode String)
readProc err exe args = do
spec <- mkProc err exe args
(Nothing, Just out, Nothing, h) <- createProcess_ "readProc" spec{ std_out = CreatePipe }
result <- hGetContents out
hPutStr err result >> hFlush err
waitForProcess h >>= \ case
ExitSuccess -> return $ Right result
status -> return $ Left status
mkProc :: Handle -> FilePath -> [FilePath] -> IO CreateProcess
mkProc err exe args = do
hPutStrLn err $ unwords $ "$" : exe : args hPutStrLn err $ unwords $ "$" : exe : args
hFlush err hFlush err
return (proc exe args){ cwd = Just cwd, std_err = UseHandle err } (_, _, _, h) <- createProcess_ "runProc" (proc exe args){ cwd = Just cwd, std_err = UseHandle err }
waitForProcess h
(.&&.) :: Monad m => m ExitCode -> m ExitCode -> m ExitCode
cmd1 .&&. cmd2 = cmd1 >>= \case cmd1 .&&. cmd2 = cmd1 >>= \case
ExitSuccess -> cmd2 ExitSuccess -> cmd2
e -> pure e e -> pure e
@ -891,6 +781,3 @@ uninstallSignalHandlers = io $ do
installHandler openEndedPipe Default Nothing installHandler openEndedPipe Default Nothing
installHandler sigCHLD Default Nothing installHandler sigCHLD Default Nothing
return () return ()
trim :: String -> String
trim = reverse . dropWhile isSpace . reverse . dropWhile isSpace

View File

@ -62,13 +62,9 @@ data Tall a = Tall { tallNMaster :: !Int -- ^ The default number o
-- a nice pure layout, lots of properties for the layout, and its messages, in Properties.hs -- a nice pure layout, lots of properties for the layout, and its messages, in Properties.hs
instance LayoutClass Tall a where instance LayoutClass Tall a where
pureLayout (Tall nmaster _ frac) r s pureLayout (Tall nmaster _ frac) r s = zip ws rs
| frac == 0 = drop nmaster layout
| frac == 1 = take nmaster layout
| otherwise = layout
where ws = W.integrate s where ws = W.integrate s
rs = tile frac r nmaster (length ws) rs = tile frac r nmaster (length ws)
layout = zip ws rs
pureMessage (Tall nmaster delta frac) m = pureMessage (Tall nmaster delta frac) m =
msum [fmap resize (fromMessage m) msum [fmap resize (fromMessage m)

View File

@ -15,18 +15,18 @@
-- --
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
module XMonad.Main (xmonad, buildLaunch, launch) where module XMonad.Main (xmonad, launch) where
import System.Locale.SetLocale import System.Locale.SetLocale
import qualified Control.Exception as E import qualified Control.Exception as E
import Data.Bits import Data.Bits
import Data.List ((\\)) import Data.List ((\\))
import Data.Foldable (traverse_) import Data.Foldable (traverse_)
import Data.Function
import qualified Data.Map as M import qualified Data.Map as M
import qualified Data.Set as S import qualified Data.Set as S
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.State import Control.Monad.State
import Control.Monad (filterM, guard, unless, void, when, forever)
import Data.Maybe (fromMaybe, isJust) import Data.Maybe (fromMaybe, isJust)
import Data.Monoid (getAll) import Data.Monoid (getAll)
@ -131,6 +131,25 @@ buildLaunch dirs = do
args <- getArgs args <- getArgs
executeFile bin False args Nothing executeFile bin False args Nothing
sendRestart :: IO ()
sendRestart = do
dpy <- openDisplay ""
rw <- rootWindow dpy $ defaultScreen dpy
xmonad_restart <- internAtom dpy "XMONAD_RESTART" False
allocaXEvent $ \e -> do
setEventType e clientMessage
setClientMessageEvent' e rw xmonad_restart 32 []
sendEvent dpy rw False structureNotifyMask e
sync dpy False
-- | a wrapper for 'replace'
sendReplace :: IO ()
sendReplace = do
dpy <- openDisplay ""
let dflt = defaultScreen dpy
rootw <- rootWindow dpy dflt
replace dpy dflt rootw
-- | Entry point into xmonad for custom builds. -- | Entry point into xmonad for custom builds.
-- --
-- This function isn't meant to be called by the typical xmonad user -- This function isn't meant to be called by the typical xmonad user
@ -248,10 +267,11 @@ launch initxmc drs = do
userCode $ startupHook initxmc userCode $ startupHook initxmc
rrData <- io $ xrrQueryExtension dpy rrData <- io $ xrrQueryExtension dpy
let rrUpdate = when (isJust rrData) . void . xrrUpdateConfiguration
-- main loop, for all you HOF/recursion fans out there. -- main loop, for all you HOF/recursion fans out there.
forever $ prehandle =<< io (nextEvent dpy e >> rrUpdate e >> getEvent e) -- forever $ prehandle =<< io (nextEvent dpy e >> rrUpdate e >> getEvent e)
-- sadly, 9.2.{1,2,3} join points mishandle the above and trash the heap (see #389)
mainLoop dpy e rrData
return () return ()
where where
@ -262,6 +282,8 @@ launch initxmc drs = do
in local (\c -> c { mousePosition = mouse, currentEvent = Just e }) (handleWithHook e) in local (\c -> c { mousePosition = mouse, currentEvent = Just e }) (handleWithHook e)
evs = [ keyPress, keyRelease, enterNotify, leaveNotify evs = [ keyPress, keyRelease, enterNotify, leaveNotify
, buttonPress, buttonRelease] , buttonPress, buttonRelease]
rrUpdate e r = when (isJust r) (void (xrrUpdateConfiguration e))
mainLoop d e r = io (nextEvent d e >> rrUpdate e r >> getEvent e) >>= prehandle >> mainLoop d e r
-- | Runs handleEventHook from the configuration and runs the default handler -- | Runs handleEventHook from the configuration and runs the default handler
@ -462,3 +484,36 @@ grabButtons = do
ems <- extraModifiers ems <- extraModifiers
ba <- asks buttonActions ba <- asks buttonActions
mapM_ (\(m,b) -> mapM_ (grab b . (m .|.)) ems) (M.keys ba) mapM_ (\(m,b) -> mapM_ (grab b . (m .|.)) ems) (M.keys ba)
-- | @replace@ to signals compliant window managers to exit.
replace :: Display -> ScreenNumber -> Window -> IO ()
replace dpy dflt rootw = do
-- check for other WM
wmSnAtom <- internAtom dpy ("WM_S" ++ show dflt) False
currentWmSnOwner <- xGetSelectionOwner dpy wmSnAtom
when (currentWmSnOwner /= 0) $ do
-- prepare to receive destroyNotify for old WM
selectInput dpy currentWmSnOwner structureNotifyMask
-- create off-screen window
netWmSnOwner <- allocaSetWindowAttributes $ \attributes -> do
set_override_redirect attributes True
set_event_mask attributes propertyChangeMask
let screen = defaultScreenOfDisplay dpy
visual = defaultVisualOfScreen screen
attrmask = cWOverrideRedirect .|. cWEventMask
createWindow dpy rootw (-100) (-100) 1 1 0 copyFromParent copyFromParent visual attrmask attributes
-- try to acquire wmSnAtom, this should signal the old WM to terminate
xSetSelectionOwner dpy wmSnAtom netWmSnOwner currentTime
-- SKIPPED: check if we acquired the selection
-- SKIPPED: send client message indicating that we are now the WM
-- wait for old WM to go away
fix $ \again -> do
evt <- allocaXEvent $ \event -> do
windowEvent dpy currentWmSnOwner structureNotifyMask event
get_EventType event
when (evt /= destroyNotify) again

View File

@ -64,8 +64,11 @@ x <&&> y = ifM x y (pure False)
(<||>) :: Monad m => m Bool -> m Bool -> m Bool (<||>) :: Monad m => m Bool -> m Bool -> m Bool
x <||> y = ifM x (pure True) y x <||> y = ifM x (pure True) y
-- | Return the window title; i.e., the string returned by @_NET_WM_NAME@, -- | If-then-else lifted to a 'Monad'.
-- or failing that, the string returned by @WM_NAME@. 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 title :: Query String
title = ask >>= \w -> liftX $ do title = ask >>= \w -> liftX $ do
d <- asks display d <- asks display
@ -74,7 +77,7 @@ title = ask >>= \w -> liftX $ do
(internAtom d "_NET_WM_NAME" False >>= getTextProperty d w) (internAtom d "_NET_WM_NAME" False >>= getTextProperty d w)
`E.catch` \(SomeException _) -> getTextProperty d w wM_NAME `E.catch` \(SomeException _) -> getTextProperty d w wM_NAME
extract prop = do l <- wcTextPropertyToTextList d prop extract prop = do l <- wcTextPropertyToTextList d prop
return $ fromMaybe "" $ listToMaybe l return $ if null l then "" else head l
io $ bracket getProp (xFree . tp_value) extract `E.catch` \(SomeException _) -> return "" io $ bracket getProp (xFree . tp_value) extract `E.catch` \(SomeException _) -> return ""
-- | Return the application name; i.e., the /first/ string returned by -- | Return the application name; i.e., the /first/ string returned by
@ -92,9 +95,7 @@ className :: Query String
className = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resClass $ io $ getClassHint d w) className = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resClass $ io $ getClassHint d w)
-- | A query that can return an arbitrary X property of type 'String', -- | A query that can return an arbitrary X property of type 'String',
-- identified by name. Works for ASCII strings only. For the properties -- identified by name.
-- @_NET_WM_NAME@/@WM_NAME@ and @WM_CLASS@ the specialised variants 'title'
-- and 'appName'/'className' are preferred.
stringProperty :: String -> Query String stringProperty :: String -> Query String
stringProperty p = ask >>= (\w -> liftX $ withDisplay $ \d -> fromMaybe "" <$> getStringProperty d w p) stringProperty p = ask >>= (\w -> liftX $ withDisplay $ \d -> fromMaybe "" <$> getStringProperty d w p)

View File

@ -33,11 +33,10 @@ module XMonad.Operations (
-- * Keyboard and Mouse -- * Keyboard and Mouse
cleanMask, extraModifiers, cleanMask, extraModifiers,
mouseDrag, mouseMoveWindow, mouseResizeWindow, mouseDrag, mouseMoveWindow, mouseResizeWindow,
setButtonGrab, setFocusX, cacheNumlockMask, mkGrabs, unGrab, setButtonGrab, setFocusX, cacheNumlockMask, mkGrabs,
-- * Messages -- * Messages
sendMessage, broadcastMessage, sendMessageWithNoRefresh, sendMessage, broadcastMessage, sendMessageWithNoRefresh,
sendRestart, sendReplace,
-- * Save and Restore State -- * Save and Restore State
StateFile (..), writeStateToFile, readStateFile, restart, StateFile (..), writeStateToFile, readStateFile, restart,
@ -71,10 +70,9 @@ import qualified Data.Map as M
import qualified Data.Set as S import qualified Data.Set as S
import Control.Arrow (second) import Control.Arrow (second)
import Control.Monad.Fix (fix)
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.State import Control.Monad.State
import Control.Monad (forM, forM_, guard, join, unless, void, when) import Control.Monad (void)
import qualified Control.Exception as C import qualified Control.Exception as C
import System.IO import System.IO
@ -197,7 +195,6 @@ windows f = do
let m = W.floating ws let m = W.floating ws
flt = [(fw, scaleRationalRect viewrect r) flt = [(fw, scaleRationalRect viewrect r)
| fw <- filter (`M.member` m) (W.index this) | fw <- filter (`M.member` m) (W.index this)
, fw `notElem` vis
, Just r <- [M.lookup fw m]] , Just r <- [M.lookup fw m]]
vs = flt ++ rs vs = flt ++ rs
@ -475,28 +472,6 @@ mkGrabs ks = withDisplay $ \dpy -> do
, extraMod <- extraMods , extraMod <- extraMods
] ]
-- | Release XMonad's keyboard grab, so other grabbers can do their thing.
--
-- Start a keyboard action with this if it is going to run something
-- that needs to do a keyboard, pointer, or server grab. For example,
--
-- > , ((modm .|. controlMask, xK_p), unGrab >> spawn "scrot")
--
-- (Other examples are certain screen lockers and "gksu".)
-- This avoids needing to insert a pause/sleep before running the
-- command.
--
-- XMonad retains the keyboard grab during key actions because if they
-- use a submap, they need the keyboard to be grabbed, and if they had
-- to assert their own grab then the asynchronous nature of X11 allows
-- race conditions between XMonad, other clients, and the X server that
-- would cause keys to sometimes be "leaked" to the focused window.
unGrab :: X ()
unGrab = withDisplay $ \d -> io $ do
ungrabKeyboard d currentTime
ungrabPointer d currentTime
sync d False
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- Message handling -- Message handling
@ -543,59 +518,6 @@ setLayout l = do
handleMessage (W.layout ws) (SomeMessage ReleaseResources) handleMessage (W.layout ws) (SomeMessage ReleaseResources)
windows $ const $ ss{ W.current = c{ W.workspace = ws{ W.layout = l } } } windows $ const $ ss{ W.current = c{ W.workspace = ws{ W.layout = l } } }
-- | Signal xmonad to restart itself.
sendRestart :: IO ()
sendRestart = do
dpy <- openDisplay ""
rw <- rootWindow dpy $ defaultScreen dpy
xmonad_restart <- internAtom dpy "XMONAD_RESTART" False
allocaXEvent $ \e -> do
setEventType e clientMessage
setClientMessageEvent' e rw xmonad_restart 32 []
sendEvent dpy rw False structureNotifyMask e
sync dpy False
-- | Signal compliant window managers to exit.
sendReplace :: IO ()
sendReplace = do
dpy <- openDisplay ""
let dflt = defaultScreen dpy
rootw <- rootWindow dpy dflt
replace dpy dflt rootw
-- | Signal compliant window managers to exit.
replace :: Display -> ScreenNumber -> Window -> IO ()
replace dpy dflt rootw = do
-- check for other WM
wmSnAtom <- internAtom dpy ("WM_S" ++ show dflt) False
currentWmSnOwner <- xGetSelectionOwner dpy wmSnAtom
when (currentWmSnOwner /= 0) $ do
-- prepare to receive destroyNotify for old WM
selectInput dpy currentWmSnOwner structureNotifyMask
-- create off-screen window
netWmSnOwner <- allocaSetWindowAttributes $ \attributes -> do
set_override_redirect attributes True
set_event_mask attributes propertyChangeMask
let screen = defaultScreenOfDisplay dpy
visual = defaultVisualOfScreen screen
attrmask = cWOverrideRedirect .|. cWEventMask
createWindow dpy rootw (-100) (-100) 1 1 0 copyFromParent copyFromParent visual attrmask attributes
-- try to acquire wmSnAtom, this should signal the old WM to terminate
xSetSelectionOwner dpy wmSnAtom netWmSnOwner currentTime
-- SKIPPED: check if we acquired the selection
-- SKIPPED: send client message indicating that we are now the WM
-- wait for old WM to go away
fix $ \again -> do
evt <- allocaXEvent $ \event -> do
windowEvent dpy currentWmSnOwner structureNotifyMask event
get_EventType event
when (evt /= destroyNotify) again
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- Utilities -- Utilities
@ -726,7 +648,6 @@ floatLocation w =
where go = withDisplay $ \d -> do where go = withDisplay $ \d -> do
ws <- gets windowset ws <- gets windowset
sh <- io $ getWMNormalHints d w
wa <- io $ getWindowAttributes d w wa <- io $ getWindowAttributes d w
let bw = (fromIntegral . wa_border_width) wa let bw = (fromIntegral . wa_border_width) wa
point_sc <- pointScreen (fi $ wa_x wa) (fi $ wa_y wa) point_sc <- pointScreen (fi $ wa_x wa) (fi $ wa_y wa)
@ -741,14 +662,13 @@ floatLocation w =
sr = screenRect . W.screenDetail $ sc sr = screenRect . W.screenDetail $ sc
x = (fi (wa_x wa) - fi (rect_x sr)) % fi (rect_width sr) 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) y = (fi (wa_y wa) - fi (rect_y sr)) % fi (rect_height sr)
(width, height) = applySizeHintsContents sh (wa_width wa, wa_height wa) width = fi (wa_width wa + bw*2) % fi (rect_width sr)
rwidth = fi (width + bw*2) % fi (rect_width sr) height = fi (wa_height wa + bw*2) % fi (rect_height sr)
rheight = fi (height + bw*2) % fi (rect_height sr)
-- adjust x/y of unmanaged windows if we ignored or didn't get pointScreen, -- adjust x/y of unmanaged windows if we ignored or didn't get pointScreen,
-- it might be out of bounds otherwise -- it might be out of bounds otherwise
rr = if managed || point_sc `sr_eq` Just sc rr = if managed || point_sc `sr_eq` Just sc
then W.RationalRect x y rwidth rheight then W.RationalRect x y width height
else W.RationalRect (0.5 - rwidth/2) (0.5 - rheight/2) rwidth rheight else W.RationalRect (0.5 - width/2) (0.5 - height/2) width height
return (W.screen sc, rr) return (W.screen sc, rr)

View File

@ -106,10 +106,7 @@ import qualified Data.Map as M (Map,insert,delete,empty)
-- --
-- <https://dspace.library.uu.nl/handle/1874/2532 R. Hinze and J. Jeuring, Functional Pearl: Weaving a Web> -- <https://dspace.library.uu.nl/handle/1874/2532 R. Hinze and J. Jeuring, Functional Pearl: Weaving a Web>
-- --
-- and -- and Conor McBride's zipper differentiation paper.
--
-- <http://strictlypositive.org/diff.pdf Conor McBride, The Derivative of a Regular Type is its Type of One-Hole Contexts>.
--
-- Another good reference is: <https://wiki.haskell.org/Zipper The Zipper, Haskell wikibook> -- Another good reference is: <https://wiki.haskell.org/Zipper The Zipper, Haskell wikibook>
-- $xinerama -- $xinerama

View File

@ -1,4 +1,4 @@
resolver: lts-21.12 resolver: lts-19.6
packages: packages:
- ./ - ./
@ -6,6 +6,13 @@ packages:
extra-deps: extra-deps:
- X11-1.10 - 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: nix:
packages: packages:
- zlib - zlib

View File

@ -166,16 +166,11 @@ tests =
-- tall layout -- tall layout
,("tile 1 window fullsize", property prop_tile_fullscreen) ,("tile 1 window fullsize", property prop_tile_fullscreen)
,("tile max ratio", property prop_tile_max_ratio)
,("tile min ratio", property prop_tile_min_ratio)
,("tiles never overlap", property prop_tile_non_overlap) ,("tiles never overlap", property prop_tile_non_overlap)
,("split horizontal", property prop_split_horizontal) ,("split horizontal", property prop_split_horizontal)
,("split vertical", property prop_split_vertical) ,("split vertical", property prop_split_vertical)
,("pure layout tall", property prop_purelayout_tall) ,("pure layout tall", property prop_purelayout_tall)
{- Following two test cases should be automatically generated by QuickCheck ideally, but it fails. -}
,("pure layout tall: ratio = 0", property (\n d rect -> prop_purelayout_tall n d 0 rect))
,("pure layout tall: ratio = 1", property (\n d rect -> prop_purelayout_tall n d 1 rect))
,("send shrink tall", property prop_shrink_tall) ,("send shrink tall", property prop_shrink_tall)
,("send expand tall", property prop_expand_tall) ,("send expand tall", property prop_expand_tall)
,("send incmaster tall", property prop_incmaster_tall) ,("send incmaster tall", property prop_incmaster_tall)

View File

@ -11,9 +11,8 @@ import XMonad.Layout
import Graphics.X11.Xlib.Types (Rectangle(..)) import Graphics.X11.Xlib.Types (Rectangle(..))
import Control.Applicative
import Data.List (sort)
import Data.Maybe import Data.Maybe
import Data.List (sort)
import Data.Ratio import Data.Ratio
------------------------------------------------------------------------ ------------------------------------------------------------------------
@ -28,22 +27,6 @@ prop_tile_non_overlap rect windows nmaster = noOverlaps (tile pct rect nmaster w
where _ = rect :: Rectangle where _ = rect :: Rectangle
pct = 3 % 100 pct = 3 % 100
-- with a ratio of 1, no stack windows are drawn of there is at least
-- one master window around.
prop_tile_max_ratio = extremeRatio 1 drop
-- with a ratio of 0, no master windows are drawn at all if there are
-- any stack windows around.
prop_tile_min_ratio = extremeRatio 0 take
extremeRatio amount getRects rect = do
w@(NonNegative windows) <- arbitrary `suchThat` (> NonNegative 0)
NonNegative nmaster <- arbitrary `suchThat` (< w)
let tiled = tile amount rect nmaster windows
pure $ if nmaster == 0
then prop_tile_non_overlap rect windows nmaster
else all ((== 0) . rect_width) $ getRects nmaster tiled
-- splitting horizontally yields sensible results -- splitting horizontally yields sensible results
prop_split_horizontal (NonNegative n) x = prop_split_horizontal (NonNegative n) x =
noOverflows (+) (rect_x x) (rect_width x) ==> noOverflows (+) (rect_x x) (rect_width x) ==>
@ -66,20 +49,13 @@ prop_split_vertical (r :: Rational) x =
-- pureLayout works. -- pureLayout works.
prop_purelayout_tall n d r rect = do prop_purelayout_tall n r1 r2 rect = do
x <- (arbitrary :: Gen T) `suchThat` (isJust . peek) x <- (arbitrary :: Gen T) `suchThat` (isJust . peek)
let layout = Tall n d r let layout = Tall n r1 r2
st = fromJust . stack . workspace . current $ x st = fromJust . stack . workspace . current $ x
ts = pureLayout layout rect st ts = pureLayout layout rect st
ntotal = length (index x)
return $ return $
(if r == 0 then length ts == length (index x)
-- (<=) for Bool is the logical implication
(0 <= n && n <= ntotal) <= (length ts == ntotal - n)
else if r == 1 then
(0 <= n && n <= ntotal) <= (length ts == n)
else
length ts == ntotal)
&& &&
noOverlaps (map snd ts) noOverlaps (map snd ts)
&& &&

View File

@ -1,5 +1,9 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
#ifdef VERSION_quickcheck_classes
{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}
#endif
module Properties.Stack where module Properties.Stack where
@ -11,11 +15,13 @@ import qualified XMonad.StackSet as S (filter)
import Data.Maybe import Data.Maybe
#ifdef VERSION_quickcheck_classes
import Data.Proxy import Data.Proxy
import Test.QuickCheck.Classes ( import Test.QuickCheck.Classes (
Laws (lawsTypeclass, lawsProperties), Proxy1 (Proxy1), Laws (lawsTypeclass, lawsProperties), Proxy1 (Proxy1),
foldableLaws, traversableLaws, foldableLaws, traversableLaws,
) )
#endif
-- The list returned by index should be the same length as the actual -- The list returned by index should be the same length as the actual
@ -59,6 +65,7 @@ prop_differentiate xs =
where _ = xs :: [Int] where _ = xs :: [Int]
#ifdef VERSION_quickcheck_classes
-- Check type class laws of 'Data.Foldable.Foldable' and 'Data.Traversable.Traversable'. -- Check type class laws of 'Data.Foldable.Foldable' and 'Data.Traversable.Traversable'.
newtype TestStack a = TestStack (Stack a) newtype TestStack a = TestStack (Stack a)
deriving (Eq, Read, Show, Foldable, Functor) deriving (Eq, Read, Show, Foldable, Functor)
@ -75,3 +82,6 @@ prop_laws_Stack = format (foldableLaws p) <> format (traversableLaws p)
p = Proxy :: Proxy TestStack p = Proxy :: Proxy TestStack
format laws = [ ("Stack: " <> lawsTypeclass laws <> ": " <> name, prop) format laws = [ ("Stack: " <> lawsTypeclass laws <> ": " <> name, prop)
| (name, prop) <- lawsProperties laws ] | (name, prop) <- lawsProperties laws ]
#else
prop_laws_Stack = []
#endif

View File

@ -1,5 +1,5 @@
name: xmonad name: xmonad
version: 0.18.0.9 version: 0.17.1
synopsis: A tiling window manager synopsis: A tiling window manager
description: xmonad is a tiling window manager for X. Windows are arranged description: xmonad is a tiling window manager for X. Windows are arranged
automatically to tile the screen without gaps or overlap, maximising automatically to tile the screen without gaps or overlap, maximising
@ -27,7 +27,7 @@ author: Spencer Janssen, Don Stewart, Adam Vogt, David Roundy, Jason
Ondřej Súkup, Paul Hebble, Shachaf Ben-Kiki, Siim Põder, Tim McIver, Ondřej Súkup, Paul Hebble, Shachaf Ben-Kiki, Siim Põder, Tim McIver,
Trevor Elliott, Wouter Swierstra, Conrad Irwin, Tim Thelion, Tony Zorman Trevor Elliott, Wouter Swierstra, Conrad Irwin, Tim Thelion, Tony Zorman
maintainer: xmonad@haskell.org maintainer: xmonad@haskell.org
tested-with: GHC == 8.6.5 || == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.8 || == 9.4.8 || == 9.6.7 || == 9.8.4 || == 9.10.2 || == 9.12.2 tested-with: GHC == 8.4.4 || == 8.6.5 || == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.2
category: System category: System
homepage: http://xmonad.org homepage: http://xmonad.org
bug-reports: https://github.com/xmonad/xmonad/issues bug-reports: https://github.com/xmonad/xmonad/issues
@ -54,6 +54,8 @@ flag pedantic
default: False default: False
manual: True manual: True
flag quickcheck-classes
library library
exposed-modules: XMonad exposed-modules: XMonad
XMonad.Config XMonad.Config
@ -65,7 +67,7 @@ library
XMonad.StackSet XMonad.StackSet
other-modules: Paths_xmonad other-modules: Paths_xmonad
hs-source-dirs: src hs-source-dirs: src
build-depends: base >= 4.12 && < 5 build-depends: base >= 4.11 && < 5
, X11 >= 1.10 && < 1.11 , X11 >= 1.10 && < 1.11
, containers , containers
, data-default-class , data-default-class
@ -81,7 +83,7 @@ library
default-language: Haskell2010 default-language: Haskell2010
-- Keep this in sync with the oldest version in 'tested-with' -- Keep this in sync with the oldest version in 'tested-with'
if impl(ghc > 8.6.5) if impl(ghc > 8.4.4)
ghc-options: -Wno-unused-imports ghc-options: -Wno-unused-imports
if flag(pedantic) if flag(pedantic)
@ -94,7 +96,7 @@ executable xmonad
default-language: Haskell2010 default-language: Haskell2010
-- Keep this in sync with the oldest version in 'tested-with' -- Keep this in sync with the oldest version in 'tested-with'
if impl(ghc > 8.6.5) if impl(ghc > 8.4.4)
ghc-options: -Wno-unused-imports ghc-options: -Wno-unused-imports
if flag(pedantic) if flag(pedantic)
@ -123,14 +125,15 @@ test-suite properties
hs-source-dirs: tests hs-source-dirs: tests
build-depends: base build-depends: base
, QuickCheck >= 2 , QuickCheck >= 2
, quickcheck-classes >= 0.4.3
, X11 , X11
, containers , containers
, xmonad , xmonad
default-language: Haskell2010 default-language: Haskell2010
if impl(ghc > 9.8) if flag(quickcheck-classes) && impl(ghc > 8.5)
ghc-options: -Wno-x-partial -- 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) if flag(pedantic)
ghc-options: -Werror ghc-options: -Werror