mirror of
https://github.com/xmonad/xmonad-contrib.git
synced 2025-05-19 11:30:22 -07:00
Compare commits
69 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
00832bf5e6 | ||
|
410b34f074 | ||
|
15dd45be0e | ||
|
f7451b9378 | ||
|
849208d1b8 | ||
|
4b86621051 | ||
|
18eb8aca94 | ||
|
a84f3e8540 | ||
|
bd81961a63 | ||
|
209839f3ca | ||
|
50e7dd4262 | ||
|
e2cdc0cc2c | ||
|
68da8c44ba | ||
|
0517c94960 | ||
|
b1bf33d6eb | ||
|
195a0ac3c0 | ||
|
b470de0d75 | ||
|
87585a6884 | ||
|
41f1d1434c | ||
|
ddcce31597 | ||
|
4496b4f2d5 | ||
|
5119626269 | ||
|
27c86d0dda | ||
|
58dbf59cab | ||
|
9d457a73ce | ||
|
f1f392cd01 | ||
|
6c1441d9db | ||
|
6a6d913dee | ||
|
4fc3642fa2 | ||
|
7614f94d92 | ||
|
c7061b0d73 | ||
|
6df1044265 | ||
|
619a347f3f | ||
|
2b11459496 | ||
|
beabe75dda | ||
|
0404372fd3 | ||
|
c0a5bc5f0f | ||
|
1f13bb2468 | ||
|
d4473946d4 | ||
|
55e1adde4c | ||
|
de01015af5 | ||
|
7f0f0ad498 | ||
|
195537e97e | ||
|
d9e54c1b96 | ||
|
b570ab1a74 | ||
|
0dc879698d | ||
|
fe826ca8db | ||
|
d19ea051d4 | ||
|
c5032a43fb | ||
|
61f8b4aa8e | ||
|
60fc830e2e | ||
|
f97ce867ac | ||
|
2f42d2e7b4 | ||
|
b454f1e0be | ||
|
5680205c72 | ||
|
1c5261d65a | ||
|
2c161ff670 | ||
|
2ec4bbc833 | ||
|
42340e0f76 | ||
|
4350936ba5 | ||
|
2973c283ae | ||
|
a96a2031f6 | ||
|
1e5fcb1216 | ||
|
6bc6bf8abd | ||
|
c98715623d | ||
|
b3c249434d | ||
|
d0d9d42761 | ||
|
e203096143 | ||
|
6811b9e296 |
6
.github/workflows/haskell-ci-hackage.patch
vendored
6
.github/workflows/haskell-ci-hackage.patch
vendored
@ -38,13 +38,13 @@ set in GitHub repository secrets.
|
|||||||
linux:
|
linux:
|
||||||
name: Haskell-CI - Linux - ${{ matrix.compiler }}
|
name: Haskell-CI - Linux - ${{ matrix.compiler }}
|
||||||
@@ -33,6 +40,7 @@
|
@@ -33,6 +40,7 @@
|
||||||
compilerVersion: 9.8.2
|
compilerVersion: 9.8.4
|
||||||
setup-method: ghcup
|
setup-method: ghcup
|
||||||
allow-failure: false
|
allow-failure: false
|
||||||
+ upload: true
|
+ upload: true
|
||||||
- compiler: ghc-9.6.6
|
- compiler: ghc-9.6.7
|
||||||
compilerKind: ghc
|
compilerKind: ghc
|
||||||
compilerVersion: 9.6.6
|
compilerVersion: 9.6.7
|
||||||
@@ -257,6 +265,10 @@
|
@@ -257,6 +265,10 @@
|
||||||
- name: haddock
|
- name: haddock
|
||||||
run: |
|
run: |
|
||||||
|
66
.github/workflows/haskell-ci.yml
vendored
66
.github/workflows/haskell-ci.yml
vendored
@ -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.20240708
|
# version: 0.19.20250506
|
||||||
#
|
#
|
||||||
# REGENDATA ("0.19.20240708",["github","cabal.project"])
|
# REGENDATA ("0.19.20250506",["github","cabal.project"])
|
||||||
#
|
#
|
||||||
name: Haskell-CI
|
name: Haskell-CI
|
||||||
on:
|
on:
|
||||||
@ -26,7 +26,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
linux:
|
linux:
|
||||||
name: Haskell-CI - Linux - ${{ matrix.compiler }}
|
name: Haskell-CI - Linux - ${{ matrix.compiler }}
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
timeout-minutes:
|
timeout-minutes:
|
||||||
60
|
60
|
||||||
container:
|
container:
|
||||||
@ -35,20 +35,25 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- compiler: ghc-9.10.1
|
- compiler: ghc-9.12.2
|
||||||
compilerKind: ghc
|
compilerKind: ghc
|
||||||
compilerVersion: 9.10.1
|
compilerVersion: 9.12.2
|
||||||
setup-method: ghcup
|
setup-method: ghcup
|
||||||
allow-failure: false
|
allow-failure: false
|
||||||
- compiler: ghc-9.8.2
|
- compiler: ghc-9.10.2
|
||||||
compilerKind: ghc
|
compilerKind: ghc
|
||||||
compilerVersion: 9.8.2
|
compilerVersion: 9.10.2
|
||||||
|
setup-method: ghcup
|
||||||
|
allow-failure: false
|
||||||
|
- compiler: ghc-9.8.4
|
||||||
|
compilerKind: ghc
|
||||||
|
compilerVersion: 9.8.4
|
||||||
setup-method: ghcup
|
setup-method: ghcup
|
||||||
allow-failure: false
|
allow-failure: false
|
||||||
upload: true
|
upload: true
|
||||||
- compiler: ghc-9.6.6
|
- compiler: ghc-9.6.7
|
||||||
compilerKind: ghc
|
compilerKind: ghc
|
||||||
compilerVersion: 9.6.6
|
compilerVersion: 9.6.7
|
||||||
setup-method: ghcup
|
setup-method: ghcup
|
||||||
allow-failure: false
|
allow-failure: false
|
||||||
- compiler: ghc-9.4.8
|
- compiler: ghc-9.4.8
|
||||||
@ -76,24 +81,32 @@ jobs:
|
|||||||
compilerVersion: 8.8.4
|
compilerVersion: 8.8.4
|
||||||
setup-method: ghcup
|
setup-method: ghcup
|
||||||
allow-failure: false
|
allow-failure: false
|
||||||
- compiler: ghc-8.6.5
|
|
||||||
compilerKind: ghc
|
|
||||||
compilerVersion: 8.6.5
|
|
||||||
setup-method: ghcup
|
|
||||||
allow-failure: false
|
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
steps:
|
steps:
|
||||||
- name: apt
|
- name: apt-get install
|
||||||
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
|
||||||
mkdir -p "$HOME/.ghcup/bin"
|
|
||||||
curl -sL https://downloads.haskell.org/ghcup/0.1.30.0/x86_64-linux-ghcup-0.1.30.0 > "$HOME/.ghcup/bin/ghcup"
|
|
||||||
chmod a+x "$HOME/.ghcup/bin/ghcup"
|
|
||||||
"$HOME/.ghcup/bin/ghcup" install ghc "$HCVER" || (cat "$HOME"/.ghcup/logs/*.* && false)
|
|
||||||
"$HOME/.ghcup/bin/ghcup" install cabal 3.12.1.0 || (cat "$HOME"/.ghcup/logs/*.* && false)
|
|
||||||
apt-get update
|
|
||||||
apt-get install -y libx11-dev libxext-dev libxft-dev libxinerama-dev libxrandr-dev libxss-dev
|
apt-get install -y libx11-dev libxext-dev libxft-dev libxinerama-dev libxrandr-dev libxss-dev
|
||||||
|
- name: Install GHCup
|
||||||
|
run: |
|
||||||
|
mkdir -p "$HOME/.ghcup/bin"
|
||||||
|
curl -sL https://downloads.haskell.org/ghcup/0.1.50.1/x86_64-linux-ghcup-0.1.50.1 > "$HOME/.ghcup/bin/ghcup"
|
||||||
|
chmod a+x "$HOME/.ghcup/bin/ghcup"
|
||||||
|
- name: Install cabal-install
|
||||||
|
run: |
|
||||||
|
"$HOME/.ghcup/bin/ghcup" install cabal 3.14.2.0 || (cat "$HOME"/.ghcup/logs/*.* && false)
|
||||||
|
echo "CABAL=$HOME/.ghcup/bin/cabal-3.14.2.0 -vnormal+nowrap" >> "$GITHUB_ENV"
|
||||||
|
- name: Install GHC (GHCup)
|
||||||
|
if: matrix.setup-method == 'ghcup'
|
||||||
|
run: |
|
||||||
|
"$HOME/.ghcup/bin/ghcup" install ghc "$HCVER" || (cat "$HOME"/.ghcup/logs/*.* && false)
|
||||||
|
HC=$("$HOME/.ghcup/bin/ghcup" whereis ghc "$HCVER")
|
||||||
|
HCPKG=$(echo "$HC" | sed 's#ghc$#ghc-pkg#')
|
||||||
|
HADDOCK=$(echo "$HC" | sed 's#ghc$#haddock#')
|
||||||
|
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 }}
|
||||||
@ -104,21 +117,12 @@ 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
|
|
||||||
HC=$("$HOME/.ghcup/bin/ghcup" whereis ghc "$HCVER")
|
|
||||||
HCPKG=$(echo "$HC" | sed 's#ghc$#ghc-pkg#')
|
|
||||||
HADDOCK=$(echo "$HC" | sed 's#ghc$#haddock#')
|
|
||||||
echo "HC=$HC" >> "$GITHUB_ENV"
|
|
||||||
echo "HCPKG=$HCPKG" >> "$GITHUB_ENV"
|
|
||||||
echo "HADDOCK=$HADDOCK" >> "$GITHUB_ENV"
|
|
||||||
echo "CABAL=$HOME/.ghcup/bin/cabal-3.12.1.0 -vnormal+nowrap" >> "$GITHUB_ENV"
|
|
||||||
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 }}
|
||||||
@ -248,8 +252,8 @@ jobs:
|
|||||||
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: save cache
|
||||||
uses: actions/cache/save@v4
|
|
||||||
if: always()
|
if: always()
|
||||||
|
uses: actions/cache/save@v4
|
||||||
with:
|
with:
|
||||||
key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
|
key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
|
||||||
path: ~/.cabal/store
|
path: ~/.cabal/store
|
||||||
|
10
.github/workflows/nix.yml
vendored
10
.github/workflows/nix.yml
vendored
@ -6,19 +6,15 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-20.04 # FIXME
|
runs-on: ubuntu-latest
|
||||||
name: Nix Flake - Linux
|
name: Nix Flake - Linux
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@V27
|
uses: cachix/install-nix-action@v31
|
||||||
with:
|
with:
|
||||||
install_url: https://nixos-nix-install-tests.cachix.org/serve/i6laym9jw3wg9mw6ncyrk6gjx4l34vvx/install
|
github_access_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
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@v4
|
||||||
- name: Build
|
- name: Build
|
||||||
|
8
.github/workflows/stack.yml
vendored
8
.github/workflows/stack.yml
vendored
@ -12,10 +12,8 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- resolver: lts-14 # GHC 8.6
|
- resolver: lts-16 # GHC 8.8
|
||||||
yaml: stack.yaml
|
yaml: stack.yaml
|
||||||
- resolver: lts-14 # GHC 8.6
|
|
||||||
yaml: stack-master.yaml
|
|
||||||
- resolver: lts-16 # GHC 8.8
|
- resolver: lts-16 # GHC 8.8
|
||||||
yaml: stack-master.yaml
|
yaml: stack-master.yaml
|
||||||
- resolver: lts-18 # GHC 8.10
|
- resolver: lts-18 # GHC 8.10
|
||||||
@ -27,8 +25,10 @@ jobs:
|
|||||||
- resolver: lts-21 # GHC 9.4
|
- resolver: lts-21 # GHC 9.4
|
||||||
yaml: stack-master.yaml
|
yaml: stack-master.yaml
|
||||||
- resolver: lts-22 # GHC 9.6
|
- resolver: lts-22 # GHC 9.6
|
||||||
|
yaml: stack-master.yaml
|
||||||
|
- resolver: lts-23 # GHC 9.8
|
||||||
yaml: stack.yaml
|
yaml: stack.yaml
|
||||||
- resolver: lts-22 # GHC 9.6
|
- resolver: lts-23 # GHC 9.8
|
||||||
yaml: stack-master.yaml
|
yaml: stack-master.yaml
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
79
CHANGES.md
79
CHANGES.md
@ -2,6 +2,65 @@
|
|||||||
|
|
||||||
## _unreleased_
|
## _unreleased_
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
* Drop support for GHC 8.6
|
||||||
|
|
||||||
|
### Bug Fixes and Minor Changes
|
||||||
|
|
||||||
|
* `XMonad.Util.EZConfig`
|
||||||
|
|
||||||
|
- Added `XF86WLAN` and `Menu` to the list of supported special keys.
|
||||||
|
|
||||||
|
* `XMonad.Actions.DynamicProjects`
|
||||||
|
|
||||||
|
- No longer autodelete projects when `switchProject` is called from
|
||||||
|
an empty workspace. This also fixes a bug where static workspaces
|
||||||
|
would be deleted when switching to a dynamic project.
|
||||||
|
- Improved documentation on how to close a project.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.Rescreen`
|
||||||
|
|
||||||
|
- Allow overriding the `rescreen` operation itself. Additionally, the
|
||||||
|
`XMonad.Actions.PhysicalScreens` module now provides an alternative
|
||||||
|
implementation of `rescreen` that avoids reshuffling the workspaces if
|
||||||
|
the number of screens doesn't change and only their locations do (which
|
||||||
|
is especially common if one uses `xrandr --setmonitor` to split an
|
||||||
|
ultra-wide display in two).
|
||||||
|
|
||||||
|
- Added an optional delay when waiting for events to settle. This may be
|
||||||
|
used to avoid flicker and unnecessary workspace reshuffling if multiple
|
||||||
|
`xrandr` commands are used to reconfigure the display layout.
|
||||||
|
|
||||||
|
* `XMonad.Layout.NoBorders`
|
||||||
|
|
||||||
|
- It's no longer necessary to use `borderEventHook` to garbage collect
|
||||||
|
`alwaysHidden`/`neverHidden` lists. The layout listens to
|
||||||
|
`DestroyWindowEvent` messages instead, which are broadcast to layouts
|
||||||
|
since xmonad v0.17.0.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.EwmhDesktops`
|
||||||
|
|
||||||
|
- Added a customization option for the action that gets executed when
|
||||||
|
a client sends a **_NET_CURRENT_DESKTOP** request. It is now possible
|
||||||
|
to change it using the `setEwmhSwitchDesktopHook`.
|
||||||
|
- Added a customization option for mapping hidden workspaces to screens
|
||||||
|
when setting the **_NET_DESKTOP_VIEWPORT**. This can be done using
|
||||||
|
the `setEwmhHiddenWorkspaceToScreenMapping`.
|
||||||
|
|
||||||
|
* `XMonad.Layout.IndependentScreens`
|
||||||
|
|
||||||
|
- Added `focusWorkspace` for focusing workspaces on the screen that they
|
||||||
|
belong to.
|
||||||
|
- Added `doFocus'` hook as an alternative for `doFocus` when using
|
||||||
|
IndependentScreens.
|
||||||
|
- Added `screenOnMonitor` for getting the active screen for a monitor.
|
||||||
|
|
||||||
|
* `XMonad.Util.NamedScratchPad`
|
||||||
|
|
||||||
|
- Fix unintended window hiding in `nsSingleScratchpadPerWorkspace`.
|
||||||
|
Only hide the previously active scratchpad.
|
||||||
|
|
||||||
## 0.18.1 (August 20, 2024)
|
## 0.18.1 (August 20, 2024)
|
||||||
|
|
||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
@ -421,7 +480,8 @@
|
|||||||
* `XMonad.Config.{Arossato,Dmwit,Droundy,Monad,Prime,Saegesser,Sjanssen}`
|
* `XMonad.Config.{Arossato,Dmwit,Droundy,Monad,Prime,Saegesser,Sjanssen}`
|
||||||
|
|
||||||
- Deprecated all of these modules. The user-specific configuration
|
- Deprecated all of these modules. The user-specific configuration
|
||||||
modules may still be found [on the website].
|
modules may still be found [on the
|
||||||
|
website](https://xmonad.org/configurations.html)
|
||||||
|
|
||||||
* `XMonad.Util.NamedScratchpad`
|
* `XMonad.Util.NamedScratchpad`
|
||||||
|
|
||||||
@ -442,8 +502,6 @@
|
|||||||
- Deprecated `urgencyConfig`; use `def` from the new `Default`
|
- Deprecated `urgencyConfig`; use `def` from the new `Default`
|
||||||
instance of `UrgencyConfig` instead.
|
instance of `UrgencyConfig` instead.
|
||||||
|
|
||||||
[on the website]: https://xmonad.org/configurations.html
|
|
||||||
|
|
||||||
### New Modules
|
### New Modules
|
||||||
|
|
||||||
* `XMonad.Actions.PerLayoutKeys`
|
* `XMonad.Actions.PerLayoutKeys`
|
||||||
@ -518,7 +576,8 @@
|
|||||||
`todo +d 12 02 2024` work.
|
`todo +d 12 02 2024` work.
|
||||||
|
|
||||||
- Added the ability to specify alphabetic (`#A`, `#B`, and `#C`)
|
- Added the ability to specify alphabetic (`#A`, `#B`, and `#C`)
|
||||||
[priorities] at the end of the input note.
|
[priorities](https://orgmode.org/manual/Priorities.html) at the end of
|
||||||
|
the input note.
|
||||||
|
|
||||||
* `XMonad.Prompt.Unicode`
|
* `XMonad.Prompt.Unicode`
|
||||||
|
|
||||||
@ -612,7 +671,8 @@
|
|||||||
|
|
||||||
- Modified `mkAbsolutePath` to support a leading environment variable, so
|
- Modified `mkAbsolutePath` to support a leading environment variable, so
|
||||||
things like `$HOME/NOTES` work. If you want more general environment
|
things like `$HOME/NOTES` work. If you want more general environment
|
||||||
variable support, comment on [this PR].
|
variable support, comment on [this
|
||||||
|
PR](https://github.com/xmonad/xmonad-contrib/pull/744)
|
||||||
|
|
||||||
* `XMonad.Util.XUtils`
|
* `XMonad.Util.XUtils`
|
||||||
|
|
||||||
@ -651,9 +711,6 @@
|
|||||||
|
|
||||||
- Added a `Default` instance for `UrgencyConfig` and `DzenUrgencyHook`.
|
- Added a `Default` instance for `UrgencyConfig` and `DzenUrgencyHook`.
|
||||||
|
|
||||||
[this PR]: https://github.com/xmonad/xmonad-contrib/pull/744
|
|
||||||
[priorities]: https://orgmode.org/manual/Priorities.html
|
|
||||||
|
|
||||||
### Other changes
|
### Other changes
|
||||||
|
|
||||||
* Migrated the sample build scripts from the deprecated `xmonad-testing` repo to
|
* Migrated the sample build scripts from the deprecated `xmonad-testing` repo to
|
||||||
@ -2179,8 +2236,8 @@
|
|||||||
|
|
||||||
* `XMonad.Prompt.Pass`
|
* `XMonad.Prompt.Pass`
|
||||||
|
|
||||||
This module provides 3 `XMonad.Prompt`s to ease passwords
|
This module provides 3 `XMonad.Prompt`s to ease passwords manipulation
|
||||||
manipulation (generate, read, remove) via [pass][].
|
(generate, read, remove) via [pass](http://www.passwordstore.org/).
|
||||||
|
|
||||||
* `XMonad.Util.RemoteWindows`
|
* `XMonad.Util.RemoteWindows`
|
||||||
|
|
||||||
@ -2256,5 +2313,3 @@
|
|||||||
## See Also
|
## See Also
|
||||||
|
|
||||||
<https://wiki.haskell.org/Xmonad/Notable_changes_since_0.8>
|
<https://wiki.haskell.org/Xmonad/Notable_changes_since_0.8>
|
||||||
|
|
||||||
[pass]: http://www.passwordstore.org/
|
|
||||||
|
@ -69,7 +69,9 @@ import qualified XMonad.Util.ExtensibleState as XS
|
|||||||
-- the working directory to the one configured for the matching
|
-- the working directory to the one configured for the matching
|
||||||
-- project. If the workspace doesn't have any windows, the project's
|
-- project. If the workspace doesn't have any windows, the project's
|
||||||
-- start-up hook is executed. This allows you to launch applications
|
-- start-up hook is executed. This allows you to launch applications
|
||||||
-- or further configure the workspace/project.
|
-- or further configure the workspace/project. To close a project,
|
||||||
|
-- you can use the functions provided by "XMonad.Actions.DynamicWorkspaces",
|
||||||
|
-- such as @removeWorkspace@ or @removeWorkspaceByTag@.
|
||||||
--
|
--
|
||||||
-- When using the @switchProjectPrompt@ function, workspaces are
|
-- When using the @switchProjectPrompt@ function, workspaces are
|
||||||
-- created as needed. This means you can create new project spaces
|
-- created as needed. This means you can create new project spaces
|
||||||
@ -230,7 +232,9 @@ lookupProject name = Map.lookup name <$> XS.gets projects
|
|||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- | Fetch the current project (the one being used for the currently
|
-- | Fetch the current project (the one being used for the currently
|
||||||
-- active workspace).
|
-- active workspace). If the workspace doesn't have a project, a
|
||||||
|
-- default project is returned, using the workspace name as the
|
||||||
|
-- project name.
|
||||||
currentProject :: X Project
|
currentProject :: X Project
|
||||||
currentProject = do
|
currentProject = do
|
||||||
name <- gets (W.tag . W.workspace . W.current . windowset)
|
name <- gets (W.tag . W.workspace . W.current . windowset)
|
||||||
@ -255,20 +259,7 @@ modifyProject f = do
|
|||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- | Switch to the given project.
|
-- | Switch to the given project.
|
||||||
switchProject :: Project -> X ()
|
switchProject :: Project -> X ()
|
||||||
switchProject p = do
|
switchProject p = appendWorkspace (projectName p)
|
||||||
oldws <- gets (W.workspace . W.current . windowset)
|
|
||||||
oldp <- currentProject
|
|
||||||
|
|
||||||
let name = W.tag oldws
|
|
||||||
ws = W.integrate' (W.stack oldws)
|
|
||||||
|
|
||||||
-- If the project we are switching away from has no windows, and
|
|
||||||
-- it's a dynamic project, remove it from the configuration.
|
|
||||||
when (null ws && isNothing (projectStartHook oldp)) $ do
|
|
||||||
removeWorkspaceByTag name -- also remove the old workspace
|
|
||||||
XS.modify (\s -> s {projects = Map.delete name $ projects s})
|
|
||||||
|
|
||||||
appendWorkspace (projectName p)
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- | Prompt for a project name and then switch to it. Automatically
|
-- | Prompt for a project name and then switch to it. Automatically
|
||||||
|
@ -1,51 +1,56 @@
|
|||||||
-----------------------------------------------------------------------------
|
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Actions.OnScreen
|
-- Module : XMonad.Actions.OnScreen
|
||||||
-- Description : Control workspaces on different screens (in xinerama mode).
|
-- Description : Control workspaces on different screens (in xinerama mode).
|
||||||
-- Copyright : (c) 2009 Nils Schweinsberg
|
-- Copyright : (c) 2009-2025 Nils Schweinsberg
|
||||||
-- License : BSD3-style (see LICENSE)
|
-- License : BSD3-style (see LICENSE)
|
||||||
--
|
--
|
||||||
-- Maintainer : Nils Schweinsberg <mail@n-sch.de>
|
-- Maintainer : Nils Schweinsberg <mail@nils.cc>
|
||||||
-- Stability : unstable
|
-- Stability : unstable
|
||||||
-- Portability : unportable
|
-- Portability : unportable
|
||||||
--
|
--
|
||||||
-- Control workspaces on different screens (in xinerama mode).
|
-- Control workspaces on different screens (in xinerama mode).
|
||||||
--
|
module XMonad.Actions.OnScreen
|
||||||
-----------------------------------------------------------------------------
|
( -- * Usage
|
||||||
|
|
||||||
module XMonad.Actions.OnScreen (
|
|
||||||
-- * Usage
|
|
||||||
-- $usage
|
-- $usage
|
||||||
onScreen
|
onScreen,
|
||||||
, onScreen'
|
onScreen',
|
||||||
, Focus(..)
|
Focus (..),
|
||||||
, viewOnScreen
|
viewOnScreen,
|
||||||
, greedyViewOnScreen
|
greedyViewOnScreen,
|
||||||
, onlyOnScreen
|
onlyOnScreen,
|
||||||
, toggleOnScreen
|
toggleOnScreen,
|
||||||
, toggleGreedyOnScreen
|
toggleGreedyOnScreen,
|
||||||
) where
|
)
|
||||||
|
where
|
||||||
|
|
||||||
import XMonad
|
import XMonad
|
||||||
import XMonad.Prelude (fromMaybe, guard, empty)
|
import XMonad.Prelude (empty, fromMaybe, guard)
|
||||||
import XMonad.StackSet hiding (new)
|
import XMonad.StackSet hiding (new)
|
||||||
|
|
||||||
|
|
||||||
-- | Focus data definitions
|
-- | Focus data definitions
|
||||||
data Focus = FocusNew -- ^ always focus the new screen
|
data Focus
|
||||||
| FocusCurrent -- ^ always keep the focus on the current screen
|
= -- | always focus the new screen
|
||||||
| FocusTag WorkspaceId -- ^ always focus tag i on the new stack
|
FocusNew
|
||||||
| FocusTagVisible WorkspaceId -- ^ focus tag i only if workspace with tag i is visible on the old stack
|
| -- | always keep the focus on the current screen
|
||||||
|
FocusCurrent
|
||||||
|
| -- | always focus tag i on the new stack
|
||||||
|
FocusTag WorkspaceId
|
||||||
|
| -- | focus tag i only if workspace with tag i is visible on the old stack
|
||||||
|
FocusTagVisible WorkspaceId
|
||||||
|
|
||||||
-- | Run any function that modifies the stack on a given screen. This function
|
-- | Run any function that modifies the stack on a given screen. This function
|
||||||
-- will also need to know which Screen to focus after the function has been
|
-- will also need to know which Screen to focus after the function has been
|
||||||
-- run.
|
-- run.
|
||||||
onScreen :: (WindowSet -> WindowSet) -- ^ function to run
|
onScreen ::
|
||||||
-> Focus -- ^ what to do with the focus
|
-- | function to run
|
||||||
-> ScreenId -- ^ screen id
|
(WindowSet -> WindowSet) ->
|
||||||
-> WindowSet -- ^ current stack
|
-- | what to do with the focus
|
||||||
-> WindowSet
|
Focus ->
|
||||||
|
-- | screen id
|
||||||
|
ScreenId ->
|
||||||
|
-- | current stack
|
||||||
|
WindowSet ->
|
||||||
|
WindowSet
|
||||||
onScreen f foc sc st = fromMaybe st $ do
|
onScreen f foc sc st = fromMaybe st $ do
|
||||||
ws <- lookupWorkspace sc st
|
ws <- lookupWorkspace sc st
|
||||||
|
|
||||||
@ -53,12 +58,14 @@ onScreen f foc sc st = fromMaybe st $ do
|
|||||||
|
|
||||||
return $ setFocus foc st fStack
|
return $ setFocus foc st fStack
|
||||||
|
|
||||||
|
|
||||||
-- set focus for new stack
|
-- set focus for new stack
|
||||||
setFocus :: Focus
|
setFocus ::
|
||||||
-> WindowSet -- ^ old stack
|
Focus ->
|
||||||
-> WindowSet -- ^ new stack
|
-- | old stack
|
||||||
-> WindowSet
|
WindowSet ->
|
||||||
|
-- | new stack
|
||||||
|
WindowSet ->
|
||||||
|
WindowSet
|
||||||
setFocus FocusNew _ new = new
|
setFocus FocusNew _ new = new
|
||||||
setFocus FocusCurrent old new =
|
setFocus FocusCurrent old new =
|
||||||
case lookupWorkspace (screen $ current old) new of
|
case lookupWorkspace (screen $ current old) new of
|
||||||
@ -74,10 +81,14 @@ setFocus (FocusTagVisible i) old new =
|
|||||||
-- on the given screen.
|
-- on the given screen.
|
||||||
-- Warning: This function will change focus even if the function it's supposed
|
-- Warning: This function will change focus even if the function it's supposed
|
||||||
-- to run doesn't succeed.
|
-- to run doesn't succeed.
|
||||||
onScreen' :: X () -- ^ X function to run
|
onScreen' ::
|
||||||
-> Focus -- ^ focus
|
-- | X function to run
|
||||||
-> ScreenId -- ^ screen id
|
X () ->
|
||||||
-> X ()
|
-- | focus
|
||||||
|
Focus ->
|
||||||
|
-- | screen id
|
||||||
|
ScreenId ->
|
||||||
|
X ()
|
||||||
onScreen' x foc sc = do
|
onScreen' x foc sc = do
|
||||||
st <- gets windowset
|
st <- gets windowset
|
||||||
case lookupWorkspace sc st of
|
case lookupWorkspace sc st of
|
||||||
@ -87,55 +98,77 @@ onScreen' x foc sc = do
|
|||||||
x
|
x
|
||||||
windows $ setFocus foc st
|
windows $ setFocus foc st
|
||||||
|
|
||||||
|
|
||||||
-- | Switch to workspace @i@ on screen @sc@. If @i@ is visible use @view@ to
|
-- | Switch to workspace @i@ on screen @sc@. If @i@ is visible use @view@ to
|
||||||
-- switch focus to the workspace @i@.
|
-- switch focus to the workspace @i@.
|
||||||
viewOnScreen :: ScreenId -- ^ screen id
|
viewOnScreen ::
|
||||||
-> WorkspaceId -- ^ index of the workspace
|
-- | screen id
|
||||||
-> WindowSet -- ^ current stack
|
ScreenId ->
|
||||||
-> WindowSet
|
-- | index of the workspace
|
||||||
|
WorkspaceId ->
|
||||||
|
-- | current stack
|
||||||
|
WindowSet ->
|
||||||
|
WindowSet
|
||||||
viewOnScreen sid i =
|
viewOnScreen sid i =
|
||||||
onScreen (view i) (FocusTag i) sid
|
onScreen (view i) (FocusTag i) sid
|
||||||
|
|
||||||
-- | Switch to workspace @i@ on screen @sc@. If @i@ is visible use @greedyView@
|
-- | Switch to workspace @i@ on screen @sc@. If @i@ is visible use @greedyView@
|
||||||
-- to switch the current workspace with workspace @i@.
|
-- to switch the current workspace with workspace @i@.
|
||||||
greedyViewOnScreen :: ScreenId -- ^ screen id
|
greedyViewOnScreen ::
|
||||||
-> WorkspaceId -- ^ index of the workspace
|
-- | screen id
|
||||||
-> WindowSet -- ^ current stack
|
ScreenId ->
|
||||||
-> WindowSet
|
-- | index of the workspace
|
||||||
|
WorkspaceId ->
|
||||||
|
-- | current stack
|
||||||
|
WindowSet ->
|
||||||
|
WindowSet
|
||||||
greedyViewOnScreen sid i =
|
greedyViewOnScreen sid i =
|
||||||
onScreen (greedyView i) (FocusTagVisible i) sid
|
onScreen (greedyView i) (FocusTagVisible i) sid
|
||||||
|
|
||||||
-- | Switch to workspace @i@ on screen @sc@. If @i@ is visible do nothing.
|
-- | Switch to workspace @i@ on screen @sc@. If @i@ is visible do nothing.
|
||||||
onlyOnScreen :: ScreenId -- ^ screen id
|
onlyOnScreen ::
|
||||||
-> WorkspaceId -- ^ index of the workspace
|
-- | screen id
|
||||||
-> WindowSet -- ^ current stack
|
ScreenId ->
|
||||||
-> WindowSet
|
-- | index of the workspace
|
||||||
|
WorkspaceId ->
|
||||||
|
-- | current stack
|
||||||
|
WindowSet ->
|
||||||
|
WindowSet
|
||||||
onlyOnScreen sid i =
|
onlyOnScreen sid i =
|
||||||
onScreen (view i) FocusCurrent sid
|
onScreen (view i) FocusCurrent sid
|
||||||
|
|
||||||
-- | @toggleOrView@ as in "XMonad.Actions.CycleWS" for @onScreen@ with view
|
-- | @toggleOrView@ as in "XMonad.Actions.CycleWS" for @onScreen@ with view
|
||||||
toggleOnScreen :: ScreenId -- ^ screen id
|
toggleOnScreen ::
|
||||||
-> WorkspaceId -- ^ index of the workspace
|
-- | screen id
|
||||||
-> WindowSet -- ^ current stack
|
ScreenId ->
|
||||||
-> WindowSet
|
-- | index of the workspace
|
||||||
|
WorkspaceId ->
|
||||||
|
-- | current stack
|
||||||
|
WindowSet ->
|
||||||
|
WindowSet
|
||||||
toggleOnScreen sid i =
|
toggleOnScreen sid i =
|
||||||
onScreen (toggleOrView' view i) FocusCurrent sid
|
onScreen (toggleOrView' view i) FocusCurrent sid
|
||||||
|
|
||||||
-- | @toggleOrView@ from "XMonad.Actions.CycleWS" for @onScreen@ with greedyView
|
-- | @toggleOrView@ from "XMonad.Actions.CycleWS" for @onScreen@ with greedyView
|
||||||
toggleGreedyOnScreen :: ScreenId -- ^ screen id
|
toggleGreedyOnScreen ::
|
||||||
-> WorkspaceId -- ^ index of the workspace
|
-- | screen id
|
||||||
-> WindowSet -- ^ current stack
|
ScreenId ->
|
||||||
-> WindowSet
|
-- | index of the workspace
|
||||||
|
WorkspaceId ->
|
||||||
|
-- | current stack
|
||||||
|
WindowSet ->
|
||||||
|
WindowSet
|
||||||
toggleGreedyOnScreen sid i =
|
toggleGreedyOnScreen sid i =
|
||||||
onScreen (toggleOrView' greedyView i) FocusCurrent sid
|
onScreen (toggleOrView' greedyView i) FocusCurrent sid
|
||||||
|
|
||||||
|
|
||||||
-- a \"pure\" version of X.A.CycleWS.toggleOrDoSkip
|
-- a \"pure\" version of X.A.CycleWS.toggleOrDoSkip
|
||||||
toggleOrView' :: (WorkspaceId -> WindowSet -> WindowSet) -- ^ function to run
|
toggleOrView' ::
|
||||||
-> WorkspaceId -- ^ tag to look for
|
-- | function to run
|
||||||
-> WindowSet -- ^ current stackset
|
(WorkspaceId -> WindowSet -> WindowSet) ->
|
||||||
-> WindowSet
|
-- | tag to look for
|
||||||
|
WorkspaceId ->
|
||||||
|
-- | current stackset
|
||||||
|
WindowSet ->
|
||||||
|
WindowSet
|
||||||
toggleOrView' f i st = fromMaybe (f i st) $ do
|
toggleOrView' f i st = fromMaybe (f i st) $ do
|
||||||
let st' = hidden st
|
let st' = hidden st
|
||||||
-- make sure we actually have to do something
|
-- make sure we actually have to do something
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||||
|
{-# LANGUAGE LambdaCase #-}
|
||||||
|
{-# LANGUAGE ParallelListComp #-}
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Actions.PhysicalScreens
|
-- Module : XMonad.Actions.PhysicalScreens
|
||||||
@ -28,10 +30,13 @@ module XMonad.Actions.PhysicalScreens (
|
|||||||
, getScreenIdAndRectangle
|
, getScreenIdAndRectangle
|
||||||
, screenComparatorById
|
, screenComparatorById
|
||||||
, screenComparatorByRectangle
|
, screenComparatorByRectangle
|
||||||
|
, rescreen
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import XMonad
|
import Data.List.NonEmpty (nonEmpty)
|
||||||
import XMonad.Prelude (elemIndex, fromMaybe, on, sortBy)
|
import XMonad hiding (rescreen)
|
||||||
|
import XMonad.Prelude (elemIndex, fromMaybe, on, sortBy, NonEmpty((:|)))
|
||||||
|
import qualified Data.List.NonEmpty as NE
|
||||||
import qualified XMonad.StackSet as W
|
import qualified XMonad.StackSet as W
|
||||||
|
|
||||||
{- $usage
|
{- $usage
|
||||||
@ -146,3 +151,53 @@ onNextNeighbour sc = neighbourWindows sc 1
|
|||||||
-- | Apply operation on a WindowSet with the WorkspaceId of the previous screen in the physical order as parameter.
|
-- | Apply operation on a WindowSet with the WorkspaceId of the previous screen in the physical order as parameter.
|
||||||
onPrevNeighbour :: ScreenComparator -> (WorkspaceId -> WindowSet -> WindowSet) -> X ()
|
onPrevNeighbour :: ScreenComparator -> (WorkspaceId -> WindowSet -> WindowSet) -> X ()
|
||||||
onPrevNeighbour sc = neighbourWindows sc (-1)
|
onPrevNeighbour sc = neighbourWindows sc (-1)
|
||||||
|
|
||||||
|
-- | An alternative to 'XMonad.Operations.rescreen' that avoids reshuffling
|
||||||
|
-- the workspaces if the number of screens doesn't change and only their
|
||||||
|
-- locations do. Useful for users of @xrandr --setmonitor@.
|
||||||
|
--
|
||||||
|
-- See 'XMonad.Hooks.Rescreen.setRescreenWorkspacesHook', which lets you
|
||||||
|
-- replace the builtin rescreen handler.
|
||||||
|
rescreen :: ScreenComparator -> X ()
|
||||||
|
rescreen (ScreenComparator cmpScreen) = withDisplay (fmap nonEmpty . getCleanedScreenInfo) >>= \case
|
||||||
|
Nothing -> trace "getCleanedScreenInfo returned []"
|
||||||
|
Just xinescs -> windows $ rescreen' xinescs
|
||||||
|
where
|
||||||
|
rescreen' :: NonEmpty Rectangle -> WindowSet -> WindowSet
|
||||||
|
rescreen' xinescs ws
|
||||||
|
| NE.length xinescs == length (W.visible ws) + 1 = rescreenSameLength xinescs ws
|
||||||
|
| otherwise = rescreenCore xinescs ws
|
||||||
|
|
||||||
|
-- the 'XMonad.Operations.rescreen' implementation from core as a fallback
|
||||||
|
rescreenCore :: NonEmpty Rectangle -> WindowSet -> WindowSet
|
||||||
|
rescreenCore (xinesc :| xinescs) ws@W.StackSet{ W.current = v, W.visible = vs, W.hidden = hs } =
|
||||||
|
let (xs, ys) = splitAt (length xinescs) (map W.workspace vs ++ hs)
|
||||||
|
a = W.Screen (W.workspace v) 0 (SD xinesc)
|
||||||
|
as = zipWith3 W.Screen xs [1..] $ map SD xinescs
|
||||||
|
in ws{ W.current = a
|
||||||
|
, W.visible = as
|
||||||
|
, W.hidden = ys }
|
||||||
|
|
||||||
|
-- sort both existing screens and the screens we just got from xinerama
|
||||||
|
-- using cmpScreen, and then replace the rectangles in the WindowSet,
|
||||||
|
-- keeping the order of current/visible workspaces intact
|
||||||
|
rescreenSameLength :: NonEmpty Rectangle -> WindowSet -> WindowSet
|
||||||
|
rescreenSameLength xinescs ws =
|
||||||
|
ws{ W.current = (W.current ws){ W.screenDetail = SD newCurrentRect }
|
||||||
|
, W.visible = [ w{ W.screenDetail = SD r } | w <- W.visible ws | r <- newVisibleRects ]
|
||||||
|
}
|
||||||
|
where
|
||||||
|
undoSort =
|
||||||
|
NE.map fst $
|
||||||
|
NE.sortBy (cmpScreen `on` (getScreenIdAndRectangle . snd)) $
|
||||||
|
NE.zip ((0 :: Int) :| [1..]) $ -- add indices to undo the sort later
|
||||||
|
W.current ws :| W.visible ws
|
||||||
|
newCurrentRect :| newVisibleRects =
|
||||||
|
NE.map snd $ NE.sortWith fst $ NE.zip undoSort $ -- sort back into current:visible order
|
||||||
|
NE.map snd $ NE.sortBy cmpScreen $ NE.zip (0 :| [1..]) xinescs
|
||||||
|
|
||||||
|
-- TODO:
|
||||||
|
-- If number of screens before and after isn't the same, we might still
|
||||||
|
-- try to match locations and avoid changing the workspace for those that
|
||||||
|
-- didn't move, while making sure that the current workspace is still
|
||||||
|
-- visible somewhere.
|
||||||
|
169
XMonad/Actions/UpKeys.hs
Normal file
169
XMonad/Actions/UpKeys.hs
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
{-# LANGUAGE BlockArguments #-}
|
||||||
|
{-# LANGUAGE InstanceSigs #-}
|
||||||
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
{- |
|
||||||
|
Module : XMonad.Actions.UpKeys
|
||||||
|
Description : Bind an action to the release of a key
|
||||||
|
Copyright : (c) Tony Zorman, 2024
|
||||||
|
License : BSD-3
|
||||||
|
Maintainer : Tony Zorman <soliditsallgood@mailbox.org>
|
||||||
|
|
||||||
|
A combinator for binding an action to the release of a key. This can be
|
||||||
|
useful for hold-type buttons, where the press of a key engages some
|
||||||
|
functionality, and its release… releases it again.
|
||||||
|
-}
|
||||||
|
module XMonad.Actions.UpKeys
|
||||||
|
( -- * Usage
|
||||||
|
-- $usage
|
||||||
|
useUpKeys,
|
||||||
|
UpKeysConfig (..),
|
||||||
|
ezUpKeys,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Data.Map.Strict (Map)
|
||||||
|
import qualified Data.Map.Strict as Map
|
||||||
|
import XMonad
|
||||||
|
import XMonad.Prelude
|
||||||
|
import XMonad.Util.EZConfig (mkKeymap)
|
||||||
|
import qualified XMonad.Util.ExtensibleConf as XC
|
||||||
|
|
||||||
|
{- $usage
|
||||||
|
You can use this module with the following in your @xmonad.hs@:
|
||||||
|
|
||||||
|
> import XMonad.Actions.UpKeys
|
||||||
|
|
||||||
|
Next, define the keys and actions you want to have happen on the release
|
||||||
|
of a key:
|
||||||
|
|
||||||
|
> myUpKeys = ezUpKeys $
|
||||||
|
> [ ("M-z", myAction)
|
||||||
|
> , ("M-a", myAction2)
|
||||||
|
> ]
|
||||||
|
|
||||||
|
All that's left is to plug this definition into the 'useUpKeys'
|
||||||
|
combinator that this module provides:
|
||||||
|
|
||||||
|
> main :: IO ()
|
||||||
|
> main = xmonad
|
||||||
|
> . useUpKeys (def{ grabKeys = True, upKeys = myUpKeys })
|
||||||
|
> $ myConfig
|
||||||
|
|
||||||
|
Note the presence of @'grabKeys' = True@; this is for situations where
|
||||||
|
you don't have any of these keys bound to do something upon pressing
|
||||||
|
them; i.e., you use them solely for their release actions. If you want
|
||||||
|
something to happen in both cases, remove that part (@'grabKeys' =
|
||||||
|
False@ is the default) and bind the keys to actions as you normally
|
||||||
|
would.
|
||||||
|
|
||||||
|
==== __Examples__
|
||||||
|
|
||||||
|
As an extended example, consider the case where you want all of your
|
||||||
|
docks (e.g., status bar) to "pop up" when you press the super key, and
|
||||||
|
then vanish again once that keys is released.
|
||||||
|
|
||||||
|
Since docks are not generally part of XMonad's window-set—otherwise, we
|
||||||
|
would have to manage them—we first need a way to access and manipulate
|
||||||
|
all docks.
|
||||||
|
|
||||||
|
> onAllDocks :: (Display -> Window -> IO ()) -> X ()
|
||||||
|
> onAllDocks act = withDisplay \dpy -> do
|
||||||
|
> rootw <- asks theRoot
|
||||||
|
> (_, _, wins) <- io $ queryTree dpy rootw
|
||||||
|
> traverse_ (io . act dpy) =<< filterM (runQuery checkDock) wins
|
||||||
|
|
||||||
|
This is also the place where one could filter for just status bar,
|
||||||
|
trayer, and so on.
|
||||||
|
|
||||||
|
Now we have to decide what kinds of keys we want to watch out for. Since
|
||||||
|
you most likely use left super as your modifier key, this is a little
|
||||||
|
bit more complicated than for other keys, as you will most likely see
|
||||||
|
the key both as a @KeyMask@, as well as a @KeySym@. One could think a
|
||||||
|
bit and probably come up with an elegant solution for this—or one could
|
||||||
|
grab all possible key combinations by brute-force!
|
||||||
|
|
||||||
|
> dockKeys :: X () -> [((KeyMask, KeySym), X ())]
|
||||||
|
> dockKeys act = map (actKey . foldr1 (.|.)) . combinations $ keyMasks
|
||||||
|
> where
|
||||||
|
> actKey :: KeyMask -> ((KeyMask, KeySym), X ())
|
||||||
|
> actKey mask = ((mask, xK_Super_L), act)
|
||||||
|
>
|
||||||
|
> keyMasks :: [KeyMask]
|
||||||
|
> keyMasks = [ noModMask, shiftMask, lockMask, controlMask, mod1Mask, mod2Mask, mod3Mask, mod4Mask, mod5Mask ]
|
||||||
|
>
|
||||||
|
> -- Return all combinations of a sequence of values.
|
||||||
|
> combinations :: [a] -> [[a]]
|
||||||
|
> combinations xs = concat [combs i xs | i <- [1 .. length xs]]
|
||||||
|
> where
|
||||||
|
> combs 0 _ = [[]]
|
||||||
|
> combs _ [] = []
|
||||||
|
> combs n (x:xs) = map (x:) (combs (n-1) xs) <> combs n xs
|
||||||
|
|
||||||
|
Given some action, like lowering or raising the window, we generate all
|
||||||
|
possible combinations of modifiers that may be pressed with the super
|
||||||
|
key. This is a good time to say that this is just for demonstrative
|
||||||
|
purposes, btw—please don't actually do this.
|
||||||
|
|
||||||
|
All that's left is to plug everything into the machinery of this module,
|
||||||
|
and we're done!
|
||||||
|
|
||||||
|
> import qualified Data.Map.Strict as Map
|
||||||
|
>
|
||||||
|
> main :: IO ()
|
||||||
|
> main = xmonad
|
||||||
|
> . … -- other combinators
|
||||||
|
> . useUpKeys (def { upKeys = Map.fromList $ dockKeys (onAllDocks lowerWindow) })
|
||||||
|
> $ myConfig `additionalKeys` dockKeys (onAllDocks raiseWindow)
|
||||||
|
>
|
||||||
|
> myConfig = …
|
||||||
|
-}
|
||||||
|
|
||||||
|
data UpKeysConfig = UpKeysConfig
|
||||||
|
{ -- | Whether to grab all keys that are not already grabbed.
|
||||||
|
grabKeys :: !Bool
|
||||||
|
-- | The keys themselves.
|
||||||
|
, upKeys :: !(Map (KeyMask, KeySym) (X ()))
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | The default 'UpKeysConfig'; keys are not grabbed, and no upkeys are
|
||||||
|
-- specified.
|
||||||
|
instance Default UpKeysConfig where
|
||||||
|
def :: UpKeysConfig
|
||||||
|
def = UpKeysConfig { grabKeys = False, upKeys = mempty }
|
||||||
|
|
||||||
|
instance Semigroup UpKeysConfig where
|
||||||
|
(<>) :: UpKeysConfig -> UpKeysConfig -> UpKeysConfig
|
||||||
|
UpKeysConfig g u <> UpKeysConfig g' u' = UpKeysConfig (g && g') (u <> u')
|
||||||
|
|
||||||
|
-- | Bind actions to keys upon their release.
|
||||||
|
useUpKeys :: UpKeysConfig -> (XConfig l -> XConfig l)
|
||||||
|
useUpKeys upKeysConf = flip XC.once upKeysConf \conf -> conf
|
||||||
|
{ handleEventHook = handleEventHook conf <> (\e -> handleKeyUp e $> All True)
|
||||||
|
, startupHook = startupHook conf <> when (grabKeys upKeysConf) grabUpKeys
|
||||||
|
}
|
||||||
|
where
|
||||||
|
grabUpKeys :: X ()
|
||||||
|
grabUpKeys = do
|
||||||
|
XConf{ display = dpy, theRoot = rootw } <- ask
|
||||||
|
realKeys <- maybe mempty upKeys <$> XC.ask @X @UpKeysConfig
|
||||||
|
let grab :: (KeyMask, KeyCode) -> X ()
|
||||||
|
grab (km, kc) = io $ grabKey dpy kc km rootw True grabModeAsync grabModeAsync
|
||||||
|
traverse_ grab =<< mkGrabs (Map.keys realKeys)
|
||||||
|
|
||||||
|
-- | Parse the given EZConfig-style keys into the internal keymap
|
||||||
|
-- representation.
|
||||||
|
--
|
||||||
|
-- This is just 'mkKeymap' with a better name.
|
||||||
|
ezUpKeys :: XConfig l -> [(String, X ())] -> Map (KeyMask, KeySym) (X ())
|
||||||
|
ezUpKeys = mkKeymap
|
||||||
|
|
||||||
|
-- | A handler for key-up events.
|
||||||
|
handleKeyUp :: Event -> X ()
|
||||||
|
handleKeyUp KeyEvent{ ev_event_type, ev_state, ev_keycode }
|
||||||
|
| ev_event_type == keyRelease = withDisplay \dpy -> do
|
||||||
|
s <- io $ keycodeToKeysym dpy ev_keycode 0
|
||||||
|
cln <- cleanMask ev_state
|
||||||
|
ks <- maybe mempty upKeys <$> XC.ask @X @UpKeysConfig
|
||||||
|
userCodeDef () $ whenJust (ks Map.!? (cln, s)) id
|
||||||
|
handleKeyUp _ = pure ()
|
@ -1,3 +1,4 @@
|
|||||||
|
{-# OPTIONS_HADDOCK hide #-}
|
||||||
{-# LANGUAGE FlexibleContexts, FlexibleInstances, FunctionalDependencies, KindSignatures, UndecidableInstances #-}
|
{-# LANGUAGE FlexibleContexts, FlexibleInstances, FunctionalDependencies, KindSignatures, UndecidableInstances #-}
|
||||||
|
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
|
@ -33,7 +33,6 @@ import XMonad.Util.DebugWindow (debugWindow)
|
|||||||
-- import Graphics.X11.Xlib.Extras.GetAtomName (getAtomName)
|
-- import Graphics.X11.Xlib.Extras.GetAtomName (getAtomName)
|
||||||
|
|
||||||
import Control.Exception as E
|
import Control.Exception as E
|
||||||
import Control.Monad.Fail
|
|
||||||
import Control.Monad.State
|
import Control.Monad.State
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Codec.Binary.UTF8.String
|
import Codec.Binary.UTF8.String
|
||||||
|
@ -42,6 +42,10 @@ module XMonad.Hooks.EwmhDesktops (
|
|||||||
-- $customActivate
|
-- $customActivate
|
||||||
setEwmhActivateHook,
|
setEwmhActivateHook,
|
||||||
|
|
||||||
|
-- ** Workspace switching
|
||||||
|
-- $customWorkspaceSwitch
|
||||||
|
setEwmhSwitchDesktopHook,
|
||||||
|
|
||||||
-- ** Fullscreen
|
-- ** Fullscreen
|
||||||
-- $customFullscreen
|
-- $customFullscreen
|
||||||
setEwmhFullscreenHooks,
|
setEwmhFullscreenHooks,
|
||||||
@ -50,6 +54,9 @@ module XMonad.Hooks.EwmhDesktops (
|
|||||||
-- $customManageDesktopViewport
|
-- $customManageDesktopViewport
|
||||||
disableEwmhManageDesktopViewport,
|
disableEwmhManageDesktopViewport,
|
||||||
|
|
||||||
|
-- $customHiddenWorkspaceMapper
|
||||||
|
setEwmhHiddenWorkspaceToScreenMapping,
|
||||||
|
|
||||||
-- * Standalone hooks (deprecated)
|
-- * Standalone hooks (deprecated)
|
||||||
ewmhDesktopsStartup,
|
ewmhDesktopsStartup,
|
||||||
ewmhDesktopsLogHook,
|
ewmhDesktopsLogHook,
|
||||||
@ -114,8 +121,12 @@ data EwmhDesktopsConfig =
|
|||||||
-- ^ configurable handling of window activation requests
|
-- ^ configurable handling of window activation requests
|
||||||
, fullscreenHooks :: (ManageHook, ManageHook)
|
, fullscreenHooks :: (ManageHook, ManageHook)
|
||||||
-- ^ configurable handling of fullscreen state requests
|
-- ^ configurable handling of fullscreen state requests
|
||||||
|
, switchDesktopHook :: WorkspaceId -> WindowSet -> WindowSet
|
||||||
|
-- ^ configurable action for handling _NET_CURRENT_DESKTOP
|
||||||
, manageDesktopViewport :: Bool
|
, manageDesktopViewport :: Bool
|
||||||
-- ^ manage @_NET_DESKTOP_VIEWPORT@?
|
-- ^ manage @_NET_DESKTOP_VIEWPORT@?
|
||||||
|
, hiddenWorkspaceToScreen :: WindowSet -> WindowSpace -> WindowScreen
|
||||||
|
-- ^ map hidden workspaces to screens for @_NET_DESKTOP_VIEWPORT@
|
||||||
}
|
}
|
||||||
|
|
||||||
instance Default EwmhDesktopsConfig where
|
instance Default EwmhDesktopsConfig where
|
||||||
@ -124,7 +135,10 @@ instance Default EwmhDesktopsConfig where
|
|||||||
, workspaceRename = pure pure
|
, workspaceRename = pure pure
|
||||||
, activateHook = doFocus
|
, activateHook = doFocus
|
||||||
, fullscreenHooks = (doFullFloat, doSink)
|
, fullscreenHooks = (doFullFloat, doSink)
|
||||||
|
, switchDesktopHook = W.view
|
||||||
, manageDesktopViewport = True
|
, manageDesktopViewport = True
|
||||||
|
-- Hidden workspaces are mapped to the current screen by default.
|
||||||
|
, hiddenWorkspaceToScreen = \winset _ -> W.current winset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -231,8 +245,8 @@ setEwmhWorkspaceRename f = XC.modifyDef $ \c -> c{ workspaceRename = f }
|
|||||||
-- > [ className =? "Google-chrome" <||> className =? "google-chrome" -?> doAskUrgent
|
-- > [ className =? "Google-chrome" <||> className =? "google-chrome" -?> doAskUrgent
|
||||||
-- > , pure True -?> doFocus ]
|
-- > , pure True -?> doFocus ]
|
||||||
--
|
--
|
||||||
-- See "XMonad.ManageHook", "XMonad.Hooks.ManageHelpers" and "XMonad.Hooks.Focus"
|
-- See "XMonad.ManageHook", "XMonad.Hooks.ManageHelpers", "XMonad.Hooks.Focus" and
|
||||||
-- for functions that can be useful here.
|
-- "XMonad.Layout.IndependentScreens" for functions that can be useful here.
|
||||||
|
|
||||||
-- | Set (replace) the hook which is invoked when a client sends a
|
-- | Set (replace) the hook which is invoked when a client sends a
|
||||||
-- @_NET_ACTIVE_WINDOW@ request to activate a window. The default is 'doFocus'
|
-- @_NET_ACTIVE_WINDOW@ request to activate a window. The default is 'doFocus'
|
||||||
@ -245,6 +259,31 @@ setEwmhActivateHook :: ManageHook -> XConfig l -> XConfig l
|
|||||||
setEwmhActivateHook h = XC.modifyDef $ \c -> c{ activateHook = h }
|
setEwmhActivateHook h = XC.modifyDef $ \c -> c{ activateHook = h }
|
||||||
|
|
||||||
|
|
||||||
|
-- $customWorkspaceSwitch
|
||||||
|
-- When a client sends a @_NET_CURRENT_DESKTOP@ request to switch to a workspace,
|
||||||
|
-- the default action used to do that is the 'W.view' function.
|
||||||
|
-- This may not be the desired behaviour in all configurations.
|
||||||
|
--
|
||||||
|
-- For example if using the "XMonad.Layout.IndependentScreens" the default action
|
||||||
|
-- might move a workspace to a screen that it isn't supposed to be on.
|
||||||
|
-- This behaviour can be fixed using the following:
|
||||||
|
--
|
||||||
|
-- > import XMonad.Actions.OnScreen
|
||||||
|
-- > import XMonad.Layout.IndependentScreens
|
||||||
|
-- >
|
||||||
|
-- > main = xmonad $ ... . setEwmhSwitchDesktopHook focusWorkspace . ewmh . ... $
|
||||||
|
-- > def{
|
||||||
|
-- > ...
|
||||||
|
-- > workspaces = withScreens 2 (workspaces def)
|
||||||
|
-- > ...
|
||||||
|
-- > }
|
||||||
|
|
||||||
|
-- | Set (replace) the action which is invoked when a client sends a
|
||||||
|
-- @_NET_CURRENT_DESKTOP@ request to switch workspace.
|
||||||
|
setEwmhSwitchDesktopHook :: (WorkspaceId -> WindowSet -> WindowSet) -> XConfig l -> XConfig l
|
||||||
|
setEwmhSwitchDesktopHook action = XC.modifyDef $ \c -> c{ switchDesktopHook = action }
|
||||||
|
|
||||||
|
|
||||||
-- $customFullscreen
|
-- $customFullscreen
|
||||||
-- When a client sends a @_NET_WM_STATE@ request to add\/remove\/toggle the
|
-- When a client sends a @_NET_WM_STATE@ request to add\/remove\/toggle the
|
||||||
-- @_NET_WM_STATE_FULLSCREEN@ state, 'ewmhFullscreen' uses a pair of hooks to
|
-- @_NET_WM_STATE_FULLSCREEN@ state, 'ewmhFullscreen' uses a pair of hooks to
|
||||||
@ -284,6 +323,34 @@ disableEwmhManageDesktopViewport :: XConfig l -> XConfig l
|
|||||||
disableEwmhManageDesktopViewport = XC.modifyDef $ \c -> c{ manageDesktopViewport = False }
|
disableEwmhManageDesktopViewport = XC.modifyDef $ \c -> c{ manageDesktopViewport = False }
|
||||||
|
|
||||||
|
|
||||||
|
-- $customHiddenWorkspaceMapper
|
||||||
|
--
|
||||||
|
-- Mapping the hidden workspaces to the current screen is a good default behavior,
|
||||||
|
-- but it makes the assumption that workspaces don't belong to a sepcific screen.
|
||||||
|
-- If the default behaviour is undesired, for example when using "XMonad.Layout.IndependentScreens",
|
||||||
|
-- it can be customized.
|
||||||
|
--
|
||||||
|
-- The following example demonstrates a way to configure the mapping when using "XMonad.Layout.IndependentScreens":
|
||||||
|
--
|
||||||
|
-- > import XMonad.Layout.IndependentScreens
|
||||||
|
-- >
|
||||||
|
-- > customMapper :: WindowSet -> (WindowSpace -> WindowScreen)
|
||||||
|
-- > customMapper winset (Workspace wsid _ _) = fromMaybe (W.current winset) maybeMappedScreen
|
||||||
|
-- > where
|
||||||
|
-- > screenId = unmarshallS wsid
|
||||||
|
-- > maybeMappedScreen = screenOnMonitor screenId winset
|
||||||
|
-- >
|
||||||
|
-- >
|
||||||
|
-- > main = xmonad $ ... . setEwmhHiddenWorkspaceToScreenMapping customMapper . ewmh . ... $ def{...}
|
||||||
|
|
||||||
|
-- | Set (replace) the function responsible for mapping the hidden workspaces to screens.
|
||||||
|
setEwmhHiddenWorkspaceToScreenMapping :: (WindowSet -> (WindowSpace -> WindowScreen))
|
||||||
|
-- ^ Function that given the current WindowSet
|
||||||
|
-- produces a function to maps a (hidden) workspace to a screen.
|
||||||
|
-> XConfig l -> XConfig l
|
||||||
|
setEwmhHiddenWorkspaceToScreenMapping mapper = XC.modifyDef $ \c -> c{ hiddenWorkspaceToScreen = mapper }
|
||||||
|
|
||||||
|
|
||||||
-- | Initializes EwmhDesktops and advertises EWMH support to the X server.
|
-- | Initializes EwmhDesktops and advertises EWMH support to the X server.
|
||||||
{-# DEPRECATED ewmhDesktopsStartup "Use ewmh instead." #-}
|
{-# DEPRECATED ewmhDesktopsStartup "Use ewmh instead." #-}
|
||||||
ewmhDesktopsStartup :: X ()
|
ewmhDesktopsStartup :: X ()
|
||||||
@ -358,7 +425,7 @@ whenChanged :: (Eq a, ExtensionClass a) => a -> X () -> X ()
|
|||||||
whenChanged = whenX . XS.modified . const
|
whenChanged = whenX . XS.modified . const
|
||||||
|
|
||||||
ewmhDesktopsLogHook' :: EwmhDesktopsConfig -> X ()
|
ewmhDesktopsLogHook' :: EwmhDesktopsConfig -> X ()
|
||||||
ewmhDesktopsLogHook' EwmhDesktopsConfig{workspaceSort, workspaceRename, manageDesktopViewport} = withWindowSet $ \s -> do
|
ewmhDesktopsLogHook' EwmhDesktopsConfig{workspaceSort, workspaceRename, manageDesktopViewport, hiddenWorkspaceToScreen} = withWindowSet $ \s -> do
|
||||||
sort' <- workspaceSort
|
sort' <- workspaceSort
|
||||||
let ws = sort' $ W.workspaces s
|
let ws = sort' $ W.workspaces s
|
||||||
|
|
||||||
@ -423,18 +490,20 @@ ewmhDesktopsLogHook' EwmhDesktopsConfig{workspaceSort, workspaceRename, manageDe
|
|||||||
when manageDesktopViewport $ do
|
when manageDesktopViewport $ do
|
||||||
let visibleScreens = W.current s : W.visible s
|
let visibleScreens = W.current s : W.visible s
|
||||||
currentTags = map (W.tag . W.workspace) visibleScreens
|
currentTags = map (W.tag . W.workspace) visibleScreens
|
||||||
whenChanged (MonitorTags currentTags) $ mkViewPorts s (map W.tag ws)
|
whenChanged (MonitorTags currentTags) $ mkViewPorts s hiddenWorkspaceToScreen (map W.tag ws)
|
||||||
|
|
||||||
-- | Create the viewports from the current 'WindowSet' and a list of
|
-- | Create the viewports from the current 'WindowSet' and a list of
|
||||||
-- already sorted workspace IDs.
|
-- already sorted workspace IDs.
|
||||||
mkViewPorts :: WindowSet -> [WorkspaceId] -> X ()
|
mkViewPorts :: WindowSet -> (WindowSet -> WindowSpace -> WindowScreen) -> [WorkspaceId] -> X ()
|
||||||
mkViewPorts winset = setDesktopViewport . concat . mapMaybe (viewPorts M.!?)
|
mkViewPorts winset hiddenWorkspaceMapper = setDesktopViewport . concat . mapMaybe (viewPorts M.!?)
|
||||||
where
|
where
|
||||||
foc = W.current winset
|
foc = W.current winset
|
||||||
-- Hidden workspaces are mapped to the current screen's viewport.
|
|
||||||
viewPorts :: M.Map WorkspaceId [Position]
|
viewPorts :: M.Map WorkspaceId [Position]
|
||||||
viewPorts = M.fromList $ map mkVisibleViewPort (foc : W.visible winset)
|
viewPorts = M.fromList $ map mkVisibleViewPort (foc : W.visible winset)
|
||||||
++ map (mkViewPort foc) (W.hidden winset)
|
++ map (uncurry mkViewPort) hiddenWorkspacesWithScreens
|
||||||
|
|
||||||
|
hiddenWorkspacesWithScreens :: [(WindowScreen,WindowSpace)]
|
||||||
|
hiddenWorkspacesWithScreens = map (\x -> (hiddenWorkspaceMapper winset x, x)) (W.hidden winset)
|
||||||
|
|
||||||
mkViewPort :: WindowScreen -> WindowSpace -> (WorkspaceId, [Position])
|
mkViewPort :: WindowScreen -> WindowSpace -> (WorkspaceId, [Position])
|
||||||
mkViewPort scr w = (W.tag w, mkPos scr)
|
mkViewPort scr w = (W.tag w, mkPos scr)
|
||||||
@ -449,7 +518,7 @@ mkViewPorts winset = setDesktopViewport . concat . mapMaybe (viewPorts M.!?)
|
|||||||
ewmhDesktopsEventHook' :: Event -> EwmhDesktopsConfig -> X All
|
ewmhDesktopsEventHook' :: Event -> EwmhDesktopsConfig -> X All
|
||||||
ewmhDesktopsEventHook'
|
ewmhDesktopsEventHook'
|
||||||
ClientMessageEvent{ev_window = w, ev_message_type = mt, ev_data = d}
|
ClientMessageEvent{ev_window = w, ev_message_type = mt, ev_data = d}
|
||||||
EwmhDesktopsConfig{workspaceSort, activateHook} =
|
EwmhDesktopsConfig{workspaceSort, activateHook, switchDesktopHook} =
|
||||||
withWindowSet $ \s -> do
|
withWindowSet $ \s -> do
|
||||||
sort' <- workspaceSort
|
sort' <- workspaceSort
|
||||||
let ws = sort' $ W.workspaces s
|
let ws = sort' $ W.workspaces s
|
||||||
@ -462,7 +531,7 @@ ewmhDesktopsEventHook'
|
|||||||
if | mt == a_cw ->
|
if | mt == a_cw ->
|
||||||
killWindow w
|
killWindow w
|
||||||
| mt == a_cd, n : _ <- d, Just ww <- ws !? fi n ->
|
| mt == a_cd, n : _ <- d, Just ww <- ws !? fi n ->
|
||||||
if W.currentTag s == W.tag ww then mempty else windows $ W.view (W.tag ww)
|
if W.currentTag s == W.tag ww then mempty else windows $ switchDesktopHook (W.tag ww)
|
||||||
| mt == a_cd ->
|
| mt == a_cd ->
|
||||||
trace $ "Bad _NET_CURRENT_DESKTOP with data=" ++ show d
|
trace $ "Bad _NET_CURRENT_DESKTOP with data=" ++ show d
|
||||||
| not (w `W.member` s) ->
|
| not (w `W.member` s) ->
|
||||||
|
@ -15,10 +15,13 @@ module XMonad.Hooks.Rescreen (
|
|||||||
-- $usage
|
-- $usage
|
||||||
addAfterRescreenHook,
|
addAfterRescreenHook,
|
||||||
addRandrChangeHook,
|
addRandrChangeHook,
|
||||||
|
setRescreenWorkspacesHook,
|
||||||
|
setRescreenDelay,
|
||||||
RescreenConfig(..),
|
RescreenConfig(..),
|
||||||
rescreenHook,
|
rescreenHook,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Control.Concurrent (threadDelay)
|
||||||
import Graphics.X11.Xrandr
|
import Graphics.X11.Xrandr
|
||||||
import XMonad
|
import XMonad
|
||||||
import XMonad.Prelude
|
import XMonad.Prelude
|
||||||
@ -59,16 +62,21 @@ import qualified XMonad.Util.ExtensibleConf as XC
|
|||||||
data RescreenConfig = RescreenConfig
|
data RescreenConfig = RescreenConfig
|
||||||
{ afterRescreenHook :: X () -- ^ hook to invoke after 'rescreen'
|
{ afterRescreenHook :: X () -- ^ hook to invoke after 'rescreen'
|
||||||
, randrChangeHook :: X () -- ^ hook for other randr changes, e.g. (dis)connects
|
, randrChangeHook :: X () -- ^ hook for other randr changes, e.g. (dis)connects
|
||||||
|
, rescreenWorkspacesHook :: Last (X ()) -- ^ hook to invoke instead of 'rescreen'
|
||||||
|
, rescreenDelay :: Last Int -- ^ delay (in microseconds) to wait for events to settle
|
||||||
}
|
}
|
||||||
|
|
||||||
instance Default RescreenConfig where
|
instance Default RescreenConfig where
|
||||||
def = RescreenConfig
|
def = RescreenConfig
|
||||||
{ afterRescreenHook = mempty
|
{ afterRescreenHook = mempty
|
||||||
, randrChangeHook = mempty
|
, randrChangeHook = mempty
|
||||||
|
, rescreenWorkspacesHook = mempty
|
||||||
|
, rescreenDelay = mempty
|
||||||
}
|
}
|
||||||
|
|
||||||
instance Semigroup RescreenConfig where
|
instance Semigroup RescreenConfig where
|
||||||
RescreenConfig arh rch <> RescreenConfig arh' rch' = RescreenConfig (arh <> arh') (rch <> rch')
|
RescreenConfig arh rch rwh rd <> RescreenConfig arh' rch' rwh' rd' =
|
||||||
|
RescreenConfig (arh <> arh') (rch <> rch') (rwh <> rwh') (rd <> rd')
|
||||||
|
|
||||||
instance Monoid RescreenConfig where
|
instance Monoid RescreenConfig where
|
||||||
mempty = def
|
mempty = def
|
||||||
@ -89,20 +97,45 @@ instance Monoid RescreenConfig where
|
|||||||
-- 'randrChangeHook' may be used to automatically trigger xrandr (or perhaps
|
-- 'randrChangeHook' may be used to automatically trigger xrandr (or perhaps
|
||||||
-- autorandr) when outputs are (dis)connected.
|
-- autorandr) when outputs are (dis)connected.
|
||||||
--
|
--
|
||||||
|
-- 'rescreenWorkspacesHook' allows tweaking the 'rescreen' implementation,
|
||||||
|
-- to change the order workspaces are assigned to physical screens for
|
||||||
|
-- example.
|
||||||
|
--
|
||||||
|
-- 'rescreenDelay' makes xmonad wait a bit for events to settle (after the
|
||||||
|
-- first event is received) — useful when multiple @xrandr@ invocations are
|
||||||
|
-- being used to change the screen layout.
|
||||||
|
--
|
||||||
-- Note that 'rescreenHook' is safe to use several times, 'rescreen' is still
|
-- Note that 'rescreenHook' is safe to use several times, 'rescreen' is still
|
||||||
-- done just once and hooks are invoked in sequence, also just once.
|
-- done just once and hooks are invoked in sequence (except
|
||||||
|
-- 'rescreenWorkspacesHook', which has a replace rather than sequence
|
||||||
|
-- semantics), also just once.
|
||||||
rescreenHook :: RescreenConfig -> XConfig l -> XConfig l
|
rescreenHook :: RescreenConfig -> XConfig l -> XConfig l
|
||||||
rescreenHook = XC.once $ \c -> c
|
rescreenHook = XC.once hook . catchUserCode
|
||||||
|
where
|
||||||
|
hook c = c
|
||||||
{ startupHook = startupHook c <> rescreenStartupHook
|
{ startupHook = startupHook c <> rescreenStartupHook
|
||||||
, handleEventHook = handleEventHook c <> rescreenEventHook }
|
, handleEventHook = handleEventHook c <> rescreenEventHook }
|
||||||
|
catchUserCode rc@RescreenConfig{..} = rc
|
||||||
|
{ afterRescreenHook = userCodeDef () afterRescreenHook
|
||||||
|
, randrChangeHook = userCodeDef () randrChangeHook
|
||||||
|
, rescreenWorkspacesHook = flip catchX rescreen <$> rescreenWorkspacesHook
|
||||||
|
}
|
||||||
|
|
||||||
-- | Shortcut for 'rescreenHook'.
|
-- | Shortcut for 'rescreenHook'.
|
||||||
addAfterRescreenHook :: X () -> XConfig l -> XConfig l
|
addAfterRescreenHook :: X () -> XConfig l -> XConfig l
|
||||||
addAfterRescreenHook h = rescreenHook def{ afterRescreenHook = userCodeDef () h }
|
addAfterRescreenHook h = rescreenHook def{ afterRescreenHook = h }
|
||||||
|
|
||||||
-- | Shortcut for 'rescreenHook'.
|
-- | Shortcut for 'rescreenHook'.
|
||||||
addRandrChangeHook :: X () -> XConfig l -> XConfig l
|
addRandrChangeHook :: X () -> XConfig l -> XConfig l
|
||||||
addRandrChangeHook h = rescreenHook def{ randrChangeHook = userCodeDef () h }
|
addRandrChangeHook h = rescreenHook def{ randrChangeHook = h }
|
||||||
|
|
||||||
|
-- | Shortcut for 'rescreenHook'.
|
||||||
|
setRescreenWorkspacesHook :: X () -> XConfig l -> XConfig l
|
||||||
|
setRescreenWorkspacesHook h = rescreenHook def{ rescreenWorkspacesHook = pure h }
|
||||||
|
|
||||||
|
-- | Shortcut for 'rescreenHook'.
|
||||||
|
setRescreenDelay :: Int -> XConfig l -> XConfig l
|
||||||
|
setRescreenDelay d = rescreenHook def{ rescreenDelay = pure d }
|
||||||
|
|
||||||
-- | Startup hook to listen for @RRScreenChangeNotify@ events.
|
-- | Startup hook to listen for @RRScreenChangeNotify@ events.
|
||||||
rescreenStartupHook :: X ()
|
rescreenStartupHook :: X ()
|
||||||
@ -126,13 +159,14 @@ handleEvent :: Event -> X ()
|
|||||||
handleEvent e = XC.with $ \RescreenConfig{..} -> do
|
handleEvent e = XC.with $ \RescreenConfig{..} -> do
|
||||||
-- Xorg emits several events after every change, clear them to prevent
|
-- Xorg emits several events after every change, clear them to prevent
|
||||||
-- triggering the hook multiple times.
|
-- triggering the hook multiple times.
|
||||||
|
whenJust (getLast rescreenDelay) (io . threadDelay)
|
||||||
moreConfigureEvents <- clearTypedWindowEvents (ev_window e) configureNotify
|
moreConfigureEvents <- clearTypedWindowEvents (ev_window e) configureNotify
|
||||||
_ <- clearTypedWindowRREvents (ev_window e) rrScreenChangeNotify
|
_ <- clearTypedWindowRREvents (ev_window e) rrScreenChangeNotify
|
||||||
-- If there were any ConfigureEvents, this is an actual screen
|
-- If there were any ConfigureEvents, this is an actual screen
|
||||||
-- configuration change, so rescreen and fire rescreenHook. Otherwise,
|
-- configuration change, so rescreen and fire rescreenHook. Otherwise,
|
||||||
-- this is just a connect/disconnect, fire randrChangeHook.
|
-- this is just a connect/disconnect, fire randrChangeHook.
|
||||||
if ev_event_type e == configureNotify || moreConfigureEvents
|
if ev_event_type e == configureNotify || moreConfigureEvents
|
||||||
then rescreen >> afterRescreenHook
|
then fromMaybe rescreen (getLast rescreenWorkspacesHook) >> afterRescreenHook
|
||||||
else randrChangeHook
|
else randrChangeHook
|
||||||
|
|
||||||
-- | Remove all X events of a given window and type from the event queue,
|
-- | Remove all X events of a given window and type from the event queue,
|
||||||
|
@ -1,44 +1,43 @@
|
|||||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TupleSections #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
-----------------------------------------------------------------------------
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
|
{-# LANGUAGE TupleSections #-}
|
||||||
|
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Hooks.ScreenCorners
|
-- Module : XMonad.Hooks.ScreenCorners
|
||||||
-- Description : Run X () actions by touching the edge of your screen with your mouse.
|
-- Description : Run X () actions by touching the edge of your screen with your mouse.
|
||||||
-- Copyright : (c) 2009 Nils Schweinsberg, 2015 Evgeny Kurnevsky, 2024 Yuanle Song
|
-- Copyright : (c) 2009-2025 Nils Schweinsberg, 2015 Evgeny Kurnevsky, 2024 Yuanle Song
|
||||||
-- License : BSD3-style (see LICENSE)
|
-- License : BSD3-style (see LICENSE)
|
||||||
--
|
--
|
||||||
-- Maintainer : Nils Schweinsberg <mail@n-sch.de>
|
-- Maintainer : Nils Schweinsberg <mail@nils.cc>
|
||||||
-- Stability : unstable
|
-- Stability : unstable
|
||||||
-- Portability : unportable
|
-- Portability : unportable
|
||||||
--
|
--
|
||||||
-- Run @X ()@ actions by touching the edge of your screen with your mouse.
|
-- Run @X ()@ actions by touching the edge of your screen with your mouse.
|
||||||
--
|
|
||||||
-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
module XMonad.Hooks.ScreenCorners
|
module XMonad.Hooks.ScreenCorners
|
||||||
(
|
( -- * Usage
|
||||||
-- * Usage
|
|
||||||
-- $usage
|
-- $usage
|
||||||
|
|
||||||
-- * Adding screen corners
|
-- * Adding screen corners
|
||||||
ScreenCorner (..)
|
ScreenCorner (..),
|
||||||
, addScreenCorner
|
addScreenCorner,
|
||||||
, addScreenCorners
|
addScreenCorners,
|
||||||
|
|
||||||
-- * Event hook
|
-- * Event hook
|
||||||
, screenCornerEventHook
|
screenCornerEventHook,
|
||||||
|
|
||||||
-- * Layout hook
|
-- * Layout hook
|
||||||
, screenCornerLayoutHook
|
screenCornerLayoutHook,
|
||||||
) where
|
)
|
||||||
|
where
|
||||||
import XMonad.Prelude
|
|
||||||
import XMonad
|
|
||||||
import XMonad.Layout.LayoutModifier
|
|
||||||
|
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
|
import XMonad
|
||||||
|
import XMonad.Layout.LayoutModifier
|
||||||
|
import XMonad.Prelude
|
||||||
import qualified XMonad.Util.ExtensibleState as XS
|
import qualified XMonad.Util.ExtensibleState as XS
|
||||||
|
|
||||||
data ScreenCorner = SCUpperLeft
|
data ScreenCorner
|
||||||
|
= SCUpperLeft
|
||||||
| SCUpperRight
|
| SCUpperRight
|
||||||
| SCLowerLeft
|
| SCLowerLeft
|
||||||
| SCLowerRight
|
| SCLowerRight
|
||||||
@ -60,20 +59,17 @@ instance ExtensionClass ScreenCornerState where
|
|||||||
-- | Add one single @X ()@ action to a screen corner
|
-- | Add one single @X ()@ action to a screen corner
|
||||||
addScreenCorner :: ScreenCorner -> X () -> X ()
|
addScreenCorner :: ScreenCorner -> X () -> X ()
|
||||||
addScreenCorner corner xF = do
|
addScreenCorner corner xF = do
|
||||||
|
|
||||||
ScreenCornerState m <- XS.get
|
ScreenCornerState m <- XS.get
|
||||||
(win,xFunc) <- case find (\(_,(sc,_)) -> sc == corner) (M.toList m) of
|
(win, xFunc) <- case find (\(_, (sc, _)) -> sc == corner) (M.toList m) of
|
||||||
|
Just (w, (_, xF')) -> return (w, xF' >> xF) -- chain X actions
|
||||||
|
Nothing -> (,xF) <$> createWindowAt corner
|
||||||
|
|
||||||
Just (w, (_,xF')) -> return (w, xF' >> xF) -- chain X actions
|
XS.modify $ \(ScreenCornerState m') -> ScreenCornerState $ M.insert win (corner, xFunc) m'
|
||||||
Nothing -> (, xF) <$> createWindowAt corner
|
|
||||||
|
|
||||||
XS.modify $ \(ScreenCornerState m') -> ScreenCornerState $ M.insert win (corner,xFunc) m'
|
|
||||||
|
|
||||||
-- | Add a list of @(ScreenCorner, X ())@ tuples
|
-- | Add a list of @(ScreenCorner, X ())@ tuples
|
||||||
addScreenCorners :: [ (ScreenCorner, X ()) ] -> X ()
|
addScreenCorners :: [(ScreenCorner, X ())] -> X ()
|
||||||
addScreenCorners = mapM_ (uncurry addScreenCorner)
|
addScreenCorners = mapM_ (uncurry addScreenCorner)
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Xlib functions
|
-- Xlib functions
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
@ -85,33 +81,27 @@ createWindowAt SCUpperLeft = createWindowAt' 0 0 1 1
|
|||||||
createWindowAt SCUpperRight = withDisplay $ \dpy ->
|
createWindowAt SCUpperRight = withDisplay $ \dpy ->
|
||||||
let w = displayWidth dpy (defaultScreen dpy) - 1
|
let w = displayWidth dpy (defaultScreen dpy) - 1
|
||||||
in createWindowAt' (fi w) 0 1 1
|
in createWindowAt' (fi w) 0 1 1
|
||||||
|
|
||||||
createWindowAt SCLowerLeft = withDisplay $ \dpy ->
|
createWindowAt SCLowerLeft = withDisplay $ \dpy ->
|
||||||
let h = displayHeight dpy (defaultScreen dpy) - 1
|
let h = displayHeight dpy (defaultScreen dpy) - 1
|
||||||
in createWindowAt' 0 (fi h) 1 1
|
in createWindowAt' 0 (fi h) 1 1
|
||||||
|
|
||||||
createWindowAt SCLowerRight = withDisplay $ \dpy ->
|
createWindowAt SCLowerRight = withDisplay $ \dpy ->
|
||||||
let w = displayWidth dpy (defaultScreen dpy) - 1
|
let w = displayWidth dpy (defaultScreen dpy) - 1
|
||||||
h = displayHeight dpy (defaultScreen dpy) - 1
|
h = displayHeight dpy (defaultScreen dpy) - 1
|
||||||
in createWindowAt' (fi w) (fi h) 1 1
|
in createWindowAt' (fi w) (fi h) 1 1
|
||||||
|
|
||||||
createWindowAt SCTop = withDisplay $ \dpy ->
|
createWindowAt SCTop = withDisplay $ \dpy ->
|
||||||
let w = displayWidth dpy (defaultScreen dpy) - 1
|
let w = displayWidth dpy (defaultScreen dpy) - 1
|
||||||
-- leave some gap so corner and edge can work nicely when they overlap
|
-- leave some gap so corner and edge can work nicely when they overlap
|
||||||
threshold = 150
|
threshold = 150
|
||||||
in createWindowAt' threshold 0 (fi $ fi w - threshold * 2) 1
|
in createWindowAt' threshold 0 (fi $ fi w - threshold * 2) 1
|
||||||
|
|
||||||
createWindowAt SCBottom = withDisplay $ \dpy ->
|
createWindowAt SCBottom = withDisplay $ \dpy ->
|
||||||
let w = displayWidth dpy (defaultScreen dpy) - 1
|
let w = displayWidth dpy (defaultScreen dpy) - 1
|
||||||
h = displayHeight dpy (defaultScreen dpy) - 1
|
h = displayHeight dpy (defaultScreen dpy) - 1
|
||||||
threshold = 150
|
threshold = 150
|
||||||
in createWindowAt' threshold (fi h) (fi $ fi w - threshold * 2) 1
|
in createWindowAt' threshold (fi h) (fi $ fi w - threshold * 2) 1
|
||||||
|
|
||||||
createWindowAt SCLeft = withDisplay $ \dpy ->
|
createWindowAt SCLeft = withDisplay $ \dpy ->
|
||||||
let h = displayHeight dpy (defaultScreen dpy) - 1
|
let h = displayHeight dpy (defaultScreen dpy) - 1
|
||||||
threshold = 150
|
threshold = 150
|
||||||
in createWindowAt' 0 threshold 1 (fi $ fi h - threshold * 2)
|
in createWindowAt' 0 threshold 1 (fi $ fi h - threshold * 2)
|
||||||
|
|
||||||
createWindowAt SCRight = withDisplay $ \dpy ->
|
createWindowAt SCRight = withDisplay $ \dpy ->
|
||||||
let w = displayWidth dpy (defaultScreen dpy) - 1
|
let w = displayWidth dpy (defaultScreen dpy) - 1
|
||||||
h = displayHeight dpy (defaultScreen dpy) - 1
|
h = displayHeight dpy (defaultScreen dpy) - 1
|
||||||
@ -121,17 +111,15 @@ createWindowAt SCRight = withDisplay $ \dpy ->
|
|||||||
-- Create a new X window at a (x,y) Position, with given width and height.
|
-- Create a new X window at a (x,y) Position, with given width and height.
|
||||||
createWindowAt' :: Position -> Position -> Dimension -> Dimension -> X Window
|
createWindowAt' :: Position -> Position -> Dimension -> Dimension -> X Window
|
||||||
createWindowAt' x y width height = withDisplay $ \dpy -> io $ do
|
createWindowAt' x y width height = withDisplay $ \dpy -> io $ do
|
||||||
|
|
||||||
rootw <- rootWindow dpy (defaultScreen dpy)
|
rootw <- rootWindow dpy (defaultScreen dpy)
|
||||||
|
|
||||||
let
|
let visual = defaultVisualOfScreen $ defaultScreenOfDisplay dpy
|
||||||
visual = defaultVisualOfScreen $ defaultScreenOfDisplay dpy
|
|
||||||
attrmask = cWOverrideRedirect
|
attrmask = cWOverrideRedirect
|
||||||
|
|
||||||
w <- allocaSetWindowAttributes $ \attributes -> do
|
w <- allocaSetWindowAttributes $ \attributes -> do
|
||||||
|
|
||||||
set_override_redirect attributes True
|
set_override_redirect attributes True
|
||||||
createWindow dpy -- display
|
createWindow
|
||||||
|
dpy -- display
|
||||||
rootw -- parent window
|
rootw -- parent window
|
||||||
x -- x
|
x -- x
|
||||||
y -- y
|
y -- y
|
||||||
@ -156,8 +144,7 @@ createWindowAt' x y width height = withDisplay $ \dpy -> io $ do
|
|||||||
|
|
||||||
-- | Handle screen corner events
|
-- | Handle screen corner events
|
||||||
screenCornerEventHook :: Event -> X All
|
screenCornerEventHook :: Event -> X All
|
||||||
screenCornerEventHook CrossingEvent { ev_window = win } = do
|
screenCornerEventHook CrossingEvent {ev_window = win} = do
|
||||||
|
|
||||||
ScreenCornerState m <- XS.get
|
ScreenCornerState m <- XS.get
|
||||||
|
|
||||||
case M.lookup win m of
|
case M.lookup win m of
|
||||||
@ -165,16 +152,14 @@ screenCornerEventHook CrossingEvent { ev_window = win } = do
|
|||||||
Nothing -> return ()
|
Nothing -> return ()
|
||||||
|
|
||||||
return (All True)
|
return (All True)
|
||||||
|
|
||||||
screenCornerEventHook _ = return (All True)
|
screenCornerEventHook _ = return (All True)
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Layout hook
|
-- Layout hook
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
data ScreenCornerLayout a = ScreenCornerLayout
|
data ScreenCornerLayout a = ScreenCornerLayout
|
||||||
deriving ( Read, Show )
|
deriving (Read, Show)
|
||||||
|
|
||||||
instance LayoutModifier ScreenCornerLayout a where
|
instance LayoutModifier ScreenCornerLayout a where
|
||||||
hook ScreenCornerLayout = withDisplay $ \dpy -> do
|
hook ScreenCornerLayout = withDisplay $ \dpy -> do
|
||||||
@ -185,8 +170,8 @@ instance LayoutModifier ScreenCornerLayout a where
|
|||||||
screenCornerLayoutHook :: l a -> ModifiedLayout ScreenCornerLayout l a
|
screenCornerLayoutHook :: l a -> ModifiedLayout ScreenCornerLayout l a
|
||||||
screenCornerLayoutHook = ModifiedLayout ScreenCornerLayout
|
screenCornerLayoutHook = ModifiedLayout ScreenCornerLayout
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
-- This extension adds KDE-like screen corners and GNOME Hot Edge like
|
-- This extension adds KDE-like screen corners and GNOME Hot Edge like
|
||||||
|
@ -426,12 +426,12 @@ statusBarPipe cmd xpp = do
|
|||||||
-- > xmobarBottom = statusBarPropTo "_XMONAD_LOG_2" "xmobar -x 0 ~/.config/xmobar/xmobarrc_bottom" (pure ppBottom)
|
-- > xmobarBottom = statusBarPropTo "_XMONAD_LOG_2" "xmobar -x 0 ~/.config/xmobar/xmobarrc_bottom" (pure ppBottom)
|
||||||
-- > xmobar1 = statusBarPropTo "_XMONAD_LOG_3" "xmobar -x 1 ~/.config/xmobar/xmobarrc1" (pure pp1)
|
-- > xmobar1 = statusBarPropTo "_XMONAD_LOG_3" "xmobar -x 1 ~/.config/xmobar/xmobarrc1" (pure pp1)
|
||||||
-- >
|
-- >
|
||||||
-- > barSpawner :: ScreenId -> IO StatusBarConfig
|
-- > barSpawner :: ScreenId -> StatusBarConfig
|
||||||
-- > barSpawner 0 = pure $ xmobarTop <> xmobarBottom -- two bars on the main screen
|
-- > barSpawner 0 = xmobarTop <> xmobarBottom -- two bars on the main screen
|
||||||
-- > barSpawner 1 = pure $ xmobar1
|
-- > barSpawner 1 = xmobar1
|
||||||
-- > barSpawner _ = mempty -- nothing on the rest of the screens
|
-- > barSpawner _ = mempty -- nothing on the rest of the screens
|
||||||
-- >
|
-- >
|
||||||
-- > main = xmonad $ dynamicSBs barSpawner (def { ... })
|
-- > main = xmonad $ dynamicSBs (pure . barSpawner) (def { ... })
|
||||||
--
|
--
|
||||||
-- Make sure you specify which screen to place the status bar on (in xmobar,
|
-- Make sure you specify which screen to place the status bar on (in xmobar,
|
||||||
-- this is achieved by the @-x@ argument). In addition to making sure that your
|
-- this is achieved by the @-x@ argument). In addition to making sure that your
|
||||||
|
@ -116,7 +116,7 @@ data CircleExMsg
|
|||||||
= Rotate !Double -- ^ Rotate secondary windows by specific angle
|
= Rotate !Double -- ^ Rotate secondary windows by specific angle
|
||||||
| IncStackRatio !Rational -- ^ Increase (or decrease, with negative value) sizes of secondary windows
|
| IncStackRatio !Rational -- ^ Increase (or decrease, with negative value) sizes of secondary windows
|
||||||
| IncMultiplier !Rational -- ^ Increase 'cMultiplier'.
|
| IncMultiplier !Rational -- ^ Increase 'cMultiplier'.
|
||||||
deriving (Eq, Show, Typeable)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
instance Message CircleExMsg
|
instance Message CircleExMsg
|
||||||
|
|
||||||
|
@ -158,6 +158,5 @@ data ManageAspectRatio =
|
|||||||
FixRatio Rational Window -- ^ Set the aspect ratio for the window
|
FixRatio Rational Window -- ^ Set the aspect ratio for the window
|
||||||
| ResetRatio Window -- ^ Remove the aspect ratio for the window
|
| ResetRatio Window -- ^ Remove the aspect ratio for the window
|
||||||
| ToggleRatio Rational Window -- ^ Toggle the reatio
|
| ToggleRatio Rational Window -- ^ Toggle the reatio
|
||||||
deriving Typeable
|
|
||||||
|
|
||||||
instance Message ManageAspectRatio
|
instance Message ManageAspectRatio
|
||||||
|
@ -26,8 +26,8 @@ module XMonad.Layout.IndependentScreens (
|
|||||||
marshallPP,
|
marshallPP,
|
||||||
whenCurrentOn,
|
whenCurrentOn,
|
||||||
countScreens,
|
countScreens,
|
||||||
workspacesOn,
|
workspacesOn, screenOnMonitor,
|
||||||
workspaceOnScreen, focusWindow', focusScreen, nthWorkspace, withWspOnScreen,
|
workspaceOnScreen, focusWindow', doFocus', focusScreen, focusWorkspace, nthWorkspace, withWspOnScreen,
|
||||||
-- * Converting between virtual and physical workspaces
|
-- * Converting between virtual and physical workspaces
|
||||||
-- $converting
|
-- $converting
|
||||||
marshall, unmarshall, unmarshallS, unmarshallW,
|
marshall, unmarshall, unmarshallS, unmarshallW,
|
||||||
@ -40,6 +40,7 @@ import XMonad
|
|||||||
import XMonad.Hooks.StatusBar.PP
|
import XMonad.Hooks.StatusBar.PP
|
||||||
import XMonad.Prelude
|
import XMonad.Prelude
|
||||||
import qualified XMonad.StackSet as W
|
import qualified XMonad.StackSet as W
|
||||||
|
import XMonad.Actions.OnScreen (viewOnScreen)
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
@ -147,7 +148,7 @@ withWspOnScreen screenId operation ws = case workspaceOnScreen screenId ws of
|
|||||||
Just wsp -> operation wsp ws
|
Just wsp -> operation wsp ws
|
||||||
Nothing -> ws
|
Nothing -> ws
|
||||||
|
|
||||||
-- | Get the workspace that is active on a given screen.
|
-- | Get the screen that is active on a given monitor.
|
||||||
screenOnMonitor :: ScreenId -> WindowSet -> Maybe WindowScreen
|
screenOnMonitor :: ScreenId -> WindowSet -> Maybe WindowScreen
|
||||||
screenOnMonitor screenId ws = find ((screenId ==) . W.screen) (W.current ws : W.visible ws)
|
screenOnMonitor screenId ws = find ((screenId ==) . W.screen) (W.current ws : W.visible ws)
|
||||||
|
|
||||||
@ -159,10 +160,20 @@ focusWindow' window ws
|
|||||||
Just tag -> W.focusWindow window $ focusScreen (unmarshallS tag) ws
|
Just tag -> W.focusWindow window $ focusScreen (unmarshallS tag) ws
|
||||||
Nothing -> ws
|
Nothing -> ws
|
||||||
|
|
||||||
|
-- | ManageHook to focus a window, switching workspace on the correct Xinerama screen if neccessary.
|
||||||
|
-- Useful in 'XMonad.Hooks.EwmhDesktops.setActivateHook' when using this module.
|
||||||
|
doFocus' :: ManageHook
|
||||||
|
doFocus' = doF . focusWindow' =<< ask
|
||||||
|
|
||||||
-- | Focus a given screen.
|
-- | Focus a given screen.
|
||||||
focusScreen :: ScreenId -> WindowSet -> WindowSet
|
focusScreen :: ScreenId -> WindowSet -> WindowSet
|
||||||
focusScreen screenId = withWspOnScreen screenId W.view
|
focusScreen screenId = withWspOnScreen screenId W.view
|
||||||
|
|
||||||
|
-- | Focus the given workspace on the correct Xinerama screen.
|
||||||
|
-- An example usage can be found at `XMonad.Hooks.EwmhDesktops.setEwmhSwitchDesktopHook`
|
||||||
|
focusWorkspace :: WorkspaceId -> WindowSet -> WindowSet
|
||||||
|
focusWorkspace workspaceId = viewOnScreen (unmarshallS workspaceId) workspaceId
|
||||||
|
|
||||||
-- | Get the nth virtual workspace
|
-- | Get the nth virtual workspace
|
||||||
nthWorkspace :: Int -> X (Maybe VirtualWorkspace)
|
nthWorkspace :: Int -> X (Maybe VirtualWorkspace)
|
||||||
nthWorkspace n = (!? n) . workspaces' <$> asks config
|
nthWorkspace n = (!? n) . workspaces' <$> asks config
|
||||||
|
@ -143,10 +143,8 @@ data ConfigurableBorder p w = ConfigurableBorder
|
|||||||
|
|
||||||
-- | Only necessary with 'BorderMessage' - remove non-existent windows from the
|
-- | Only necessary with 'BorderMessage' - remove non-existent windows from the
|
||||||
-- 'alwaysHidden' or 'neverHidden' lists.
|
-- 'alwaysHidden' or 'neverHidden' lists.
|
||||||
|
{-# DEPRECATED borderEventHook "No longer needed." #-}
|
||||||
borderEventHook :: Event -> X All
|
borderEventHook :: Event -> X All
|
||||||
borderEventHook DestroyWindowEvent{ ev_window = w } = do
|
|
||||||
broadcastMessage $ ResetBorder w
|
|
||||||
return $ All True
|
|
||||||
borderEventHook _ = return $ All True
|
borderEventHook _ = return $ All True
|
||||||
|
|
||||||
instance (Read p, Show p, SetsAmbiguous p) => LayoutModifier (ConfigurableBorder p) Window where
|
instance (Read p, Show p, SetsAmbiguous p) => LayoutModifier (ConfigurableBorder p) Window where
|
||||||
@ -167,14 +165,17 @@ instance (Read p, Show p, SetsAmbiguous p) => LayoutModifier (ConfigurableBorder
|
|||||||
in ConfigurableBorder gh <$> consNewIf ah (not b)
|
in ConfigurableBorder gh <$> consNewIf ah (not b)
|
||||||
<*> consNewIf nh b
|
<*> consNewIf nh b
|
||||||
<*> pure ch
|
<*> pure ch
|
||||||
| Just (ResetBorder w) <- fromMessage m =
|
| Just (ResetBorder w) <- fromMessage m = resetBorder w
|
||||||
|
| Just DestroyWindowEvent { ev_window = w } <- fromMessage m = resetBorder w
|
||||||
|
| otherwise = Nothing
|
||||||
|
where
|
||||||
|
resetBorder w =
|
||||||
let delete' e l = if e `elem` l then (True,delete e l) else (False,l)
|
let delete' e l = if e `elem` l then (True,delete e l) else (False,l)
|
||||||
(da,ah') = delete' w ah
|
(da,ah') = delete' w ah
|
||||||
(dn,nh') = delete' w nh
|
(dn,nh') = delete' w nh
|
||||||
in if da || dn
|
in if da || dn
|
||||||
then Just cb { alwaysHidden = ah', neverHidden = nh' }
|
then Just cb { alwaysHidden = ah', neverHidden = nh' }
|
||||||
else Nothing
|
else Nothing
|
||||||
| otherwise = Nothing
|
|
||||||
|
|
||||||
-- | SetsAmbiguous allows custom actions to generate lists of windows that
|
-- | SetsAmbiguous allows custom actions to generate lists of windows that
|
||||||
-- should not have borders drawn through 'ConfigurableBorder'
|
-- should not have borders drawn through 'ConfigurableBorder'
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
{-# LANGUAGE LambdaCase #-}
|
||||||
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Layout.OnHost
|
-- Module : XMonad.Layout.OnHost
|
||||||
@ -27,10 +28,12 @@ module XMonad.Layout.OnHost (-- * Usage
|
|||||||
|
|
||||||
import XMonad
|
import XMonad
|
||||||
import qualified XMonad.StackSet as W
|
import qualified XMonad.StackSet as W
|
||||||
|
import XMonad.Prelude
|
||||||
|
|
||||||
import XMonad.Layout.LayoutModifier
|
import XMonad.Layout.LayoutModifier
|
||||||
|
|
||||||
import Data.Maybe (fromMaybe)
|
import Foreign (allocaArray0)
|
||||||
|
import Foreign.C
|
||||||
import System.Posix.Env (getEnv)
|
import System.Posix.Env (getEnv)
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
@ -56,11 +59,13 @@ import System.Posix.Env (getEnv)
|
|||||||
--
|
--
|
||||||
-- > layoutHook = A ||| B ||| onHost "foo" D C
|
-- > layoutHook = A ||| B ||| onHost "foo" D C
|
||||||
--
|
--
|
||||||
-- Note that we rely on '$HOST' being set in the environment, as is true on most
|
-- Note that we rely on either @$HOST@ being set in the environment, or
|
||||||
-- modern systems; if it's not, you may want to use a wrapper around xmonad or
|
-- <https://linux.die.net/man/2/gethostname gethostname> returning something
|
||||||
-- perhaps use 'System.Posix.Env.setEnv' (or 'putEnv') to set it in 'main'.
|
-- useful, as is true on most modern systems; if this is not the case for you,
|
||||||
-- This is to avoid dragging in the network package as an xmonad dependency.
|
-- you may want to use a wrapper around xmonad or perhaps use
|
||||||
-- If '$HOST' is not defined, it will behave as if the host name never matches.
|
-- 'System.Posix.Env.setEnv' (or 'putEnv') to set @$HOST@ in 'main'. If
|
||||||
|
-- neither of the two methods work, the module will behave as if the host name
|
||||||
|
-- never matches.
|
||||||
--
|
--
|
||||||
-- Also note that '$HOST' is usually a fully qualified domain name, not a short name.
|
-- Also note that '$HOST' is usually a fully qualified domain name, not a short name.
|
||||||
-- If you use a short name, this code will try to truncate $HOST to match; this may
|
-- If you use a short name, this code will try to truncate $HOST to match; this may
|
||||||
@ -116,16 +121,16 @@ data OnHost l1 l2 a = OnHost [String]
|
|||||||
|
|
||||||
instance (LayoutClass l1 a, LayoutClass l2 a, Show a) => LayoutClass (OnHost l1 l2) a where
|
instance (LayoutClass l1 a, LayoutClass l2 a, Show a) => LayoutClass (OnHost l1 l2) a where
|
||||||
runLayout (W.Workspace i p@(OnHost hosts _ lt lf) ms) r = do
|
runLayout (W.Workspace i p@(OnHost hosts _ lt lf) ms) r = do
|
||||||
h <- io $ getEnv "HOST"
|
h <- io $ getEnv "HOST" <|> getHostName
|
||||||
if maybe False (`elemFQDN` hosts) h
|
if maybe False (`elemFQDN` hosts) h
|
||||||
then do (wrs, mlt') <- runLayout (W.Workspace i lt ms) r
|
then do (wrs, mlt') <- runLayout (W.Workspace i lt ms) r
|
||||||
return (wrs, Just $ mkNewOnHostT p mlt')
|
return (wrs, Just $ mkNewOnHostT p mlt')
|
||||||
else do (wrs, mlt') <- runLayout (W.Workspace i lf ms) r
|
else do (wrs, mlt') <- runLayout (W.Workspace i lf ms) r
|
||||||
return (wrs, Just $ mkNewOnHostF p mlt')
|
return (wrs, Just $ mkNewOnHostF p mlt')
|
||||||
|
|
||||||
handleMessage (OnHost hosts bool lt lf) m
|
handleMessage (OnHost hosts choice lt lf) m
|
||||||
| bool = handleMessage lt m >>= maybe (return Nothing) (\nt -> return . Just $ OnHost hosts bool nt lf)
|
| choice = handleMessage lt m >>= maybe (return Nothing) (\nt -> return . Just $ OnHost hosts choice nt lf)
|
||||||
| otherwise = handleMessage lf m >>= maybe (return Nothing) (return . Just . OnHost hosts bool lt)
|
| otherwise = handleMessage lf m >>= maybe (return Nothing) (return . Just . OnHost hosts choice lt)
|
||||||
|
|
||||||
description (OnHost _ True l1 _) = description l1
|
description (OnHost _ True l1 _) = description l1
|
||||||
description (OnHost _ _ _ l2) = description l2
|
description (OnHost _ _ _ l2) = description l2
|
||||||
@ -154,3 +159,17 @@ eqFQDN a b
|
|||||||
| '.' `elem` a = takeWhile (/= '.') a == b
|
| '.' `elem` a = takeWhile (/= '.') a == b
|
||||||
| '.' `elem` b = a == takeWhile (/= '.') b
|
| '.' `elem` b = a == takeWhile (/= '.') b
|
||||||
| otherwise = a == b
|
| otherwise = a == b
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------
|
||||||
|
-- cbits
|
||||||
|
|
||||||
|
foreign import ccall "gethostname" gethostname :: CString -> CSize -> IO CInt
|
||||||
|
|
||||||
|
getHostName :: IO (Maybe String)
|
||||||
|
getHostName = allocaArray0 size $ \cstr -> do
|
||||||
|
throwErrnoIfMinus1_ "getHostName" $ gethostname cstr (fromIntegral size)
|
||||||
|
peekCString cstr <&> \case
|
||||||
|
"" -> Nothing
|
||||||
|
s -> Just s
|
||||||
|
where
|
||||||
|
size = 256
|
||||||
|
@ -313,6 +313,7 @@ specialKeys =
|
|||||||
, ("KP_7" , xK_KP_7)
|
, ("KP_7" , xK_KP_7)
|
||||||
, ("KP_8" , xK_KP_8)
|
, ("KP_8" , xK_KP_8)
|
||||||
, ("KP_9" , xK_KP_9)
|
, ("KP_9" , xK_KP_9)
|
||||||
|
, ("Menu" , xK_Menu)
|
||||||
]
|
]
|
||||||
|
|
||||||
-- | List of multimedia keys. If Xlib does not know about some keysym
|
-- | List of multimedia keys. If Xlib does not know about some keysym
|
||||||
@ -472,6 +473,7 @@ multimediaKeys = filter ((/= noSymbol) . snd) . map (id &&& stringToKeysym) $
|
|||||||
, "XF86_Next_VMode"
|
, "XF86_Next_VMode"
|
||||||
, "XF86_Prev_VMode"
|
, "XF86_Prev_VMode"
|
||||||
, "XF86Bluetooth"
|
, "XF86Bluetooth"
|
||||||
|
, "XF86WLAN"
|
||||||
]
|
]
|
||||||
|
|
||||||
-- | The specialized 'W.Screen' derived from 'WindowSet'.
|
-- | The specialized 'W.Screen' derived from 'WindowSet'.
|
||||||
|
@ -419,6 +419,7 @@ infixl 4 `removeMouseBindings`
|
|||||||
-- > <XF86_Next_VMode>
|
-- > <XF86_Next_VMode>
|
||||||
-- > <XF86_Prev_VMode>
|
-- > <XF86_Prev_VMode>
|
||||||
-- > <XF86Bluetooth>
|
-- > <XF86Bluetooth>
|
||||||
|
-- > <XF86WLAN>
|
||||||
|
|
||||||
mkKeymap :: XConfig l -> [(String, X ())] -> M.Map (KeyMask, KeySym) (X ())
|
mkKeymap :: XConfig l -> [(String, X ())] -> M.Map (KeyMask, KeySym) (X ())
|
||||||
mkKeymap c = M.fromList . mkSubmaps . readKeymap c
|
mkKeymap c = M.fromList . mkSubmaps . readKeymap c
|
||||||
|
@ -309,7 +309,7 @@ nsSingleScratchpadPerWorkspace :: NamedScratchpads -> X ()
|
|||||||
nsSingleScratchpadPerWorkspace scratches =
|
nsSingleScratchpadPerWorkspace scratches =
|
||||||
nsHideOnCondition $ \ _lastFocus curFocus winSet hideScratch -> do
|
nsHideOnCondition $ \ _lastFocus curFocus winSet hideScratch -> do
|
||||||
allScratchesButCurrent <-
|
allScratchesButCurrent <-
|
||||||
filterM (liftA2 (<||>) (pure . (/= curFocus)) (`isNSP` scratches))
|
filterM (liftA2 (<&&>) (pure . (/= curFocus)) (`isNSP` scratches))
|
||||||
(W.index winSet)
|
(W.index winSet)
|
||||||
whenX (isNSP curFocus scratches) $
|
whenX (isNSP curFocus scratches) $
|
||||||
for_ allScratchesButCurrent hideScratch
|
for_ allScratchesButCurrent hideScratch
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
# See NIX.md for an overview of module usage.
|
# See 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";
|
||||||
xmonad.url = github:xmonad/xmonad;
|
xmonad.url = "github:xmonad/xmonad";
|
||||||
};
|
};
|
||||||
outputs = { self, flake-utils, nixpkgs, git-ignore-nix, xmonad }:
|
outputs = { self, flake-utils, nixpkgs, git-ignore-nix, xmonad }:
|
||||||
with xmonad.lib;
|
with xmonad.lib;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
name: xmonad-contrib
|
name: xmonad-contrib
|
||||||
version: 0.18.1
|
version: 0.18.1.9
|
||||||
-- ^ also update cpp-options: -DXMONAD_CONTRIB_VERSION_*
|
-- ^ also update cpp-options: -DXMONAD_CONTRIB_VERSION_*
|
||||||
|
|
||||||
homepage: https://xmonad.org/
|
homepage: https://xmonad.org/
|
||||||
@ -38,7 +38,7 @@ cabal-version: 1.12
|
|||||||
build-type: Simple
|
build-type: Simple
|
||||||
bug-reports: https://github.com/xmonad/xmonad-contrib/issues
|
bug-reports: https://github.com/xmonad/xmonad-contrib/issues
|
||||||
|
|
||||||
tested-with: GHC == 8.6.5 || == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.8 || == 9.4.8 || == 9.6.6 || == 9.8.2 || == 9.10.1
|
tested-with: GHC == 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
|
||||||
|
|
||||||
source-repository head
|
source-repository head
|
||||||
type: git
|
type: git
|
||||||
@ -56,7 +56,7 @@ flag pedantic
|
|||||||
library
|
library
|
||||||
build-depends: base >= 4.12 && < 5,
|
build-depends: base >= 4.12 && < 5,
|
||||||
bytestring >= 0.10 && < 0.13,
|
bytestring >= 0.10 && < 0.13,
|
||||||
containers >= 0.5 && < 0.8,
|
containers >= 0.5 && < 0.9,
|
||||||
directory,
|
directory,
|
||||||
filepath,
|
filepath,
|
||||||
time >= 1.8 && < 1.15,
|
time >= 1.8 && < 1.15,
|
||||||
@ -80,7 +80,7 @@ library
|
|||||||
ghc-options: -Werror -Wwarn=deprecations
|
ghc-options: -Werror -Wwarn=deprecations
|
||||||
|
|
||||||
-- 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.8.4)
|
||||||
-- don't treat unused-imports warning as errors, they may be necessary
|
-- don't treat unused-imports warning as errors, they may be necessary
|
||||||
-- for compatibility with older versions of base (or other deps)
|
-- for compatibility with older versions of base (or other deps)
|
||||||
ghc-options: -Wwarn=unused-imports
|
ghc-options: -Wwarn=unused-imports
|
||||||
@ -154,6 +154,7 @@ library
|
|||||||
XMonad.Actions.TreeSelect
|
XMonad.Actions.TreeSelect
|
||||||
XMonad.Actions.UpdateFocus
|
XMonad.Actions.UpdateFocus
|
||||||
XMonad.Actions.UpdatePointer
|
XMonad.Actions.UpdatePointer
|
||||||
|
XMonad.Actions.UpKeys
|
||||||
XMonad.Actions.Warp
|
XMonad.Actions.Warp
|
||||||
XMonad.Actions.WindowBringer
|
XMonad.Actions.WindowBringer
|
||||||
XMonad.Actions.WindowGo
|
XMonad.Actions.WindowGo
|
||||||
@ -506,7 +507,7 @@ test-suite tests
|
|||||||
ghc-options: -Werror -Wwarn=deprecations
|
ghc-options: -Werror -Wwarn=deprecations
|
||||||
|
|
||||||
-- 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.8.4)
|
||||||
-- don't treat unused-imports warning as errors, they may be necessary
|
-- don't treat unused-imports warning as errors, they may be necessary
|
||||||
-- for compatibility with older versions of base (or other deps)
|
-- for compatibility with older versions of base (or other deps)
|
||||||
ghc-options: -Wwarn=unused-imports
|
ghc-options: -Wwarn=unused-imports
|
||||||
|
Loading…
x
Reference in New Issue
Block a user