mirror of
https://github.com/xmonad/xmonad-contrib.git
synced 2025-08-12 10:45:57 -07:00
Compare commits
92 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
60101b9a70 | ||
|
5f1fc602ab | ||
|
303f0c24bc | ||
|
8df80e7805 | ||
|
36dba39c44 | ||
|
8847b3b2f6 | ||
|
1b061245af | ||
|
94662bffc6 | ||
|
23102a6d5c | ||
|
728f9bc270 | ||
|
d9856b955a | ||
|
603fc4ccb7 | ||
|
65e7153874 | ||
|
6a0dc1685c | ||
|
41e8708eb5 | ||
|
18979de5f6 | ||
|
5ffb4e7f52 | ||
|
52a3800b96 | ||
|
dd89eae446 | ||
|
6b68ec5c00 | ||
|
db80842e65 | ||
|
4e4856c722 | ||
|
b5eb418fa4 | ||
|
04b32ae021 | ||
|
9c6d1e696f | ||
|
dad911913c | ||
|
b3f5a09673 | ||
|
4ad6ecf892 | ||
|
fcd296e87a | ||
|
fddd5ea1fe | ||
|
c3cee11ad6 | ||
|
22e6d4b017 | ||
|
fcced8991a | ||
|
bfb52a5025 | ||
|
c53436f3a6 | ||
|
66c1977c29 | ||
|
a18395a13c | ||
|
ca69574cfe | ||
|
faa252e309 | ||
|
71fec4f61a | ||
|
0301d6e21b | ||
|
0cfe3a288e | ||
|
84c63abda6 | ||
|
e12511c658 | ||
|
52890f6007 | ||
|
51bc32ea75 | ||
|
cce7f50372 | ||
|
ba9b108a68 | ||
|
e12c047870 | ||
|
2ce876c330 | ||
|
3faa9b2c38 | ||
|
6720eefce7 | ||
|
cf789504e8 | ||
|
deaaf6b177 | ||
|
60b35ff431 | ||
|
cd404eb644 | ||
|
d9f43c78d6 | ||
|
b9603a0e10 | ||
|
09a5fa111c | ||
|
41a2db5563 | ||
|
1706160b14 | ||
|
207d5962e2 | ||
|
c7e02726bf | ||
|
b0d6e0f942 | ||
|
a774168415 | ||
|
17da8bc8ee | ||
|
c2c0585d7e | ||
|
7a6d24712f | ||
|
13260cae58 | ||
|
07f4ce8029 | ||
|
298cdd6114 | ||
|
36356dd8f5 | ||
|
dd905d2603 | ||
|
dda242a459 | ||
|
204524328d | ||
|
6dcc36c904 | ||
|
705494eb4c | ||
|
6b9fb096d6 | ||
|
913183463a | ||
|
92fe5f34ff | ||
|
8c309f87b8 | ||
|
f6f925c823 | ||
|
203e63b055 | ||
|
e814f748b5 | ||
|
ed32ccd080 | ||
|
2314dd628a | ||
|
95cd118c9a | ||
|
53481b2269 | ||
|
c1c7c30532 | ||
|
617099badd | ||
|
b05f5f12cf | ||
|
270ca2da23 |
224
.travis.yml
224
.travis.yml
@@ -1,99 +1,149 @@
|
|||||||
# This file has been generated -- see https://github.com/hvr/multi-ghc-travis
|
# This Travis job script has been generated by a script via
|
||||||
|
#
|
||||||
|
# haskell-ci '-o' '.travis.yml' 'xmonad-contrib.cabal' '--apt' 'libxrandr-dev'
|
||||||
|
#
|
||||||
|
# For more information, see https://github.com/haskell-CI/haskell-ci
|
||||||
|
#
|
||||||
|
# version: 0.5.20190916
|
||||||
|
#
|
||||||
language: c
|
language: c
|
||||||
sudo: false
|
dist: xenial
|
||||||
|
git:
|
||||||
|
# whether to recursively clone submodules
|
||||||
|
submodules: false
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- $HOME/.cabsnap
|
|
||||||
- $HOME/.cabal/packages
|
- $HOME/.cabal/packages
|
||||||
|
- $HOME/.cabal/store
|
||||||
before_cache:
|
before_cache:
|
||||||
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
|
- rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log
|
||||||
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.tar
|
# remove files that are regenerated by 'cabal update'
|
||||||
|
- rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*
|
||||||
|
- rm -fv $CABALHOME/packages/hackage.haskell.org/*.json
|
||||||
|
- rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache
|
||||||
|
- rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar
|
||||||
|
- rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx
|
||||||
|
- rm -rfv $CABALHOME/packages/head.hackage
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- env: GHCVER=8.6.1 CABALVER=2.4
|
- compiler: ghc-8.8.1
|
||||||
compiler: ": #GHC 8.6.1"
|
addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.8.1","cabal-install-3.0","libxrandr-dev"]}}
|
||||||
addons: { apt: { packages: [cabal-install-2.4, ghc-8.6.1, libxrandr-dev]
|
- compiler: ghc-8.6.5
|
||||||
, sources: [hvr-ghc]
|
addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.6.5","cabal-install-3.0","libxrandr-dev"]}}
|
||||||
} }
|
- compiler: ghc-8.4.4
|
||||||
- env: GHCVER=8.4.3 CABALVER=2.2
|
addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.4.4","cabal-install-3.0","libxrandr-dev"]}}
|
||||||
compiler: ": #GHC 8.4.3"
|
- compiler: ghc-8.2.2
|
||||||
addons: { apt: { packages: [cabal-install-2.2, ghc-8.4.3, libxrandr-dev]
|
addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.2.2","cabal-install-3.0","libxrandr-dev"]}}
|
||||||
, sources: [hvr-ghc]
|
- compiler: ghc-8.0.2
|
||||||
} }
|
addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.0.2","cabal-install-3.0","libxrandr-dev"]}}
|
||||||
- env: GHCVER=8.2.2 CABALVER=2.0
|
|
||||||
compiler: ": #GHC 8.2.2"
|
|
||||||
addons: { apt: { packages: [cabal-install-2.0, ghc-8.2.2, libxrandr-dev]
|
|
||||||
, sources: [hvr-ghc]
|
|
||||||
} }
|
|
||||||
- env: GHCVER=8.0.1 CABALVER=1.24
|
|
||||||
compiler: ": #GHC 8.0.1"
|
|
||||||
addons: { apt: { packages: [cabal-install-1.24, ghc-8.0.1, libxrandr-dev]
|
|
||||||
, sources: [hvr-ghc]
|
|
||||||
} }
|
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- unset CC
|
- HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//')
|
||||||
- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
|
- WITHCOMPILER="-w $HC"
|
||||||
|
- HADDOCK=$(echo "/opt/$CC/bin/haddock" | sed 's/-/\//')
|
||||||
|
- HCPKG="$HC-pkg"
|
||||||
|
- unset CC
|
||||||
|
- CABAL=/opt/ghc/bin/cabal
|
||||||
|
- CABALHOME=$HOME/.cabal
|
||||||
|
- export PATH="$CABALHOME/bin:$PATH"
|
||||||
|
- TOP=$(pwd)
|
||||||
|
- "HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')"
|
||||||
|
- echo $HCNUMVER
|
||||||
|
- CABAL="$CABAL -vnormal+nowrap+markoutput"
|
||||||
|
- set -o pipefail
|
||||||
|
- |
|
||||||
|
echo 'function blue(s) { printf "\033[0;34m" s "\033[0m " }' >> .colorful.awk
|
||||||
|
echo 'BEGIN { state = "output"; }' >> .colorful.awk
|
||||||
|
echo '/^-----BEGIN CABAL OUTPUT-----$/ { state = "cabal" }' >> .colorful.awk
|
||||||
|
echo '/^-----END CABAL OUTPUT-----$/ { state = "output" }' >> .colorful.awk
|
||||||
|
echo '!/^(-----BEGIN CABAL OUTPUT-----|-----END CABAL OUTPUT-----)/ {' >> .colorful.awk
|
||||||
|
echo ' if (state == "cabal") {' >> .colorful.awk
|
||||||
|
echo ' print blue($0)' >> .colorful.awk
|
||||||
|
echo ' } else {' >> .colorful.awk
|
||||||
|
echo ' print $0' >> .colorful.awk
|
||||||
|
echo ' }' >> .colorful.awk
|
||||||
|
echo '}' >> .colorful.awk
|
||||||
|
- cat .colorful.awk
|
||||||
|
- |
|
||||||
|
color_cabal_output () {
|
||||||
|
awk -f $TOP/.colorful.awk
|
||||||
|
}
|
||||||
|
- echo text | color_cabal_output
|
||||||
install:
|
install:
|
||||||
- cabal --version
|
- ${CABAL} --version
|
||||||
- echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]"
|
- echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
|
||||||
- if [ -f $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz ];
|
- TEST=--enable-tests
|
||||||
then
|
- BENCH=--enable-benchmarks
|
||||||
zcat $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz >
|
- HEADHACKAGE=false
|
||||||
$HOME/.cabal/packages/hackage.haskell.org/00-index.tar;
|
- rm -f $CABALHOME/config
|
||||||
fi
|
- |
|
||||||
- travis_retry cabal update -v
|
echo "verbose: normal +nowrap +markoutput" >> $CABALHOME/config
|
||||||
|
echo "remote-build-reporting: anonymous" >> $CABALHOME/config
|
||||||
# build xmonad from HEAD
|
echo "write-ghc-environment-files: always" >> $CABALHOME/config
|
||||||
- git clone https://github.com/xmonad/xmonad.git
|
echo "remote-repo-cache: $CABALHOME/packages" >> $CABALHOME/config
|
||||||
- cabal install xmonad/
|
echo "logs-dir: $CABALHOME/logs" >> $CABALHOME/config
|
||||||
|
echo "world-file: $CABALHOME/world" >> $CABALHOME/config
|
||||||
- sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
|
echo "extra-prog-path: $CABALHOME/bin" >> $CABALHOME/config
|
||||||
- cabal install --only-dependencies --enable-tests --enable-benchmarks --dry -v > installplan.txt
|
echo "symlink-bindir: $CABALHOME/bin" >> $CABALHOME/config
|
||||||
- sed -i -e '1,/^Resolving /d' installplan.txt; cat installplan.txt
|
echo "installdir: $CABALHOME/bin" >> $CABALHOME/config
|
||||||
|
echo "build-summary: $CABALHOME/logs/build.log" >> $CABALHOME/config
|
||||||
# check whether current requested install-plan matches cached package-db snapshot
|
echo "store-dir: $CABALHOME/store" >> $CABALHOME/config
|
||||||
- if diff -u $HOME/.cabsnap/installplan.txt installplan.txt;
|
echo "install-dirs user" >> $CABALHOME/config
|
||||||
then
|
echo " prefix: $CABALHOME" >> $CABALHOME/config
|
||||||
echo "cabal build-cache HIT";
|
echo "repository hackage.haskell.org" >> $CABALHOME/config
|
||||||
rm -rfv .ghc;
|
echo " url: http://hackage.haskell.org/" >> $CABALHOME/config
|
||||||
cp -a $HOME/.cabsnap/ghc $HOME/.ghc;
|
- |
|
||||||
cp -a $HOME/.cabsnap/lib $HOME/.cabsnap/share $HOME/.cabsnap/bin $HOME/.cabal/;
|
echo "program-default-options" >> $CABALHOME/config
|
||||||
else
|
echo " ghc-options: $GHCJOBS +RTS -M6G -RTS" >> $CABALHOME/config
|
||||||
echo "cabal build-cache MISS";
|
- cat $CABALHOME/config
|
||||||
rm -rf $HOME/.cabsnap;
|
- rm -fv cabal.project cabal.project.local cabal.project.freeze
|
||||||
mkdir -p $HOME/.ghc $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin;
|
- travis_retry ${CABAL} v2-update -v
|
||||||
fi
|
# Generate cabal.project
|
||||||
- cabal install --only-dependencies --enable-tests --enable-benchmarks;
|
- rm -rf cabal.project cabal.project.local cabal.project.freeze
|
||||||
|
- touch cabal.project
|
||||||
# snapshot package-db on cache miss
|
- |
|
||||||
- if [ ! -d $HOME/.cabsnap ];
|
echo "packages: ." >> cabal.project
|
||||||
then
|
- |
|
||||||
echo "snapshotting package-db to build-cache";
|
- "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(xmonad-contrib)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
|
||||||
mkdir $HOME/.cabsnap;
|
- cat cabal.project || true
|
||||||
cp -a $HOME/.ghc $HOME/.cabsnap/ghc;
|
- cat cabal.project.local || true
|
||||||
cp -a $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin installplan.txt $HOME/.cabsnap/;
|
- if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi
|
||||||
fi
|
- ${CABAL} v2-freeze $WITHCOMPILER ${TEST} ${BENCH} | color_cabal_output
|
||||||
|
- "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"
|
||||||
# Here starts the actual work to be performed for the package under test;
|
- rm cabal.project.freeze
|
||||||
# any command which exits with a non-zero exit code causes the build to fail.
|
- ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} --dep -j2 all | color_cabal_output
|
||||||
|
- ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --dep -j2 all | color_cabal_output
|
||||||
script:
|
script:
|
||||||
- if [ -f configure.ac ]; then autoreconf -i; fi
|
- DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
|
||||||
- cabal configure --enable-tests --enable-benchmarks -v2 # -v2 provides useful information for debugging
|
# Packaging...
|
||||||
- cabal build # this builds all libraries and executables (including tests/benchmarks)
|
- ${CABAL} v2-sdist all | color_cabal_output
|
||||||
- cabal test
|
# Unpacking...
|
||||||
# - cabal check # complains about -Werror even though it is
|
- mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/
|
||||||
# hidden behind a manual flag with default false
|
- cd ${DISTDIR} || false
|
||||||
- cabal sdist # tests that a source-distribution can be generated
|
- find . -maxdepth 1 -type f -name '*.tar.gz' -exec tar -xvf '{}' \;
|
||||||
|
- find . -maxdepth 1 -type f -name '*.tar.gz' -exec rm '{}' \;
|
||||||
# Check that the resulting source distribution can be built & installed.
|
- PKGDIR_xmonad_contrib="$(find . -maxdepth 1 -type d -regex '.*/xmonad-contrib-[0-9.]*')"
|
||||||
# If there are no other `.tar.gz` files in `dist`, this can be even simpler:
|
# Generate cabal.project
|
||||||
# `cabal install --force-reinstalls dist/*-*.tar.gz`
|
- rm -rf cabal.project cabal.project.local cabal.project.freeze
|
||||||
- SRC_TGZ=$(cabal info . | awk '{print $2;exit}').tar.gz &&
|
- touch cabal.project
|
||||||
(cd dist && cabal install --force-reinstalls "$SRC_TGZ")
|
- |
|
||||||
|
echo "packages: ${PKGDIR_xmonad_contrib}" >> cabal.project
|
||||||
|
- |
|
||||||
|
- "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(xmonad-contrib)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
|
||||||
|
- cat cabal.project || true
|
||||||
|
- cat cabal.project.local || true
|
||||||
|
# Building...
|
||||||
|
# this builds all libraries and executables (without tests/benchmarks)
|
||||||
|
- ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all | color_cabal_output
|
||||||
|
# Building with tests and benchmarks...
|
||||||
|
# build & run tests, build benchmarks
|
||||||
|
- ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output
|
||||||
|
# cabal check...
|
||||||
|
- (cd ${PKGDIR_xmonad_contrib} && ${CABAL} -vnormal check)
|
||||||
|
# haddock...
|
||||||
|
- ${CABAL} v2-haddock $WITHCOMPILER --with-haddock $HADDOCK ${TEST} ${BENCH} all | color_cabal_output
|
||||||
|
# Building without installed constraints for packages in global-db...
|
||||||
|
- rm -f cabal.project.local
|
||||||
|
- ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all | color_cabal_output
|
||||||
|
|
||||||
|
# REGENDATA ["-o",".travis.yml","xmonad-contrib.cabal","--apt","libxrandr-dev"]
|
||||||
# EOF
|
# EOF
|
||||||
|
121
CHANGES.md
121
CHANGES.md
@@ -1,6 +1,117 @@
|
|||||||
# Change Log / Release Notes
|
# Change Log / Release Notes
|
||||||
|
|
||||||
## unknown
|
## 0.16
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
* `XMonad.Layout.Decoration`
|
||||||
|
- Added `Theme` record fields for controlling decoration border width for active/inactive/urgent windows.
|
||||||
|
* `XMonad.Prompt`
|
||||||
|
|
||||||
|
- Prompt ships a vim-like keymap, see `vimLikeXPKeymap` and
|
||||||
|
`vimLikeXPKeymap'`. A reworked event loop supports new vim-like prompt
|
||||||
|
actions.
|
||||||
|
- Prompt supports dynamic colors. Colors are now specified by the `XPColor`
|
||||||
|
type in `XPState` while `XPConfig` colors remain unchanged for backwards
|
||||||
|
compatibility.
|
||||||
|
- Fixes `showCompletionOnTab`.
|
||||||
|
- The behavior of `moveWord` and `moveWord'` has changed; brought in line
|
||||||
|
with the documentation and now internally consistent. The old keymaps
|
||||||
|
retain the original behavior; see the documentation to do the same your
|
||||||
|
XMonad configuration.
|
||||||
|
* `XMonad.Util.Invisble`
|
||||||
|
- Requires `MonadFail` for `Read` instance
|
||||||
|
|
||||||
|
### New Modules
|
||||||
|
|
||||||
|
* `XMonad.Layout.TwoPanePersistent`
|
||||||
|
|
||||||
|
A layout that is like TwoPane but keeps track of the slave window that is
|
||||||
|
currently beside the master. In TwoPane, the default behavior when the master
|
||||||
|
is focused is to display the next window in the stack on the slave pane. This
|
||||||
|
is a problem when a different slave window is selected without changing the stack
|
||||||
|
order.
|
||||||
|
|
||||||
|
* `XMonad.Util.ExclusiveScratchpads`
|
||||||
|
|
||||||
|
Named scratchpads that can be mutually exclusive: This new module extends the
|
||||||
|
idea of named scratchpads such that you can define "families of scratchpads"
|
||||||
|
that are exclusive on the same screen. It also allows to remove this
|
||||||
|
constraint of being mutually exclusive with another scratchpad.
|
||||||
|
|
||||||
|
### Bug Fixes and Minor Changes
|
||||||
|
|
||||||
|
* `XMonad.Layout.Tabbed`
|
||||||
|
|
||||||
|
tabbedLeft and tabbedRight will set their tabs' height and width according to decoHeight/decoWidth
|
||||||
|
|
||||||
|
* `XMonad.Prompt`
|
||||||
|
|
||||||
|
Added `sorter` to `XPConfig` used to sort the possible completions by how
|
||||||
|
well they match the search string (example: `XMonad.Prompt.FuzzyMatch`).
|
||||||
|
|
||||||
|
Fixes a potential bug where an error during prompt execution would
|
||||||
|
leave the window open and keep the keyboard grabbed. See issue
|
||||||
|
[#180](https://github.com/xmonad/xmonad-contrib/issues/180).
|
||||||
|
|
||||||
|
Fixes [issue #217](https://github.com/xmonad/xmonad-contrib/issues/217), where
|
||||||
|
using tab to wrap around the completion rows would fail when maxComplRows is
|
||||||
|
restricting the number of rows of output.
|
||||||
|
|
||||||
|
* `XMonad.Prompt.Pass`
|
||||||
|
|
||||||
|
Added 'passOTPPrompt' to support getting OTP type password. This require
|
||||||
|
pass-otp (https://github.com/tadfisher/pass-otp) has been setup in the running
|
||||||
|
machine.
|
||||||
|
|
||||||
|
Added 'passGenerateAndCopyPrompt', which both generates a new password and
|
||||||
|
copies it to the clipboard. These two actions are commonly desirable to
|
||||||
|
take together, e.g. when establishing a new account.
|
||||||
|
|
||||||
|
Made password prompts traverse symlinks when gathering password names for
|
||||||
|
autocomplete.
|
||||||
|
|
||||||
|
* `XMonad.Actions.DynamicProjects`
|
||||||
|
|
||||||
|
Make the input directory read from the prompt in `DynamicProjects`
|
||||||
|
absolute wrt the current directory.
|
||||||
|
|
||||||
|
Before this, the directory set by the prompt was treated like a relative
|
||||||
|
directory. This means that when you switch from a project with directory
|
||||||
|
`foo` into a project with directory `bar`, xmonad actually tries to `cd`
|
||||||
|
into `foo/bar`, instead of `~/bar` as expected.
|
||||||
|
|
||||||
|
* `XMonad.Actions.DynamicWorkspaceOrder`
|
||||||
|
|
||||||
|
Add a version of `withNthWorkspace` that takes a `[WorkspaceId] ->
|
||||||
|
[WorkspaceId]` transformation to apply over the list of workspace tags
|
||||||
|
resulting from the dynamic order.
|
||||||
|
|
||||||
|
* `XMonad.Actions.GroupNavigation`
|
||||||
|
|
||||||
|
Add a utility function `isOnAnyVisibleWS :: Query Bool` to allow easy
|
||||||
|
cycling between all windows on all visible workspaces.
|
||||||
|
|
||||||
|
|
||||||
|
* `XMonad.Hooks.WallpaperSetter`
|
||||||
|
|
||||||
|
Preserve the aspect ratio of wallpapers that xmonad sets. When previous
|
||||||
|
versions would distort images to fit the screen size, it will now find a
|
||||||
|
best fit by cropping instead.
|
||||||
|
|
||||||
|
* `XMonad.Util.Themes`
|
||||||
|
|
||||||
|
Add adwaitaTheme and adwaitaDarkTheme to match their respective
|
||||||
|
GTK themes.
|
||||||
|
|
||||||
|
* 'XMonad.Layout.BinarySpacePartition'
|
||||||
|
|
||||||
|
Add a new `SplitShiftDirectional` message that allows moving windows by
|
||||||
|
splitting its neighbours.
|
||||||
|
|
||||||
|
* `XMonad.Prompt.FuzzyMatch`
|
||||||
|
|
||||||
|
Make fuzzy sort show shorter strings first.
|
||||||
|
|
||||||
## 0.15
|
## 0.15
|
||||||
|
|
||||||
@@ -164,6 +275,12 @@
|
|||||||
Provides a simple transformer for use with `XMonad.Layout.MultiToggle` to
|
Provides a simple transformer for use with `XMonad.Layout.MultiToggle` to
|
||||||
dynamically toggle `XMonad.Layout.TabBarDecoration`.
|
dynamically toggle `XMonad.Layout.TabBarDecoration`.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.RefocusLast`
|
||||||
|
|
||||||
|
Provides hooks and actions that keep track of recently focused windows on a
|
||||||
|
per workspace basis and automatically refocus the last window on loss of the
|
||||||
|
current (if appropriate as determined by user specified criteria).
|
||||||
|
|
||||||
* `XMonad.Layout.StateFull`
|
* `XMonad.Layout.StateFull`
|
||||||
|
|
||||||
Provides `StateFull`: a stateful form of `Full` that does not misbehave when
|
Provides `StateFull`: a stateful form of `Full` that does not misbehave when
|
||||||
@@ -349,6 +466,8 @@
|
|||||||
|
|
||||||
- New function `passTypePrompt` which uses `xdotool` to type in a password
|
- New function `passTypePrompt` which uses `xdotool` to type in a password
|
||||||
from the store, bypassing the clipboard.
|
from the store, bypassing the clipboard.
|
||||||
|
- New function `passEditPrompt` for editing a password from the
|
||||||
|
store.
|
||||||
- Now handles password labels with spaces and special characters inside
|
- Now handles password labels with spaces and special characters inside
|
||||||
them.
|
them.
|
||||||
|
|
||||||
|
@@ -50,7 +50,7 @@ import Data.Map.Strict (Map)
|
|||||||
import qualified Data.Map.Strict as Map
|
import qualified Data.Map.Strict as Map
|
||||||
import Data.Maybe (fromMaybe, isNothing)
|
import Data.Maybe (fromMaybe, isNothing)
|
||||||
import Data.Monoid ((<>))
|
import Data.Monoid ((<>))
|
||||||
import System.Directory (setCurrentDirectory, getHomeDirectory)
|
import System.Directory (setCurrentDirectory, getHomeDirectory, makeAbsolute)
|
||||||
import XMonad
|
import XMonad
|
||||||
import XMonad.Actions.DynamicWorkspaces
|
import XMonad.Actions.DynamicWorkspaces
|
||||||
import XMonad.Prompt
|
import XMonad.Prompt
|
||||||
@@ -182,7 +182,8 @@ instance XPrompt ProjectPrompt where
|
|||||||
modifyProject (\p -> p { projectName = name })
|
modifyProject (\p -> p { projectName = name })
|
||||||
|
|
||||||
modeAction (ProjectPrompt DirMode _) buf auto = do
|
modeAction (ProjectPrompt DirMode _) buf auto = do
|
||||||
let dir = if null auto then buf else auto
|
let dir' = if null auto then buf else auto
|
||||||
|
dir <- io $ makeAbsolute dir'
|
||||||
modifyProject (\p -> p { projectDirectory = dir })
|
modifyProject (\p -> p { projectDirectory = dir })
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
@@ -30,6 +30,7 @@ module XMonad.Actions.DynamicWorkspaceOrder
|
|||||||
, moveToGreedy
|
, moveToGreedy
|
||||||
, shiftTo
|
, shiftTo
|
||||||
|
|
||||||
|
, withNthWorkspace'
|
||||||
, withNthWorkspace
|
, withNthWorkspace
|
||||||
|
|
||||||
) where
|
) where
|
||||||
@@ -183,13 +184,19 @@ moveToGreedy dir t = doTo dir t getSortByOrder (windows . W.greedyView)
|
|||||||
shiftTo :: Direction1D -> WSType -> X ()
|
shiftTo :: Direction1D -> WSType -> X ()
|
||||||
shiftTo dir t = doTo dir t getSortByOrder (windows . W.shift)
|
shiftTo dir t = doTo dir t getSortByOrder (windows . W.shift)
|
||||||
|
|
||||||
|
-- | Do something with the nth workspace in the dynamic order after
|
||||||
|
-- transforming it. The callback is given the workspace's tag as well
|
||||||
|
-- as the 'WindowSet' of the workspace itself.
|
||||||
|
withNthWorkspace' :: ([WorkspaceId] -> [WorkspaceId]) -> (String -> WindowSet -> WindowSet) -> Int -> X ()
|
||||||
|
withNthWorkspace' tr job wnum = do
|
||||||
|
sort <- getSortByOrder
|
||||||
|
ws <- gets (tr . map W.tag . sort . W.workspaces . windowset)
|
||||||
|
case drop wnum ws of
|
||||||
|
(w:_) -> windows $ job w
|
||||||
|
[] -> return ()
|
||||||
|
|
||||||
-- | Do something with the nth workspace in the dynamic order. The
|
-- | Do something with the nth workspace in the dynamic order. The
|
||||||
-- callback is given the workspace's tag as well as the 'WindowSet'
|
-- callback is given the workspace's tag as well as the 'WindowSet'
|
||||||
-- of the workspace itself.
|
-- of the workspace itself.
|
||||||
withNthWorkspace :: (String -> WindowSet -> WindowSet) -> Int -> X ()
|
withNthWorkspace :: (String -> WindowSet -> WindowSet) -> Int -> X ()
|
||||||
withNthWorkspace job wnum = do
|
withNthWorkspace = withNthWorkspace' id
|
||||||
sort <- getSortByOrder
|
|
||||||
ws <- gets (map W.tag . sort . W.workspaces . windowset)
|
|
||||||
case drop wnum ws of
|
|
||||||
(w:_) -> windows $ job w
|
|
||||||
[] -> return ()
|
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
-- query.
|
-- query.
|
||||||
--
|
--
|
||||||
-- Also provides a method for jumping back to the most recently used
|
-- Also provides a method for jumping back to the most recently used
|
||||||
-- window in any given group.
|
-- window in any given group, and predefined groups.
|
||||||
--
|
--
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -27,9 +27,14 @@ module XMonad.Actions.GroupNavigation ( -- * Usage
|
|||||||
, nextMatchOrDo
|
, nextMatchOrDo
|
||||||
, nextMatchWithThis
|
, nextMatchWithThis
|
||||||
, historyHook
|
, historyHook
|
||||||
|
|
||||||
|
-- * Utilities
|
||||||
|
-- $utilities
|
||||||
|
, isOnAnyVisibleWS
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
|
import Control.Monad.State
|
||||||
import Data.Foldable as Fold
|
import Data.Foldable as Fold
|
||||||
import Data.Map as Map
|
import Data.Map as Map
|
||||||
import Data.Sequence as Seq
|
import Data.Sequence as Seq
|
||||||
@@ -142,7 +147,7 @@ orderedWorkspaceList ss wsids = rotateTo isCurWS wspcs'
|
|||||||
where
|
where
|
||||||
wspcs = SS.workspaces ss
|
wspcs = SS.workspaces ss
|
||||||
wspcsMap = Fold.foldl' (\m ws -> Map.insert (SS.tag ws) ws m) Map.empty wspcs
|
wspcsMap = Fold.foldl' (\m ws -> Map.insert (SS.tag ws) ws m) Map.empty wspcs
|
||||||
wspcs' = fmap (\wsid -> wspcsMap ! wsid) wsids
|
wspcs' = fmap (wspcsMap !) wsids
|
||||||
isCurWS ws = SS.tag ws == SS.tag (SS.workspace $ SS.current ss)
|
isCurWS ws = SS.tag ws == SS.tag (SS.workspace $ SS.current ss)
|
||||||
|
|
||||||
--- History navigation, requires a layout modifier -------------------
|
--- History navigation, requires a layout modifier -------------------
|
||||||
@@ -167,7 +172,7 @@ updateHistory :: HistoryDB -> X HistoryDB
|
|||||||
updateHistory (HistoryDB oldcur oldhist) = withWindowSet $ \ss -> do
|
updateHistory (HistoryDB oldcur oldhist) = withWindowSet $ \ss -> do
|
||||||
let newcur = SS.peek ss
|
let newcur = SS.peek ss
|
||||||
wins = Set.fromList $ SS.allWindows ss
|
wins = Set.fromList $ SS.allWindows ss
|
||||||
newhist = flt (flip Set.member wins) (ins oldcur oldhist)
|
newhist = flt (`Set.member` wins) (ins oldcur oldhist)
|
||||||
return $ HistoryDB newcur (del newcur newhist)
|
return $ HistoryDB newcur (del newcur newhist)
|
||||||
where
|
where
|
||||||
ins x xs = maybe xs (<| xs) x
|
ins x xs = maybe xs (<| xs) x
|
||||||
@@ -216,3 +221,22 @@ findM cond xs = findM' cond (viewl xs)
|
|||||||
if isMatch
|
if isMatch
|
||||||
then return (Just x')
|
then return (Just x')
|
||||||
else findM qry xs'
|
else findM qry xs'
|
||||||
|
|
||||||
|
|
||||||
|
-- $utilities
|
||||||
|
-- #utilities#
|
||||||
|
-- Below are handy queries for use with 'nextMatch', 'nextMatchOrDo',
|
||||||
|
-- and 'nextMatchWithThis'.
|
||||||
|
|
||||||
|
-- | A query that matches all windows on visible workspaces. This is
|
||||||
|
-- useful for configurations with multiple screens, and matches even
|
||||||
|
-- invisible windows.
|
||||||
|
isOnAnyVisibleWS :: Query Bool
|
||||||
|
isOnAnyVisibleWS = do
|
||||||
|
w <- ask
|
||||||
|
ws <- liftX $ gets windowset
|
||||||
|
let allVisible = concat $ maybe [] SS.integrate . SS.stack . SS.workspace <$> SS.current ws:SS.visible ws
|
||||||
|
visibleWs = w `elem` allVisible
|
||||||
|
unfocused = maybe True (w /=) $ SS.peek ws
|
||||||
|
return $ visibleWs && unfocused
|
||||||
|
|
||||||
|
@@ -467,7 +467,7 @@ Here is a list of the modules found in @XMonad.Hooks@:
|
|||||||
* "XMonad.Hooks.DebugStack":
|
* "XMonad.Hooks.DebugStack":
|
||||||
Dump the state of the StackSet. A logHook and handleEventHook are also provided.
|
Dump the state of the StackSet. A logHook and handleEventHook are also provided.
|
||||||
|
|
||||||
* "Xmonad.Hooks.DynamicBars":
|
* "XMonad.Hooks.DynamicBars":
|
||||||
Manage per-screen status bars.
|
Manage per-screen status bars.
|
||||||
|
|
||||||
* "XMonad.Hooks.DynamicHooks":
|
* "XMonad.Hooks.DynamicHooks":
|
||||||
|
@@ -29,12 +29,15 @@ import Control.Applicative((<$>))
|
|||||||
import Data.List
|
import Data.List
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.Monoid
|
import Data.Monoid
|
||||||
|
import qualified Data.Map.Strict as M
|
||||||
|
import System.IO.Unsafe
|
||||||
|
|
||||||
import XMonad
|
import XMonad
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import qualified XMonad.StackSet as W
|
import qualified XMonad.StackSet as W
|
||||||
|
|
||||||
import XMonad.Hooks.SetWMName
|
import XMonad.Hooks.SetWMName
|
||||||
|
import qualified XMonad.Util.ExtensibleState as E
|
||||||
import XMonad.Util.XUtils (fi)
|
import XMonad.Util.XUtils (fi)
|
||||||
import XMonad.Util.WorkspaceCompare
|
import XMonad.Util.WorkspaceCompare
|
||||||
import XMonad.Util.WindowProperties (getProp32)
|
import XMonad.Util.WindowProperties (getProp32)
|
||||||
@@ -70,6 +73,58 @@ ewmhDesktopsStartup = setSupported
|
|||||||
-- of the current state of workspaces and windows.
|
-- of the current state of workspaces and windows.
|
||||||
ewmhDesktopsLogHook :: X ()
|
ewmhDesktopsLogHook :: X ()
|
||||||
ewmhDesktopsLogHook = ewmhDesktopsLogHookCustom id
|
ewmhDesktopsLogHook = ewmhDesktopsLogHookCustom id
|
||||||
|
|
||||||
|
-- |
|
||||||
|
-- Cached desktop names (e.g. @_NET_NUMBER_OF_DESKTOPS@ and
|
||||||
|
-- @_NET_DESKTOP_NAMES@).
|
||||||
|
newtype DesktopNames = DesktopNames [String]
|
||||||
|
deriving (Eq)
|
||||||
|
|
||||||
|
instance ExtensionClass DesktopNames where
|
||||||
|
initialValue = DesktopNames []
|
||||||
|
|
||||||
|
-- |
|
||||||
|
-- Cached client list (e.g. @_NET_CLIENT_LIST@).
|
||||||
|
newtype ClientList = ClientList [Window]
|
||||||
|
deriving (Eq)
|
||||||
|
|
||||||
|
instance ExtensionClass ClientList where
|
||||||
|
initialValue = ClientList []
|
||||||
|
|
||||||
|
-- |
|
||||||
|
-- Cached current desktop (e.g. @_NET_CURRENT_DESKTOP@).
|
||||||
|
newtype CurrentDesktop = CurrentDesktop Int
|
||||||
|
deriving (Eq)
|
||||||
|
|
||||||
|
instance ExtensionClass CurrentDesktop where
|
||||||
|
initialValue = CurrentDesktop 0
|
||||||
|
|
||||||
|
-- |
|
||||||
|
-- Cached window-desktop assignments (e.g. @_NET_CLIENT_LIST_STACKING@).
|
||||||
|
newtype WindowDesktops = WindowDesktops (M.Map Window Int)
|
||||||
|
deriving (Eq)
|
||||||
|
|
||||||
|
instance ExtensionClass WindowDesktops where
|
||||||
|
initialValue = WindowDesktops M.empty
|
||||||
|
|
||||||
|
-- |
|
||||||
|
-- The value of @_NET_ACTIVE_WINDOW@, cached to avoid unnecessary property
|
||||||
|
-- updates.
|
||||||
|
newtype ActiveWindow = ActiveWindow Window
|
||||||
|
deriving (Eq)
|
||||||
|
|
||||||
|
instance ExtensionClass ActiveWindow where
|
||||||
|
initialValue = ActiveWindow none
|
||||||
|
|
||||||
|
-- | Compare the given value against the value in the extensible state. Run the
|
||||||
|
-- action if it has changed.
|
||||||
|
whenChanged :: (Eq a, ExtensionClass a) => a -> X () -> X ()
|
||||||
|
whenChanged v action = do
|
||||||
|
v0 <- E.get
|
||||||
|
unless (v == v0) $ do
|
||||||
|
action
|
||||||
|
E.put v
|
||||||
|
|
||||||
-- |
|
-- |
|
||||||
-- Generalized version of ewmhDesktopsLogHook that allows an arbitrary
|
-- Generalized version of ewmhDesktopsLogHook that allows an arbitrary
|
||||||
-- user-specified function to transform the workspace list (post-sorting)
|
-- user-specified function to transform the workspace list (post-sorting)
|
||||||
@@ -78,28 +133,32 @@ ewmhDesktopsLogHookCustom f = withWindowSet $ \s -> do
|
|||||||
sort' <- getSortByIndex
|
sort' <- getSortByIndex
|
||||||
let ws = f $ sort' $ W.workspaces s
|
let ws = f $ sort' $ W.workspaces s
|
||||||
|
|
||||||
-- Number of Workspaces
|
-- Set number of workspaces and names thereof
|
||||||
setNumberOfDesktops (length ws)
|
let desktopNames = map W.tag ws
|
||||||
|
whenChanged (DesktopNames desktopNames) $ do
|
||||||
|
setNumberOfDesktops (length desktopNames)
|
||||||
|
setDesktopNames desktopNames
|
||||||
|
|
||||||
-- Names thereof
|
-- Set client list; all windows, with focused windows last
|
||||||
setDesktopNames (map W.tag ws)
|
let clientList = nub . concatMap (maybe [] (\(W.Stack x l r) -> reverse l ++ r ++ [x]) . W.stack) $ ws
|
||||||
|
whenChanged (ClientList clientList) $ setClientList clientList
|
||||||
-- all windows, with focused windows last
|
|
||||||
let wins = nub . concatMap (maybe [] (\(W.Stack x l r)-> reverse l ++ r ++ [x]) . W.stack) $ ws
|
|
||||||
setClientList wins
|
|
||||||
|
|
||||||
-- Remap the current workspace to handle any renames that f might be doing.
|
-- Remap the current workspace to handle any renames that f might be doing.
|
||||||
let maybeCurrent' = W.tag <$> listToMaybe (f [W.workspace $ W.current s])
|
let maybeCurrent' = W.tag <$> listToMaybe (f [W.workspace $ W.current s])
|
||||||
maybeCurrent = join (flip elemIndex (map W.tag ws) <$> maybeCurrent')
|
current = join (flip elemIndex (map W.tag ws) <$> maybeCurrent')
|
||||||
|
whenChanged (CurrentDesktop $ fromMaybe 0 current) $ do
|
||||||
|
mapM_ setCurrentDesktop current
|
||||||
|
|
||||||
fromMaybe (return ()) $ setCurrentDesktop <$> maybeCurrent
|
-- Set window-desktop mapping
|
||||||
|
let windowDesktops =
|
||||||
sequence_ $ zipWith setWorkspaceWindowDesktops [0..] ws
|
let f wsId workspace = M.fromList [ (winId, wsId) | winId <- W.integrate' $ W.stack workspace ]
|
||||||
|
in M.unions $ zipWith f [0..] ws
|
||||||
setActiveWindow
|
whenChanged (WindowDesktops windowDesktops) $ do
|
||||||
|
mapM_ (uncurry setWindowDesktop) (M.toList windowDesktops)
|
||||||
return ()
|
|
||||||
|
|
||||||
|
-- Set active window
|
||||||
|
let activeWindow' = fromMaybe none (W.peek s)
|
||||||
|
whenChanged (ActiveWindow activeWindow') $ setActiveWindow activeWindow'
|
||||||
|
|
||||||
-- |
|
-- |
|
||||||
-- Intercepts messages from pagers and similar applications and reacts on them.
|
-- Intercepts messages from pagers and similar applications and reacts on them.
|
||||||
@@ -221,10 +280,6 @@ setClientList wins = withDisplay $ \dpy -> do
|
|||||||
a' <- getAtom "_NET_CLIENT_LIST_STACKING"
|
a' <- getAtom "_NET_CLIENT_LIST_STACKING"
|
||||||
io $ changeProperty32 dpy r a' c propModeReplace (fmap fromIntegral wins)
|
io $ changeProperty32 dpy r a' c propModeReplace (fmap fromIntegral wins)
|
||||||
|
|
||||||
setWorkspaceWindowDesktops :: (Integral a) => a -> WindowSpace -> X()
|
|
||||||
setWorkspaceWindowDesktops index workspace =
|
|
||||||
mapM_ (flip setWindowDesktop index) (W.integrate' $ W.stack workspace)
|
|
||||||
|
|
||||||
setWindowDesktop :: (Integral a) => Window -> a -> X ()
|
setWindowDesktop :: (Integral a) => Window -> a -> X ()
|
||||||
setWindowDesktop win i = withDisplay $ \dpy -> do
|
setWindowDesktop win i = withDisplay $ \dpy -> do
|
||||||
a <- getAtom "_NET_WM_DESKTOP"
|
a <- getAtom "_NET_WM_DESKTOP"
|
||||||
@@ -250,9 +305,8 @@ setSupported = withDisplay $ \dpy -> do
|
|||||||
|
|
||||||
setWMName "xmonad"
|
setWMName "xmonad"
|
||||||
|
|
||||||
setActiveWindow :: X ()
|
setActiveWindow :: Window -> X ()
|
||||||
setActiveWindow = withWindowSet $ \s -> withDisplay $ \dpy -> do
|
setActiveWindow w = withDisplay $ \dpy -> do
|
||||||
let w = fromMaybe none (W.peek s)
|
|
||||||
r <- asks theRoot
|
r <- asks theRoot
|
||||||
a <- getAtom "_NET_ACTIVE_WINDOW"
|
a <- getAtom "_NET_ACTIVE_WINDOW"
|
||||||
c <- getAtom "WINDOW"
|
c <- getAtom "WINDOW"
|
||||||
|
295
XMonad/Hooks/RefocusLast.hs
Normal file
295
XMonad/Hooks/RefocusLast.hs
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, MultiWayIf #-}
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : XMonad.Hooks.RefocusLast
|
||||||
|
-- Description : Hooks and actions to refocus the previous window.
|
||||||
|
-- Copyright : (c) 2018 L. S. Leary
|
||||||
|
-- License : BSD3-style (see LICENSE)
|
||||||
|
--
|
||||||
|
-- Maintainer : L. S. Leary
|
||||||
|
-- Stability : unstable
|
||||||
|
-- Portability : unportable
|
||||||
|
--
|
||||||
|
-- Provides hooks and actions that keep track of recently focused windows on a
|
||||||
|
-- per workspace basis and automatically refocus the last window on loss of the
|
||||||
|
-- current (if appropriate as determined by user specified criteria).
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- --< Imports & Exports >-- {{{
|
||||||
|
|
||||||
|
module XMonad.Hooks.RefocusLast (
|
||||||
|
-- * Usage
|
||||||
|
-- $Usage
|
||||||
|
-- * Hooks
|
||||||
|
refocusLastLogHook,
|
||||||
|
refocusLastLayoutHook,
|
||||||
|
refocusLastWhen,
|
||||||
|
-- ** Predicates
|
||||||
|
-- $Predicates
|
||||||
|
refocusingIsActive,
|
||||||
|
isFloat,
|
||||||
|
-- * Actions
|
||||||
|
toggleRefocusing,
|
||||||
|
toggleFocus,
|
||||||
|
swapWithLast,
|
||||||
|
refocusWhen,
|
||||||
|
shiftRLWhen,
|
||||||
|
updateRecentsOn,
|
||||||
|
-- * Types
|
||||||
|
-- $Types
|
||||||
|
RecentWins(..),
|
||||||
|
RecentsMap(..),
|
||||||
|
RefocusLastLayoutHook(..),
|
||||||
|
RefocusLastToggle(..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import XMonad
|
||||||
|
import qualified XMonad.StackSet as W
|
||||||
|
import qualified XMonad.Util.ExtensibleState as XS
|
||||||
|
import XMonad.Util.Stack (findS, mapZ_)
|
||||||
|
import XMonad.Layout.LayoutModifier
|
||||||
|
|
||||||
|
import Data.Maybe (fromMaybe)
|
||||||
|
import Data.Monoid (All(..))
|
||||||
|
import Data.Foldable (asum)
|
||||||
|
import qualified Data.Map.Strict as M
|
||||||
|
import Control.Monad (when)
|
||||||
|
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
-- --< Usage >-- {{{
|
||||||
|
|
||||||
|
-- $Usage
|
||||||
|
-- To use this module, you must either include 'refocusLastLogHook' in your log
|
||||||
|
-- hook __or__ 'refocusLastLayoutHook' in your layout hook; don't use both.
|
||||||
|
-- This suffices to make use of both 'toggleFocus' and 'shiftRLWhen' but will
|
||||||
|
-- not refocus automatically upon loss of the current window; for that you must
|
||||||
|
-- include in your event hook @'refocusLastWhen' pred@ for some valid @pred@.
|
||||||
|
--
|
||||||
|
-- The event hooks that trigger refocusing only fire when a window is lost
|
||||||
|
-- completely, not when it's simply e.g. moved to another workspace. Hence you
|
||||||
|
-- will need to use @'shiftRLWhen' pred@ or @'refocusWhen' pred@ as appropriate
|
||||||
|
-- if you want the same behaviour in such cases.
|
||||||
|
--
|
||||||
|
-- Example configuration:
|
||||||
|
--
|
||||||
|
-- > import XMonad
|
||||||
|
-- > import XMonad.Hooks.RefocusLast
|
||||||
|
-- > import qualified Data.Map.Strict as M
|
||||||
|
-- >
|
||||||
|
-- > main :: IO ()
|
||||||
|
-- > main = xmonad def
|
||||||
|
-- > { handleEventHook = refocusLastWhen myPred <+> handleEventHook def
|
||||||
|
-- > , logHook = refocusLastLogHook <+> logHook def
|
||||||
|
-- > -- , layoutHook = refocusLastLayoutHook $ layoutHook def
|
||||||
|
-- > , keys = refocusLastKeys <+> keys def
|
||||||
|
-- > } where
|
||||||
|
-- > myPred = refocusingIsActive <||> isFloat
|
||||||
|
-- > refocusLastKeys cnf
|
||||||
|
-- > = M.fromList
|
||||||
|
-- > $ ((modMask cnf , xK_a), toggleFocus)
|
||||||
|
-- > : ((modMask cnf .|. shiftMask, xK_a), swapWithLast)
|
||||||
|
-- > : ((modMask cnf , xK_b), toggleRefocusing)
|
||||||
|
-- > : [ ( (modMask cnf .|. shiftMask, n)
|
||||||
|
-- > , windows =<< shiftRLWhen myPred wksp
|
||||||
|
-- > )
|
||||||
|
-- > | (n, wksp) <- zip [xK_1..xK_9] (workspaces cnf)
|
||||||
|
-- > ]
|
||||||
|
--
|
||||||
|
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
-- --< Types >-- {{{
|
||||||
|
|
||||||
|
-- $Types
|
||||||
|
-- The types and constructors used in this module are exported principally to
|
||||||
|
-- aid extensibility; typical users will have nothing to gain from this section.
|
||||||
|
|
||||||
|
-- | Data type holding onto the previous and current @Window@.
|
||||||
|
data RecentWins = Recent { previous :: !Window, current :: !Window }
|
||||||
|
deriving (Show, Read, Eq)
|
||||||
|
|
||||||
|
-- | Newtype wrapper for a @Map@ holding the @RecentWins@ for each workspace.
|
||||||
|
-- Is an instance of @ExtensionClass@ with persistence of state.
|
||||||
|
newtype RecentsMap = RecentsMap (M.Map WorkspaceId RecentWins)
|
||||||
|
deriving (Show, Read, Eq, Typeable)
|
||||||
|
|
||||||
|
instance ExtensionClass RecentsMap where
|
||||||
|
initialValue = RecentsMap M.empty
|
||||||
|
extensionType = PersistentExtension
|
||||||
|
|
||||||
|
-- | A 'LayoutModifier' that updates the 'RecentWins' for a workspace upon
|
||||||
|
-- relayout.
|
||||||
|
data RefocusLastLayoutHook a = RefocusLastLayoutHook
|
||||||
|
deriving (Show, Read)
|
||||||
|
|
||||||
|
instance LayoutModifier RefocusLastLayoutHook a where
|
||||||
|
modifyLayout _ w@(W.Workspace tg _ _) r = updateRecentsOn tg >> runLayout w r
|
||||||
|
|
||||||
|
-- | A newtype on @Bool@ to act as a universal toggle for refocusing.
|
||||||
|
newtype RefocusLastToggle = RefocusLastToggle { refocusing :: Bool }
|
||||||
|
deriving (Show, Read, Eq, Typeable)
|
||||||
|
|
||||||
|
instance ExtensionClass RefocusLastToggle where
|
||||||
|
initialValue = RefocusLastToggle { refocusing = True }
|
||||||
|
extensionType = PersistentExtension
|
||||||
|
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
-- --< Public Hooks >-- {{{
|
||||||
|
|
||||||
|
-- | A log hook recording the current workspace's most recently focused windows
|
||||||
|
-- into extensible state.
|
||||||
|
refocusLastLogHook :: X ()
|
||||||
|
refocusLastLogHook = withWindowSet (updateRecentsOn . W.currentTag)
|
||||||
|
|
||||||
|
-- | Records a workspace's recently focused windows into extensible state upon
|
||||||
|
-- relayout. Potentially a less wasteful alternative to @refocusLastLogHook@,
|
||||||
|
-- as it does not run on @WM_NAME@ @propertyNotify@ events.
|
||||||
|
refocusLastLayoutHook :: l a -> ModifiedLayout RefocusLastLayoutHook l a
|
||||||
|
refocusLastLayoutHook = ModifiedLayout RefocusLastLayoutHook
|
||||||
|
|
||||||
|
-- | Given a predicate on the event window determining whether or not to act,
|
||||||
|
-- construct an event hook that runs iff the core xmonad event handler will
|
||||||
|
-- unmanage the window, and which shifts focus to the last focused window on
|
||||||
|
-- the appropriate workspace if desired.
|
||||||
|
refocusLastWhen :: Query Bool -> Event -> X All
|
||||||
|
refocusLastWhen p event = All True <$ case event of
|
||||||
|
UnmapEvent { ev_send_event = synth, ev_window = w } -> do
|
||||||
|
e <- gets (fromMaybe 0 . M.lookup w . waitingUnmap)
|
||||||
|
when (synth || e == 0) (refocusLast w)
|
||||||
|
DestroyWindowEvent { ev_window = w } -> refocusLast w
|
||||||
|
_ -> return ()
|
||||||
|
where
|
||||||
|
refocusLast w = whenX (runQuery p w) . withWindowSet $ \ws ->
|
||||||
|
whenJust (W.findTag w ws) $ \tag ->
|
||||||
|
withRecentsIn tag () $ \lw cw ->
|
||||||
|
when (w == cw) . modify $ \xs ->
|
||||||
|
xs { windowset = tryFocusIn tag [lw] ws }
|
||||||
|
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
-- --< Predicates >-- {{{
|
||||||
|
|
||||||
|
-- $Predicates
|
||||||
|
-- Impure @Query Bool@ predicates on event windows for use as arguments to
|
||||||
|
-- 'refocusLastWhen', 'shiftRLWhen' and 'refocusWhen'. Can be combined with
|
||||||
|
-- '<||>' or '<&&>'. Use like e.g.
|
||||||
|
--
|
||||||
|
-- > , handleEventHook = refocusLastWhen refocusingIsActive
|
||||||
|
--
|
||||||
|
-- or in a keybinding:
|
||||||
|
--
|
||||||
|
-- > windows =<< shiftRLWhen (refocusingIsActive <&&> isFloat) "3"
|
||||||
|
--
|
||||||
|
-- It's also valid to use a property lookup like @className =? "someProgram"@ as
|
||||||
|
-- a predicate, and it should function as expected with e.g. @shiftRLWhen@.
|
||||||
|
-- In the event hook on the other hand, the window in question has already been
|
||||||
|
-- unmapped or destroyed, so external lookups to X properties don't work:
|
||||||
|
-- only the information fossilised in xmonad's state is available.
|
||||||
|
|
||||||
|
-- | Holds iff refocusing is toggled active.
|
||||||
|
refocusingIsActive :: Query Bool
|
||||||
|
refocusingIsActive = (liftX . XS.gets) refocusing
|
||||||
|
|
||||||
|
-- | Holds iff the event window is a float.
|
||||||
|
isFloat :: Query Bool
|
||||||
|
isFloat = ask >>= \w -> (liftX . gets) (M.member w . W.floating . windowset)
|
||||||
|
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
-- --< Public Actions >-- {{{
|
||||||
|
|
||||||
|
-- | Toggle automatic refocusing at runtime. Has no effect unless the
|
||||||
|
-- @refocusingIsActive@ predicate has been used.
|
||||||
|
toggleRefocusing :: X ()
|
||||||
|
toggleRefocusing = XS.modify (RefocusLastToggle . not . refocusing)
|
||||||
|
|
||||||
|
-- | Refocuses the previously focused window; acts as a toggle.
|
||||||
|
-- Is not affected by @toggleRefocusing@.
|
||||||
|
toggleFocus :: X ()
|
||||||
|
toggleFocus = withRecents $ \lw cw ->
|
||||||
|
when (cw /= lw) . windows $ tryFocus [lw]
|
||||||
|
|
||||||
|
-- | Swaps the current and previous windows of the current workspace.
|
||||||
|
-- Is not affected by @toggleRefocusing@.
|
||||||
|
swapWithLast :: X ()
|
||||||
|
swapWithLast = withRecents $ \lw cw ->
|
||||||
|
when (cw /= lw) . windows . modify''. mapZ_ $ \w ->
|
||||||
|
if | (w == lw) -> cw
|
||||||
|
| (w == cw) -> lw
|
||||||
|
| otherwise -> w
|
||||||
|
where modify'' f = W.modify (f Nothing) (f . Just)
|
||||||
|
|
||||||
|
-- | Given a target workspace and a predicate on its current window, produce a
|
||||||
|
-- 'windows' suitable function that will refocus that workspace appropriately.
|
||||||
|
-- Allows you to hook refocusing into any action you can run through
|
||||||
|
-- @windows@. See the implementation of @shiftRLWhen@ for a straight-forward
|
||||||
|
-- usage example.
|
||||||
|
refocusWhen :: Query Bool -> WorkspaceId -> X (WindowSet -> WindowSet)
|
||||||
|
refocusWhen p tag = withRecentsIn tag id $ \lw cw -> do
|
||||||
|
b <- runQuery p cw
|
||||||
|
return (if b then tryFocusIn tag [cw, lw] else id)
|
||||||
|
|
||||||
|
-- | Sends the focused window to the specified workspace, refocusing the last
|
||||||
|
-- focused window if the predicate holds on the current window. Note that the
|
||||||
|
-- native version of this, @windows . W.shift@, has a nice property that this
|
||||||
|
-- does not: shifting a window to another workspace then shifting it back
|
||||||
|
-- preserves its place in the stack. Can be used in a keybinding like e.g.
|
||||||
|
--
|
||||||
|
-- > windows =<< shiftRLWhen refocusingIsActive "3"
|
||||||
|
--
|
||||||
|
-- or
|
||||||
|
--
|
||||||
|
-- > (windows <=< shiftRLWhen refocusingIsActive) "3"
|
||||||
|
--
|
||||||
|
-- where '<=<' is imported from "Control.Monad".
|
||||||
|
shiftRLWhen :: Query Bool -> WorkspaceId -> X (WindowSet -> WindowSet)
|
||||||
|
shiftRLWhen p to = withWindowSet $ \ws -> do
|
||||||
|
refocus <- refocusWhen p (W.currentTag ws)
|
||||||
|
let shift = maybe id (W.shiftWin to) (W.peek ws)
|
||||||
|
return (refocus . shift)
|
||||||
|
|
||||||
|
-- | Perform an update to the 'RecentWins' for the specified workspace.
|
||||||
|
-- The RefocusLast log and layout hooks are both implemented trivially in
|
||||||
|
-- terms of this function. Only exported to aid extensibility.
|
||||||
|
updateRecentsOn :: WorkspaceId -> X ()
|
||||||
|
updateRecentsOn tag = withWindowSet $ \ws ->
|
||||||
|
whenJust (W.peek $ W.view tag ws) $ \fw -> do
|
||||||
|
m <- getRecentsMap
|
||||||
|
let insertRecent l c = XS.put . RecentsMap $ M.insert tag (Recent l c) m
|
||||||
|
case M.lookup tag m of
|
||||||
|
Just (Recent _ cw) -> when (cw /= fw) (insertRecent cw fw)
|
||||||
|
Nothing -> insertRecent fw fw
|
||||||
|
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
-- --< Private Utilities >-- {{{
|
||||||
|
|
||||||
|
-- | Focuses the first window in the list it can find on the current workspace.
|
||||||
|
tryFocus :: [Window] -> WindowSet -> WindowSet
|
||||||
|
tryFocus wins = W.modify' $ \s ->
|
||||||
|
fromMaybe s . asum $ (\w -> findS (== w) s) <$> wins
|
||||||
|
|
||||||
|
-- | Operate the above on a specified workspace.
|
||||||
|
tryFocusIn :: WorkspaceId -> [Window] -> WindowSet -> WindowSet
|
||||||
|
tryFocusIn tag wins ws =
|
||||||
|
W.view (W.currentTag ws) . tryFocus wins . W.view tag $ ws
|
||||||
|
|
||||||
|
-- | Get the RecentsMap out of extensible state and remove its newtype wrapper.
|
||||||
|
getRecentsMap :: X (M.Map WorkspaceId RecentWins)
|
||||||
|
getRecentsMap = XS.get >>= \(RecentsMap m) -> return m
|
||||||
|
|
||||||
|
-- | Perform an X action dependent on successful lookup of the RecentWins for
|
||||||
|
-- the specified workspace, or return a default value.
|
||||||
|
withRecentsIn :: WorkspaceId -> a -> (Window -> Window -> X a) -> X a
|
||||||
|
withRecentsIn tag dflt f = M.lookup tag <$> getRecentsMap
|
||||||
|
>>= maybe (return dflt) (\(Recent lw cw) -> f lw cw)
|
||||||
|
|
||||||
|
-- | The above specialised to the current workspace and unit.
|
||||||
|
withRecents :: (Window -> Window -> X ()) -> X ()
|
||||||
|
withRecents f = withWindowSet $ \ws -> withRecentsIn (W.currentTag ws) () f
|
||||||
|
|
||||||
|
-- }}}
|
||||||
|
|
@@ -221,7 +221,7 @@ layerCommand (rect, path) = do
|
|||||||
res <- getPicRes path
|
res <- getPicRes path
|
||||||
return $ case needsRotation rect <$> res of
|
return $ case needsRotation rect <$> res of
|
||||||
Nothing -> ""
|
Nothing -> ""
|
||||||
Just rotate ->
|
Just rotate -> let size = show (rect_width rect) ++ "x" ++ show (rect_height rect) in
|
||||||
" \\( '"++path++"' "++(if rotate then "-rotate 90 " else "")
|
" \\( '"++path++"' "++(if rotate then "-rotate 90 " else "")
|
||||||
++ " -scale "++(show$rect_width rect)++"x"++(show$rect_height rect)++"! \\)"
|
++ " -scale "++size++"^ -gravity center -extent "++size++" +gravity \\)"
|
||||||
++ " -geometry +"++(show$rect_x rect)++"+"++(show$rect_y rect)++" -composite "
|
++ " -geometry +"++(show$rect_x rect)++"+"++(show$rect_y rect)++" -composite "
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
-- Module : XMonad.Layout.BinarySpacePartition
|
-- Module : XMonad.Layout.BinarySpacePartition
|
||||||
-- Copyright : (c) 2013 Ben Weitzman <benweitzman@gmail.com>
|
-- Copyright : (c) 2013 Ben Weitzman <benweitzman@gmail.com>
|
||||||
-- 2015 Anton Pirogov <anton.pirogov@gmail.com>
|
-- 2015 Anton Pirogov <anton.pirogov@gmail.com>
|
||||||
|
-- 2019 Mateusz Karbowy <obszczymucha@gmail.com
|
||||||
-- License : BSD3-style (see LICENSE)
|
-- License : BSD3-style (see LICENSE)
|
||||||
--
|
--
|
||||||
-- Maintainer : Ben Weitzman <benweitzman@gmail.com>
|
-- Maintainer : Ben Weitzman <benweitzman@gmail.com>
|
||||||
@@ -29,6 +30,7 @@ module XMonad.Layout.BinarySpacePartition (
|
|||||||
, FocusParent(..)
|
, FocusParent(..)
|
||||||
, SelectMoveNode(..)
|
, SelectMoveNode(..)
|
||||||
, Direction2D(..)
|
, Direction2D(..)
|
||||||
|
, SplitShiftDirectional(..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import XMonad
|
import XMonad
|
||||||
@@ -66,19 +68,21 @@ import Data.Ratio ((%))
|
|||||||
--
|
--
|
||||||
-- If you don't want to use the mouse, add the following key bindings to resize the splits with the keyboard:
|
-- If you don't want to use the mouse, add the following key bindings to resize the splits with the keyboard:
|
||||||
--
|
--
|
||||||
-- > , ((modm .|. altMask, xK_l ), sendMessage $ ExpandTowards R)
|
-- > , ((modm .|. altMask, xK_l ), sendMessage $ ExpandTowards R)
|
||||||
-- > , ((modm .|. altMask, xK_h ), sendMessage $ ExpandTowards L)
|
-- > , ((modm .|. altMask, xK_h ), sendMessage $ ExpandTowards L)
|
||||||
-- > , ((modm .|. altMask, xK_j ), sendMessage $ ExpandTowards D)
|
-- > , ((modm .|. altMask, xK_j ), sendMessage $ ExpandTowards D)
|
||||||
-- > , ((modm .|. altMask, xK_k ), sendMessage $ ExpandTowards U)
|
-- > , ((modm .|. altMask, xK_k ), sendMessage $ ExpandTowards U)
|
||||||
-- > , ((modm .|. altMask .|. ctrlMask , xK_l ), sendMessage $ ShrinkFrom R)
|
-- > , ((modm .|. altMask .|. ctrlMask , xK_l ), sendMessage $ ShrinkFrom R)
|
||||||
-- > , ((modm .|. altMask .|. ctrlMask , xK_h ), sendMessage $ ShrinkFrom L)
|
-- > , ((modm .|. altMask .|. ctrlMask , xK_h ), sendMessage $ ShrinkFrom L)
|
||||||
-- > , ((modm .|. altMask .|. ctrlMask , xK_j ), sendMessage $ ShrinkFrom D)
|
-- > , ((modm .|. altMask .|. ctrlMask , xK_j ), sendMessage $ ShrinkFrom D)
|
||||||
-- > , ((modm .|. altMask .|. ctrlMask , xK_k ), sendMessage $ ShrinkFrom U)
|
-- > , ((modm .|. altMask .|. ctrlMask , xK_k ), sendMessage $ ShrinkFrom U)
|
||||||
-- > , ((modm, xK_r ), sendMessage Rotate)
|
-- > , ((modm, xK_r ), sendMessage Rotate)
|
||||||
-- > , ((modm, xK_s ), sendMessage Swap)
|
-- > , ((modm, xK_s ), sendMessage Swap)
|
||||||
-- > , ((modm, xK_n ), sendMessage FocusParent)
|
-- > , ((modm, xK_n ), sendMessage FocusParent)
|
||||||
-- > , ((modm .|. ctrlMask, xK_n ), sendMessage SelectNode)
|
-- > , ((modm .|. ctrlMask, xK_n ), sendMessage SelectNode)
|
||||||
-- > , ((modm .|. shiftMask, xK_n ), sendMessage MoveNode)
|
-- > , ((modm .|. shiftMask, xK_n ), sendMessage MoveNode)
|
||||||
|
-- > , ((modm .|. shiftMask .|. ctrlMask , xK_j ), sendMessage $ SplitShift Prev)
|
||||||
|
-- > , ((modm .|. shiftMask .|. ctrlMask , xK_k ), sendMessage $ SplitShift Next)
|
||||||
--
|
--
|
||||||
-- Here's an alternative key mapping, this time using additionalKeysP,
|
-- Here's an alternative key mapping, this time using additionalKeysP,
|
||||||
-- arrow keys, and slightly different behavior when resizing windows
|
-- arrow keys, and slightly different behavior when resizing windows
|
||||||
@@ -92,7 +96,9 @@ import Data.Ratio ((%))
|
|||||||
-- > , ("M-M1-C-<Up>", sendMessage $ ShrinkFrom D)
|
-- > , ("M-M1-C-<Up>", sendMessage $ ShrinkFrom D)
|
||||||
-- > , ("M-M1-C-<Down>", sendMessage $ ExpandTowards D)
|
-- > , ("M-M1-C-<Down>", sendMessage $ ExpandTowards D)
|
||||||
-- > , ("M-s", sendMessage $ BSP.Swap)
|
-- > , ("M-s", sendMessage $ BSP.Swap)
|
||||||
-- > , ("M-M1-s", sendMessage $ Rotate) ]
|
-- > , ("M-M1-s", sendMessage $ Rotate)
|
||||||
|
-- > , ("M-S-C-j", sendMessage $ SplitShift Prev)
|
||||||
|
-- > , ("M-S-C-k", sendMessage $ SplitShift Next)
|
||||||
--
|
--
|
||||||
-- If you have many windows open and the layout begins to look too hard to manage, you can 'Balance'
|
-- If you have many windows open and the layout begins to look too hard to manage, you can 'Balance'
|
||||||
-- the layout, so that the current splittings are discarded and windows are tiled freshly in a way that
|
-- the layout, so that the current splittings are discarded and windows are tiled freshly in a way that
|
||||||
@@ -133,6 +139,10 @@ instance Message SelectMoveNode
|
|||||||
|
|
||||||
data Axis = Horizontal | Vertical deriving (Show, Read, Eq)
|
data Axis = Horizontal | Vertical deriving (Show, Read, Eq)
|
||||||
|
|
||||||
|
-- |Message for shifting window by splitting its neighbour
|
||||||
|
data SplitShiftDirectional = SplitShift Direction1D deriving Typeable
|
||||||
|
instance Message SplitShiftDirectional
|
||||||
|
|
||||||
oppositeDirection :: Direction2D -> Direction2D
|
oppositeDirection :: Direction2D -> Direction2D
|
||||||
oppositeDirection U = D
|
oppositeDirection U = D
|
||||||
oppositeDirection D = U
|
oppositeDirection D = U
|
||||||
@@ -273,6 +283,42 @@ swapCurrent :: Zipper a -> Maybe (Zipper a)
|
|||||||
swapCurrent l@(_, []) = Just l
|
swapCurrent l@(_, []) = Just l
|
||||||
swapCurrent (n, c:cs) = Just (n, swapCrumb c:cs)
|
swapCurrent (n, c:cs) = Just (n, swapCrumb c:cs)
|
||||||
|
|
||||||
|
insertLeftLeaf :: Tree Split -> Zipper Split -> Maybe (Zipper Split)
|
||||||
|
insertLeftLeaf (Leaf n) ((Node x l r), crumb:cs) = Just (Node (Split (oppositeAxis . axis . parentVal $ crumb) 0.5) (Leaf n) (Node x l r), crumb:cs)
|
||||||
|
insertLeftLeaf (Leaf n) (Leaf x, crumb:cs) = Just (Node (Split (oppositeAxis . axis . parentVal $ crumb) 0.5) (Leaf n) (Leaf x), crumb:cs)
|
||||||
|
insertLeftLeaf (Node _ _ _) z = Just z
|
||||||
|
|
||||||
|
insertRightLeaf :: Tree Split -> Zipper Split -> Maybe (Zipper Split)
|
||||||
|
insertRightLeaf (Leaf n) ((Node x l r), crumb:cs) = Just (Node (Split (oppositeAxis . axis . parentVal $ crumb) 0.5) (Node x l r) (Leaf n), crumb:cs)
|
||||||
|
insertRightLeaf (Leaf n) (Leaf x, crumb:cs) = Just (Node (Split (oppositeAxis . axis . parentVal $ crumb) 0.5) (Leaf x) (Leaf n), crumb:cs)
|
||||||
|
insertRightLeaf (Node _ _ _) z = Just z
|
||||||
|
|
||||||
|
findRightLeaf :: Zipper Split -> Maybe (Zipper Split)
|
||||||
|
findRightLeaf n@(Node _ _ _, _) = goRight n >>= findRightLeaf
|
||||||
|
findRightLeaf l@(Leaf _, _) = Just l
|
||||||
|
|
||||||
|
findLeftLeaf :: Zipper Split -> Maybe (Zipper Split)
|
||||||
|
findLeftLeaf n@(Node _ _ _, _) = goLeft n
|
||||||
|
findLeftLeaf l@(Leaf _, _) = Just l
|
||||||
|
|
||||||
|
findTheClosestLeftmostLeaf :: Zipper Split -> Maybe (Zipper Split)
|
||||||
|
findTheClosestLeftmostLeaf s@(_, (RightCrumb _ _):_) = goUp s >>= goLeft >>= findRightLeaf
|
||||||
|
findTheClosestLeftmostLeaf s@(_, (LeftCrumb _ _):_) = goUp s >>= findTheClosestLeftmostLeaf
|
||||||
|
|
||||||
|
findTheClosestRightmostLeaf :: Zipper Split -> Maybe (Zipper Split)
|
||||||
|
findTheClosestRightmostLeaf s@(_, (RightCrumb _ _):_) = goUp s >>= findTheClosestRightmostLeaf
|
||||||
|
findTheClosestRightmostLeaf s@(_, (LeftCrumb _ _):_) = goUp s >>= goRight >>= findLeftLeaf
|
||||||
|
|
||||||
|
splitShiftLeftCurrent :: Zipper Split -> Maybe (Zipper Split)
|
||||||
|
splitShiftLeftCurrent l@(_, []) = Just l
|
||||||
|
splitShiftLeftCurrent l@(_, (RightCrumb _ _):_) = Just l -- Do nothing. We can swap windows instead.
|
||||||
|
splitShiftLeftCurrent l@(n, c:cs) = removeCurrent l >>= findTheClosestLeftmostLeaf >>= insertRightLeaf n
|
||||||
|
|
||||||
|
splitShiftRightCurrent :: Zipper Split -> Maybe (Zipper Split)
|
||||||
|
splitShiftRightCurrent l@(_, []) = Just l
|
||||||
|
splitShiftRightCurrent l@(_, (LeftCrumb _ _):_) = Just l -- Do nothing. We can swap windows instead.
|
||||||
|
splitShiftRightCurrent l@(n, c:cs) = removeCurrent l >>= findTheClosestRightmostLeaf >>= insertLeftLeaf n
|
||||||
|
|
||||||
isAllTheWay :: Direction2D -> Zipper Split -> Bool
|
isAllTheWay :: Direction2D -> Zipper Split -> Bool
|
||||||
isAllTheWay _ (_, []) = True
|
isAllTheWay _ (_, []) = True
|
||||||
isAllTheWay R (_, LeftCrumb s _:_)
|
isAllTheWay R (_, LeftCrumb s _:_)
|
||||||
@@ -513,6 +559,12 @@ swapNth (BinarySpacePartition _ _ _ Nothing) = emptyBSP
|
|||||||
swapNth b@(BinarySpacePartition _ _ _ (Just (Leaf _))) = b
|
swapNth b@(BinarySpacePartition _ _ _ (Just (Leaf _))) = b
|
||||||
swapNth b = doToNth swapCurrent b
|
swapNth b = doToNth swapCurrent b
|
||||||
|
|
||||||
|
splitShiftNth :: Direction1D -> BinarySpacePartition a -> BinarySpacePartition a
|
||||||
|
splitShiftNth _ (BinarySpacePartition _ _ _ Nothing) = emptyBSP
|
||||||
|
splitShiftNth _ b@(BinarySpacePartition _ _ _ (Just (Leaf _))) = b
|
||||||
|
splitShiftNth Prev b = doToNth splitShiftLeftCurrent b
|
||||||
|
splitShiftNth Next b = doToNth splitShiftRightCurrent b
|
||||||
|
|
||||||
growNthTowards :: Direction2D -> BinarySpacePartition a -> BinarySpacePartition a
|
growNthTowards :: Direction2D -> BinarySpacePartition a -> BinarySpacePartition a
|
||||||
growNthTowards _ (BinarySpacePartition _ _ _ Nothing) = emptyBSP
|
growNthTowards _ (BinarySpacePartition _ _ _ Nothing) = emptyBSP
|
||||||
growNthTowards _ b@(BinarySpacePartition _ _ _ (Just (Leaf _))) = b
|
growNthTowards _ b@(BinarySpacePartition _ _ _ (Just (Leaf _))) = b
|
||||||
@@ -687,6 +739,7 @@ instance LayoutClass BinarySpacePartition Window where
|
|||||||
, fmap rotateTr (fromMessage m)
|
, fmap rotateTr (fromMessage m)
|
||||||
, fmap (balanceTr r) (fromMessage m)
|
, fmap (balanceTr r) (fromMessage m)
|
||||||
, fmap move (fromMessage m)
|
, fmap move (fromMessage m)
|
||||||
|
, fmap splitShift (fromMessage m)
|
||||||
]
|
]
|
||||||
resize (ExpandTowards dir) = growNthTowards dir b
|
resize (ExpandTowards dir) = growNthTowards dir b
|
||||||
resize (ShrinkFrom dir) = shrinkNthFrom dir b
|
resize (ShrinkFrom dir) = shrinkNthFrom dir b
|
||||||
@@ -699,6 +752,7 @@ instance LayoutClass BinarySpacePartition Window where
|
|||||||
balanceTr r Balance = resetFoc $ rebalanceNth b r
|
balanceTr r Balance = resetFoc $ rebalanceNth b r
|
||||||
move MoveNode = resetFoc $ moveNode b
|
move MoveNode = resetFoc $ moveNode b
|
||||||
move SelectNode = b --should not happen here, is done above, as we need X monad
|
move SelectNode = b --should not happen here, is done above, as we need X monad
|
||||||
|
splitShift (SplitShift dir) = resetFoc $ splitShiftNth dir b
|
||||||
|
|
||||||
b = numerateLeaves b_orig
|
b = numerateLeaves b_orig
|
||||||
resetFoc bsp = bsp{getFocusedNode=(getFocusedNode bsp){refLeaf=(-1)}
|
resetFoc bsp = bsp{getFocusedNode=(getFocusedNode bsp){refLeaf=(-1)}
|
||||||
|
@@ -68,18 +68,21 @@ decoration s t ds = ModifiedLayout (Decoration (I Nothing) s t ds)
|
|||||||
--
|
--
|
||||||
-- For a collection of 'Theme's see "XMonad.Util.Themes"
|
-- For a collection of 'Theme's see "XMonad.Util.Themes"
|
||||||
data Theme =
|
data Theme =
|
||||||
Theme { activeColor :: String -- ^ Color of the active window
|
Theme { activeColor :: String -- ^ Color of the active window
|
||||||
, inactiveColor :: String -- ^ Color of the inactive window
|
, inactiveColor :: String -- ^ Color of the inactive window
|
||||||
, urgentColor :: String -- ^ Color of the urgent window
|
, urgentColor :: String -- ^ Color of the urgent window
|
||||||
, activeBorderColor :: String -- ^ Color of the border of the active window
|
, activeBorderColor :: String -- ^ Color of the border of the active window
|
||||||
, inactiveBorderColor :: String -- ^ Color of the border of the inactive window
|
, inactiveBorderColor :: String -- ^ Color of the border of the inactive window
|
||||||
, urgentBorderColor :: String -- ^ Color of the border of the urgent window
|
, urgentBorderColor :: String -- ^ Color of the border of the urgent window
|
||||||
, activeTextColor :: String -- ^ Color of the text of the active window
|
, activeBorderWidth :: Dimension -- ^ Width of the border of the active window
|
||||||
, inactiveTextColor :: String -- ^ Color of the text of the inactive window
|
, inactiveBorderWidth :: Dimension -- ^ Width of the border of the inactive window
|
||||||
, urgentTextColor :: String -- ^ Color of the text of the urgent window
|
, urgentBorderWidth :: Dimension -- ^ Width of the border of the urgent window
|
||||||
, fontName :: String -- ^ Font name
|
, activeTextColor :: String -- ^ Color of the text of the active window
|
||||||
, decoWidth :: Dimension -- ^ Maximum width of the decorations (if supported by the 'DecorationStyle')
|
, inactiveTextColor :: String -- ^ Color of the text of the inactive window
|
||||||
, decoHeight :: Dimension -- ^ Height of the decorations
|
, urgentTextColor :: String -- ^ Color of the text of the urgent window
|
||||||
|
, fontName :: String -- ^ Font name
|
||||||
|
, decoWidth :: Dimension -- ^ Maximum width of the decorations (if supported by the 'DecorationStyle')
|
||||||
|
, decoHeight :: Dimension -- ^ Height of the decorations
|
||||||
, windowTitleAddons :: [(String, Align)] -- ^ Extra text to appear in a window's title bar.
|
, windowTitleAddons :: [(String, Align)] -- ^ Extra text to appear in a window's title bar.
|
||||||
-- Refer to for a use "XMonad.Layout.ImageButtonDecoration"
|
-- Refer to for a use "XMonad.Layout.ImageButtonDecoration"
|
||||||
, windowTitleIcons :: [([[Bool]], Placement)] -- ^ Extra icons to appear in a window's title bar.
|
, windowTitleIcons :: [([[Bool]], Placement)] -- ^ Extra icons to appear in a window's title bar.
|
||||||
@@ -94,6 +97,9 @@ instance Default Theme where
|
|||||||
, activeBorderColor = "#FFFFFF"
|
, activeBorderColor = "#FFFFFF"
|
||||||
, inactiveBorderColor = "#BBBBBB"
|
, inactiveBorderColor = "#BBBBBB"
|
||||||
, urgentBorderColor = "##00FF00"
|
, urgentBorderColor = "##00FF00"
|
||||||
|
, activeBorderWidth = 1
|
||||||
|
, inactiveBorderWidth = 1
|
||||||
|
, urgentBorderWidth = 1
|
||||||
, activeTextColor = "#FFFFFF"
|
, activeTextColor = "#FFFFFF"
|
||||||
, inactiveTextColor = "#BFBFBF"
|
, inactiveTextColor = "#BFBFBF"
|
||||||
, urgentTextColor = "#FF0000"
|
, urgentTextColor = "#FF0000"
|
||||||
@@ -395,9 +401,10 @@ updateDeco sh t fs ((w,_),(Just dw,Just (Rectangle _ _ wh ht))) = do
|
|||||||
| win `elem` ur -> uc
|
| win `elem` ur -> uc
|
||||||
| otherwise -> ic) . W.peek)
|
| otherwise -> ic) . W.peek)
|
||||||
`fmap` gets windowset
|
`fmap` gets windowset
|
||||||
(bc,borderc,tc) <- focusColor w (inactiveColor t, inactiveBorderColor t, inactiveTextColor t)
|
(bc,borderc,borderw,tc) <-
|
||||||
(activeColor t, activeBorderColor t, activeTextColor t)
|
focusColor w (inactiveColor t, inactiveBorderColor t, inactiveBorderWidth t, inactiveTextColor t)
|
||||||
(urgentColor t, urgentBorderColor t, urgentTextColor t)
|
(activeColor t, activeBorderColor t, activeBorderWidth t, activeTextColor t)
|
||||||
|
(urgentColor t, urgentBorderColor t, urgentBorderWidth t, urgentTextColor t)
|
||||||
let s = shrinkIt sh
|
let s = shrinkIt sh
|
||||||
name <- shrinkWhile s (\n -> do size <- io $ textWidthXMF dpy fs n
|
name <- shrinkWhile s (\n -> do size <- io $ textWidthXMF dpy fs n
|
||||||
return $ size > fromIntegral wh - fromIntegral (ht `div` 2)) (show nw)
|
return $ size > fromIntegral wh - fromIntegral (ht `div` 2)) (show nw)
|
||||||
@@ -405,7 +412,7 @@ updateDeco sh t fs ((w,_),(Just dw,Just (Rectangle _ _ wh ht))) = do
|
|||||||
strs = name : map fst (windowTitleAddons t)
|
strs = name : map fst (windowTitleAddons t)
|
||||||
i_als = map snd (windowTitleIcons t)
|
i_als = map snd (windowTitleIcons t)
|
||||||
icons = map fst (windowTitleIcons t)
|
icons = map fst (windowTitleIcons t)
|
||||||
paintTextAndIcons dw fs wh ht 1 bc borderc tc bc als strs i_als icons
|
paintTextAndIcons dw fs wh ht borderw bc borderc tc bc als strs i_als icons
|
||||||
updateDeco _ _ _ (_,(Just w,Nothing)) = hideWindow w
|
updateDeco _ _ _ (_,(Just w,Nothing)) = hideWindow w
|
||||||
updateDeco _ _ _ _ = return ()
|
updateDeco _ _ _ _ = return ()
|
||||||
|
|
||||||
|
@@ -214,7 +214,7 @@ infixr 5 |||
|
|||||||
-- layouts, and use those.
|
-- layouts, and use those.
|
||||||
--
|
--
|
||||||
-- For the ability to select a layout from a prompt, see
|
-- For the ability to select a layout from a prompt, see
|
||||||
-- "Xmonad.Prompt.Layout".
|
-- "XMonad.Prompt.Layout".
|
||||||
|
|
||||||
-- | A reimplementation of the combinator of the same name from the
|
-- | A reimplementation of the combinator of the same name from the
|
||||||
-- xmonad core, providing layout choice, and the ability to support
|
-- xmonad core, providing layout choice, and the ability to support
|
||||||
|
@@ -226,8 +226,12 @@ instance Eq a => DecorationStyle TabbedDecoration a where
|
|||||||
ny = n y hh
|
ny = n y hh
|
||||||
upperTab = Rectangle nx y wid (fi ht)
|
upperTab = Rectangle nx y wid (fi ht)
|
||||||
lowerTab = Rectangle nx (y + fi (hh - ht)) wid (fi ht)
|
lowerTab = Rectangle nx (y + fi (hh - ht)) wid (fi ht)
|
||||||
leftTab = Rectangle x ny (fi wt) hid
|
fixHeightLoc i = y + fi (((fi ht) * fi i))
|
||||||
rightTab = Rectangle (x + fi (wh - wt)) ny (fi wt) hid
|
fixHeightTab k = Rectangle k
|
||||||
|
(maybe y (fixHeightLoc)
|
||||||
|
$ w `elemIndex` ws) (fi wt) (fi ht)
|
||||||
|
rightTab = fixHeightTab (x + fi (wh - wt))
|
||||||
|
leftTab = fixHeightTab x
|
||||||
numWindows = length ws
|
numWindows = length ws
|
||||||
shrink (Tabbed loc _ ) (Rectangle _ _ dw dh) (Rectangle x y w h)
|
shrink (Tabbed loc _ ) (Rectangle _ _ dw dh) (Rectangle x y w h)
|
||||||
= case loc of
|
= case loc of
|
||||||
|
98
XMonad/Layout/TwoPanePersistent.hs
Normal file
98
XMonad/Layout/TwoPanePersistent.hs
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : XMonad.Layout.TwoPanePersistent
|
||||||
|
-- Copyright : (c) Chayanon Wichitrnithed
|
||||||
|
-- License : BSD3-style (see LICENSE)
|
||||||
|
--
|
||||||
|
-- Maintainer : Chayanon Wichitrnithed <namowi@gatech.edu>
|
||||||
|
-- Stability : unstable
|
||||||
|
-- Portability : unportable
|
||||||
|
--
|
||||||
|
-- This layout is the same as "XMonad.Layout.TwoPane" except that it keeps track of the slave window
|
||||||
|
-- that is alongside the master pane. In other words, it prevents the slave pane
|
||||||
|
-- from changing after the focus goes back to the master pane.
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
module XMonad.Layout.TwoPanePersistent
|
||||||
|
(
|
||||||
|
-- * Usage
|
||||||
|
-- $usage
|
||||||
|
TwoPanePersistent(..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import XMonad.StackSet (focus, up, down, Stack, Stack(..))
|
||||||
|
import XMonad hiding (focus)
|
||||||
|
|
||||||
|
-- $usage
|
||||||
|
-- Import the module in @~\/.xmonad\/xmonad.hs@:
|
||||||
|
--
|
||||||
|
-- > import XMonad.Layout.TwoPanePersistent
|
||||||
|
--
|
||||||
|
-- Then add the layout to the @layoutHook@:
|
||||||
|
--
|
||||||
|
-- > myLayout = TwoPanePersistent Nothing (3/100) (1/2) ||| Full ||| etc..
|
||||||
|
-- > main = xmonad def { layoutHook = myLayout }
|
||||||
|
|
||||||
|
|
||||||
|
data TwoPanePersistent a = TwoPanePersistent
|
||||||
|
{ slaveWin :: (Maybe a) -- ^ slave window; if 'Nothing' or not in the current workspace,
|
||||||
|
-- the window below the master will go into the slave pane
|
||||||
|
, dFrac :: Rational -- ^ shrink/expand size
|
||||||
|
, mFrac :: Rational -- ^ initial master size
|
||||||
|
} deriving (Show, Read)
|
||||||
|
|
||||||
|
|
||||||
|
instance (Show a, Eq a) => LayoutClass TwoPanePersistent a where
|
||||||
|
doLayout l r s =
|
||||||
|
case reverse (up s) of
|
||||||
|
-- master is focused
|
||||||
|
[] -> return $ focusedMaster l s r
|
||||||
|
|
||||||
|
-- slave is focused
|
||||||
|
(master:_) -> return $ focusedSlave l s r master
|
||||||
|
|
||||||
|
|
||||||
|
pureMessage (TwoPanePersistent w delta split) x =
|
||||||
|
case fromMessage x of
|
||||||
|
Just Shrink -> Just (TwoPanePersistent w delta (split - delta))
|
||||||
|
Just Expand -> Just (TwoPanePersistent w delta (split + delta))
|
||||||
|
_ -> Nothing
|
||||||
|
|
||||||
|
description _ = "TwoPanePersistent"
|
||||||
|
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
focusedMaster :: (Eq a) => TwoPanePersistent a -> Stack a -> Rectangle
|
||||||
|
-> ( [(a, Rectangle)], Maybe (TwoPanePersistent a) )
|
||||||
|
focusedMaster (TwoPanePersistent w delta split) s r =
|
||||||
|
let (left, right) = splitHorizontallyBy split r in
|
||||||
|
case down s of
|
||||||
|
-- there exist windows below the master
|
||||||
|
(next:_) -> let nextSlave = ( [(focus s, left), (next, right)]
|
||||||
|
, Just $ TwoPanePersistent (Just next) delta split )
|
||||||
|
in case w of
|
||||||
|
-- if retains state, preserve the layout
|
||||||
|
Just win -> if win `elem` (down s) && (focus s /= win)
|
||||||
|
then ( [(focus s, left), (win, right)]
|
||||||
|
, Just $ TwoPanePersistent w delta split )
|
||||||
|
else nextSlave
|
||||||
|
-- if no previous state, default to the next slave window
|
||||||
|
Nothing -> nextSlave
|
||||||
|
|
||||||
|
|
||||||
|
-- the master is the only window
|
||||||
|
[] -> ( [(focus s, r)]
|
||||||
|
, Just $ TwoPanePersistent Nothing delta split )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
focusedSlave :: TwoPanePersistent a -> Stack a -> Rectangle -> a
|
||||||
|
-> ( [(a, Rectangle)], Maybe (TwoPanePersistent a) )
|
||||||
|
focusedSlave (TwoPanePersistent _ delta split) s r m =
|
||||||
|
( [(m, left), (focus s, right)]
|
||||||
|
, Just $ TwoPanePersistent (Just $ focus s) delta split )
|
||||||
|
where (left, right) = splitHorizontallyBy split r
|
1100
XMonad/Prompt.hs
1100
XMonad/Prompt.hs
File diff suppressed because it is too large
Load Diff
@@ -48,22 +48,22 @@ import Data.List
|
|||||||
-- 11. "FastSPR" is ranked before "FasterSPR" because its match starts at
|
-- 11. "FastSPR" is ranked before "FasterSPR" because its match starts at
|
||||||
-- position 5 while the match in "FasterSPR" starts at position 7.
|
-- position 5 while the match in "FasterSPR" starts at position 7.
|
||||||
--
|
--
|
||||||
-- To use these functions in an XPrompt, for example, for windowPromptGoto:
|
-- To use these functions in an XPrompt, for example, for windowPrompt:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Prompt
|
-- > import XMonad.Prompt
|
||||||
-- > import XMonad.Prompt.Window ( windowPromptGoto )
|
-- > import XMonad.Prompt.Window ( windowPrompt )
|
||||||
-- > import XMonad.Prompt.FuzzyMatch
|
-- > import XMonad.Prompt.FuzzyMatch
|
||||||
-- >
|
-- >
|
||||||
-- > myXPConfig = def { searchPredicate = fuzzyMatch
|
-- > myXPConfig = def { searchPredicate = fuzzyMatch
|
||||||
-- , sorter = fuzzySort
|
-- > , sorter = fuzzySort
|
||||||
-- }
|
-- > }
|
||||||
--
|
--
|
||||||
-- then add this to your keys definition:
|
-- then add this to your keys definition:
|
||||||
--
|
--
|
||||||
-- > , ((modm .|. shiftMask, xK_g), windowPromptGoto myXPConfig)
|
-- > , ((modm .|. shiftMask, xK_g), windowPrompt myXPConfig Goto allWindows)
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing the key bindings, see
|
-- For detailed instructions on editing the key bindings, see
|
||||||
-- "Xmonad.Doc.Extending#Editing_key_bindings".
|
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||||
|
|
||||||
-- | Returns True if the first argument is a subsequence of the second argument,
|
-- | Returns True if the first argument is a subsequence of the second argument,
|
||||||
-- that is, it can be obtained from the second sequence by deleting elements.
|
-- that is, it can be obtained from the second sequence by deleting elements.
|
||||||
@@ -77,7 +77,7 @@ fuzzyMatch xxs@(x:xs) (y:ys) | toLower x == toLower y = fuzzyMatch xs ys
|
|||||||
-- measured first by the length of the substring containing the match and second
|
-- measured first by the length of the substring containing the match and second
|
||||||
-- by the positions of the matching characters in the string.
|
-- by the positions of the matching characters in the string.
|
||||||
fuzzySort :: String -> [String] -> [String]
|
fuzzySort :: String -> [String] -> [String]
|
||||||
fuzzySort q = map snd . sortBy (compare `on` fst) . map (rankMatch q)
|
fuzzySort q = map snd . sort . map (rankMatch q)
|
||||||
|
|
||||||
rankMatch :: String -> String -> ((Int, Int), String)
|
rankMatch :: String -> String -> ((Int, Int), String)
|
||||||
rankMatch q s = (minimum $ rankMatches q s, s)
|
rankMatch q s = (minimum $ rankMatches q s, s)
|
||||||
@@ -95,7 +95,7 @@ findOccurrences :: String -> Char -> [Int]
|
|||||||
findOccurrences s c = map snd $ filter ((toLower c ==) . toLower . fst) $ zip s [0..]
|
findOccurrences s c = map snd $ filter ((toLower c ==) . toLower . fst) $ zip s [0..]
|
||||||
|
|
||||||
extendMatches :: [(Int, Int)] -> [Int] -> [(Int, Int)]
|
extendMatches :: [(Int, Int)] -> [Int] -> [(Int, Int)]
|
||||||
extendMatches spans xs = map last $ groupBy ((==) `on` snd) $ extendMatches' spans xs
|
extendMatches spans = map last . groupBy ((==) `on` snd) . extendMatches' spans
|
||||||
|
|
||||||
extendMatches' :: [(Int, Int)] -> [Int] -> [(Int, Int)]
|
extendMatches' :: [(Int, Int)] -> [Int] -> [(Int, Int)]
|
||||||
extendMatches' [] _ = []
|
extendMatches' [] _ = []
|
||||||
|
@@ -8,23 +8,33 @@
|
|||||||
-- Stability : unstable
|
-- Stability : unstable
|
||||||
-- Portability : unportable
|
-- Portability : unportable
|
||||||
--
|
--
|
||||||
-- This module provides 4 <XMonad.Prompt> to ease password manipulation (generate, read, remove):
|
-- This module provides 5 <XMonad.Prompt>s to ease password
|
||||||
|
-- manipulation (generate, read, edit, remove):
|
||||||
--
|
--
|
||||||
-- - two to lookup passwords in the password-store; one of which copies to the
|
-- - two to lookup passwords in the password-store; one of which
|
||||||
-- clipboard, and the other uses @xdotool@ to type the password directly.
|
-- copies to the clipboard, and the other uses @xdotool@ to type the
|
||||||
|
-- password directly.
|
||||||
--
|
--
|
||||||
-- - one to generate a password for a given password label that the user inputs.
|
-- - one to generate a password for a given password label that the
|
||||||
|
-- user inputs.
|
||||||
--
|
--
|
||||||
-- - one to delete a stored password for a given password label that the user inputs.
|
-- - one to edit a password for a given password label that the user
|
||||||
|
-- inputs.
|
||||||
--
|
--
|
||||||
-- All those prompts benefit from the completion system provided by the module <XMonad.Prompt>.
|
-- - one to delete a stored password for a given password label that
|
||||||
|
-- the user inputs.
|
||||||
--
|
--
|
||||||
-- The password store is setup through an environment variable PASSWORD_STORE_DIR,
|
-- All those prompts benefit from the completion system provided by
|
||||||
-- or @$HOME\/.password-store@ if it is unset.
|
-- the module <XMonad.Prompt>.
|
||||||
|
--
|
||||||
|
-- The password store is setup through an environment variable
|
||||||
|
-- PASSWORD_STORE_DIR, or @$HOME\/.password-store@ if it is unset.
|
||||||
|
-- The editor is determined from the environment variable EDITOR.
|
||||||
--
|
--
|
||||||
-- Source:
|
-- Source:
|
||||||
--
|
--
|
||||||
-- - The password store implementation is <http://git.zx2c4.com/password-store the password-store cli>.
|
-- - The <https://www.passwordstore.org/ password store>
|
||||||
|
-- implementation is <http://git.zx2c4.com/password-store here>.
|
||||||
--
|
--
|
||||||
-- - Inspired by <http://babushk.in/posts/combining-xmonad-and-pass.html>
|
-- - Inspired by <http://babushk.in/posts/combining-xmonad-and-pass.html>
|
||||||
--
|
--
|
||||||
@@ -34,8 +44,11 @@ module XMonad.Prompt.Pass (
|
|||||||
-- * Usage
|
-- * Usage
|
||||||
-- $usage
|
-- $usage
|
||||||
passPrompt
|
passPrompt
|
||||||
|
, passOTPPrompt
|
||||||
, passGeneratePrompt
|
, passGeneratePrompt
|
||||||
|
, passGenerateAndCopyPrompt
|
||||||
, passRemovePrompt
|
, passRemovePrompt
|
||||||
|
, passEditPrompt
|
||||||
, passTypePrompt
|
, passTypePrompt
|
||||||
) where
|
) where
|
||||||
|
|
||||||
@@ -58,10 +71,12 @@ import XMonad.Util.Run (runProcessWithInput)
|
|||||||
--
|
--
|
||||||
-- > import XMonad.Prompt.Pass
|
-- > import XMonad.Prompt.Pass
|
||||||
--
|
--
|
||||||
-- Then add a keybinding for 'passPrompt', 'passGeneratePrompt' or 'passRemovePrompt':
|
-- Then add a keybinding for 'passPrompt', 'passGeneratePrompt',
|
||||||
|
-- 'passRemovePrompt', 'passEditPrompt' or 'passTypePrompt':
|
||||||
--
|
--
|
||||||
-- > , ((modMask , xK_p) , passPrompt xpconfig)
|
-- > , ((modMask , xK_p) , passPrompt xpconfig)
|
||||||
-- > , ((modMask .|. controlMask, xK_p) , passGeneratePrompt xpconfig)
|
-- > , ((modMask .|. controlMask, xK_p) , passGeneratePrompt xpconfig)
|
||||||
|
-- > , ((modMask .|. shiftMask, xK_p) , passEditPrompt xpconfig)
|
||||||
-- > , ((modMask .|. controlMask .|. shiftMask, xK_p), passRemovePrompt xpconfig)
|
-- > , ((modMask .|. controlMask .|. shiftMask, xK_p), passRemovePrompt xpconfig)
|
||||||
--
|
--
|
||||||
-- For detailed instructions on:
|
-- For detailed instructions on:
|
||||||
@@ -112,6 +127,11 @@ mkPassPrompt promptLabel passwordFunction xpconfig = do
|
|||||||
passPrompt :: XPConfig -> X ()
|
passPrompt :: XPConfig -> X ()
|
||||||
passPrompt = mkPassPrompt "Select password" selectPassword
|
passPrompt = mkPassPrompt "Select password" selectPassword
|
||||||
|
|
||||||
|
-- | A prompt to retrieve a OTP from a given entry.
|
||||||
|
--
|
||||||
|
passOTPPrompt :: XPConfig -> X ()
|
||||||
|
passOTPPrompt = mkPassPrompt "Select OTP" selectOTP
|
||||||
|
|
||||||
-- | A prompt to generate a password for a given entry.
|
-- | A prompt to generate a password for a given entry.
|
||||||
-- This can be used to override an already stored entry.
|
-- This can be used to override an already stored entry.
|
||||||
-- (Beware that no confirmation is asked)
|
-- (Beware that no confirmation is asked)
|
||||||
@@ -119,6 +139,13 @@ passPrompt = mkPassPrompt "Select password" selectPassword
|
|||||||
passGeneratePrompt :: XPConfig -> X ()
|
passGeneratePrompt :: XPConfig -> X ()
|
||||||
passGeneratePrompt = mkPassPrompt "Generate password" generatePassword
|
passGeneratePrompt = mkPassPrompt "Generate password" generatePassword
|
||||||
|
|
||||||
|
-- | A prompt to generate a password for a given entry and immediately copy it
|
||||||
|
-- to the clipboard. This can be used to override an already stored entry.
|
||||||
|
-- (Beware that no confirmation is asked)
|
||||||
|
--
|
||||||
|
passGenerateAndCopyPrompt :: XPConfig -> X ()
|
||||||
|
passGenerateAndCopyPrompt = mkPassPrompt "Generate and copy password" generateAndCopyPassword
|
||||||
|
|
||||||
-- | A prompt to remove a password for a given entry.
|
-- | A prompt to remove a password for a given entry.
|
||||||
-- (Beware that no confirmation is asked)
|
-- (Beware that no confirmation is asked)
|
||||||
--
|
--
|
||||||
@@ -131,22 +158,45 @@ passRemovePrompt = mkPassPrompt "Remove password" removePassword
|
|||||||
passTypePrompt :: XPConfig -> X ()
|
passTypePrompt :: XPConfig -> X ()
|
||||||
passTypePrompt = mkPassPrompt "Type password" typePassword
|
passTypePrompt = mkPassPrompt "Type password" typePassword
|
||||||
|
|
||||||
|
-- | A prompt to edit a given entry.
|
||||||
|
-- This doesn't touch the clipboard.
|
||||||
|
--
|
||||||
|
passEditPrompt :: XPConfig -> X ()
|
||||||
|
passEditPrompt = mkPassPrompt "Edit password" editPassword
|
||||||
|
|
||||||
-- | Select a password.
|
-- | Select a password.
|
||||||
--
|
--
|
||||||
selectPassword :: String -> X ()
|
selectPassword :: String -> X ()
|
||||||
selectPassword passLabel = spawn $ "pass --clip \"" ++ escapeQuote passLabel ++ "\""
|
selectPassword passLabel = spawn $ "pass --clip \"" ++ escapeQuote passLabel ++ "\""
|
||||||
|
|
||||||
|
-- | Select a OTP.
|
||||||
|
--
|
||||||
|
selectOTP :: String -> X ()
|
||||||
|
selectOTP passLabel = spawn $ "pass otp --clip \"" ++ escapeQuote passLabel ++ "\""
|
||||||
|
|
||||||
-- | Generate a 30 characters password for a given entry.
|
-- | Generate a 30 characters password for a given entry.
|
||||||
-- If the entry already exists, it is updated with a new password.
|
-- If the entry already exists, it is updated with a new password.
|
||||||
--
|
--
|
||||||
generatePassword :: String -> X ()
|
generatePassword :: String -> X ()
|
||||||
generatePassword passLabel = spawn $ "pass generate --force \"" ++ escapeQuote passLabel ++ "\" 30"
|
generatePassword passLabel = spawn $ "pass generate --force \"" ++ escapeQuote passLabel ++ "\" 30"
|
||||||
|
|
||||||
|
-- | Generate a 30 characters password for a given entry.
|
||||||
|
-- If the entry already exists, it is updated with a new password.
|
||||||
|
-- After generating the password, it is copied to the clipboard.
|
||||||
|
--
|
||||||
|
generateAndCopyPassword :: String -> X ()
|
||||||
|
generateAndCopyPassword passLabel = spawn $ "pass generate --force -c \"" ++ escapeQuote passLabel ++ "\" 30"
|
||||||
|
|
||||||
-- | Remove a password stored for a given entry.
|
-- | Remove a password stored for a given entry.
|
||||||
--
|
--
|
||||||
removePassword :: String -> X ()
|
removePassword :: String -> X ()
|
||||||
removePassword passLabel = spawn $ "pass rm --force \"" ++ escapeQuote passLabel ++ "\""
|
removePassword passLabel = spawn $ "pass rm --force \"" ++ escapeQuote passLabel ++ "\""
|
||||||
|
|
||||||
|
-- | Edit a password stored for a given entry.
|
||||||
|
--
|
||||||
|
editPassword :: String -> X ()
|
||||||
|
editPassword passLabel = spawn $ "pass edit \"" ++ escapeQuote passLabel ++ "\""
|
||||||
|
|
||||||
-- | Type a password stored for a given entry using xdotool.
|
-- | Type a password stored for a given entry using xdotool.
|
||||||
--
|
--
|
||||||
typePassword :: String -> X ()
|
typePassword :: String -> X ()
|
||||||
@@ -163,6 +213,7 @@ escapeQuote = concatMap escape
|
|||||||
getPasswords :: FilePath -> IO [String]
|
getPasswords :: FilePath -> IO [String]
|
||||||
getPasswords passwordStoreDir = do
|
getPasswords passwordStoreDir = do
|
||||||
files <- runProcessWithInput "find" [
|
files <- runProcessWithInput "find" [
|
||||||
|
"-L", -- Traverse symlinks
|
||||||
passwordStoreDir,
|
passwordStoreDir,
|
||||||
"-type", "f",
|
"-type", "f",
|
||||||
"-name", "*.gpg",
|
"-name", "*.gpg",
|
||||||
|
263
XMonad/Util/ExclusiveScratchpads.hs
Normal file
263
XMonad/Util/ExclusiveScratchpads.hs
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
{-# LANGUAGE LambdaCase #-}
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : XMonad.Util.ExclusiveScratchpads
|
||||||
|
-- Copyright : Bruce Forte (2017)
|
||||||
|
-- License : BSD-style (see LICENSE)
|
||||||
|
--
|
||||||
|
-- Maintainer : Bruce Forte
|
||||||
|
-- Stability : unstable
|
||||||
|
-- Portability : unportable
|
||||||
|
--
|
||||||
|
-- Named scratchpads that can be mutually exclusive.
|
||||||
|
--
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
module XMonad.Util.ExclusiveScratchpads (
|
||||||
|
-- * Usage
|
||||||
|
-- $usage
|
||||||
|
mkXScratchpads,
|
||||||
|
xScratchpadsManageHook,
|
||||||
|
-- * Keyboard related
|
||||||
|
scratchpadAction,
|
||||||
|
hideAll,
|
||||||
|
resetExclusiveSp,
|
||||||
|
-- * Mouse related
|
||||||
|
setNoexclusive,
|
||||||
|
resizeNoexclusive,
|
||||||
|
floatMoveNoexclusive,
|
||||||
|
-- * Types
|
||||||
|
ExclusiveScratchpad(..),
|
||||||
|
ExclusiveScratchpads,
|
||||||
|
-- * Hooks
|
||||||
|
nonFloating,
|
||||||
|
defaultFloating,
|
||||||
|
customFloating
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Control.Applicative ((<$>))
|
||||||
|
import Control.Monad ((<=<),filterM,liftM2)
|
||||||
|
import Data.Monoid (appEndo)
|
||||||
|
import XMonad
|
||||||
|
import XMonad.Actions.Minimize
|
||||||
|
import XMonad.Actions.TagWindows (addTag,delTag)
|
||||||
|
import XMonad.Hooks.ManageHelpers (doRectFloat,isInProperty)
|
||||||
|
|
||||||
|
import qualified XMonad.StackSet as W
|
||||||
|
|
||||||
|
-- $usage
|
||||||
|
--
|
||||||
|
-- For this module to work properly, you need to use "XMonad.Layout.BoringWindows" and
|
||||||
|
-- "XMonad.Layout.Minimize", please refer to the documentation of these modules for more
|
||||||
|
-- information on how to configure them.
|
||||||
|
--
|
||||||
|
-- To use this module, put the following in your @~\/.xmonad\/xmonad.hs@:
|
||||||
|
--
|
||||||
|
-- > import XMonad.Utils.ExclusiveScratchpads
|
||||||
|
-- > import XMonad.ManageHook (title,appName)
|
||||||
|
-- > import qualified XMonad.StackSet as W
|
||||||
|
--
|
||||||
|
-- Add exclusive scratchpads, for example:
|
||||||
|
--
|
||||||
|
-- > exclusiveSps = mkXScratchpads [ ("htop", "urxvt -name htop -e htop", title =? "htop")
|
||||||
|
-- > , ("xclock", "xclock", appName =? "xclock")
|
||||||
|
-- > ] $ customFloating $ W.RationalRect (1/4) (1/4) (1/2) (1/2)
|
||||||
|
--
|
||||||
|
-- The scratchpads don\'t have to be exclusive, you can create them like this (see 'ExclusiveScratchpad'):
|
||||||
|
--
|
||||||
|
-- > regularSps = [ XSP "term" "urxvt -name scratchpad" (appName =? "scratchpad") defaultFloating [] ]
|
||||||
|
--
|
||||||
|
-- Create a list that contains all your scratchpads like this:
|
||||||
|
--
|
||||||
|
-- > scratchpads = exclusiveSps ++ regularSps
|
||||||
|
--
|
||||||
|
-- Add the hooks to your managehook (see "XMonad.Doc.Extending#Editing_the_manage_hook"), eg.:
|
||||||
|
--
|
||||||
|
-- > manageHook = myManageHook <+> xScratchpadsManageHook scratchpads
|
||||||
|
--
|
||||||
|
-- And finally add some keybindings (see "XMonad.Doc.Extending#Editing_key_bindings"):
|
||||||
|
--
|
||||||
|
-- > , ((modMask, xK_h), scratchpadAction scratchpads "htop")
|
||||||
|
-- > , ((modMask, xK_c), scratchpadAction scratchpads "xclock")
|
||||||
|
-- > , ((modMask, xK_t), scratchpadAction scratchpads "term")
|
||||||
|
-- > , ((modMask, xK_h), hideAll scratchpads)
|
||||||
|
--
|
||||||
|
-- Now you can get your scratchpads by pressing the corresponding keys, if you
|
||||||
|
-- have the @htop@ scratchpad on your current screen and you fetch the @xclock@
|
||||||
|
-- scratchpad then @htop@ gets hidden.
|
||||||
|
--
|
||||||
|
-- If you move a scratchpad it still gets hidden when you fetch a scratchpad of
|
||||||
|
-- the same family, to change that behaviour and make windows not exclusive
|
||||||
|
-- anymore when they get resized or moved add these mouse bindings
|
||||||
|
-- (see "XMonad.Doc.Extending#Editing_mouse_bindings"):
|
||||||
|
--
|
||||||
|
-- > , ((mod4Mask, button1), floatMoveNoexclusive scratchpads)
|
||||||
|
-- > , ((mod4Mask, button3), resizeNoexclusive scratchpads)
|
||||||
|
--
|
||||||
|
-- To reset a moved scratchpad to the original position that you set with its hook,
|
||||||
|
-- call @resetExclusiveSp@ when it is in focus. For example if you want to extend
|
||||||
|
-- Mod-Return to reset the placement when a scratchpad is in focus but keep the
|
||||||
|
-- default behaviour for tiled windows, set these key bindings:
|
||||||
|
--
|
||||||
|
-- > , ((modMask, xK_Return), windows W.swapMaster >> resetExclusiveSp scratchpads)
|
||||||
|
--
|
||||||
|
-- __Note:__ This is just an example, in general you can add more than two
|
||||||
|
-- exclusive scratchpads and multiple families of such.
|
||||||
|
|
||||||
|
data ExclusiveScratchpad = XSP { name :: String -- ^ Name of the scratchpad
|
||||||
|
, cmd :: String -- ^ Command to spawn the scratchpad
|
||||||
|
, query :: Query Bool -- ^ Query to match the scratchpad
|
||||||
|
, hook :: ManageHook -- ^ Hook to specify the placement policy
|
||||||
|
, exclusive :: [String] -- ^ Names of exclusive scratchpads
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExclusiveScratchpads = [ExclusiveScratchpad]
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | Create 'ExclusiveScratchpads' from @[(name,cmd,query)]@ with a common @hook@
|
||||||
|
mkXScratchpads :: [(String,String,Query Bool)] -- ^ List of @(name,cmd,query)@ of the
|
||||||
|
-- exclusive scratchpads
|
||||||
|
-> ManageHook -- ^ The common @hook@ that they use
|
||||||
|
-> ExclusiveScratchpads
|
||||||
|
mkXScratchpads xs h = foldl accumulate [] xs
|
||||||
|
where
|
||||||
|
accumulate a (n,c,q) = XSP n c q h (filter (n/=) names) : a
|
||||||
|
names = map (\(n,_,_) -> n) xs
|
||||||
|
|
||||||
|
-- | Create 'ManageHook' from 'ExclusiveScratchpads'
|
||||||
|
xScratchpadsManageHook :: ExclusiveScratchpads -- ^ List of exclusive scratchpads from
|
||||||
|
-- which a 'ManageHook' should be generated
|
||||||
|
-> ManageHook
|
||||||
|
xScratchpadsManageHook = composeAll . fmap (\sp -> query sp --> hook sp)
|
||||||
|
|
||||||
|
-- | Pop up/hide the scratchpad by name and possibly hide its exclusive
|
||||||
|
scratchpadAction :: ExclusiveScratchpads -- ^ List of exclusive scratchpads
|
||||||
|
-> String -- ^ Name of the scratchpad to toggle
|
||||||
|
-> X ()
|
||||||
|
scratchpadAction xs n =
|
||||||
|
let ys = filter ((n==).name) xs in
|
||||||
|
|
||||||
|
case ys of [] -> return ()
|
||||||
|
(sp:_) -> let q = query sp in withWindowSet $ \s -> do
|
||||||
|
ws <- filterM (runQuery q) $ W.allWindows s
|
||||||
|
|
||||||
|
case ws of [] -> do spawn (cmd sp)
|
||||||
|
hideOthers xs n
|
||||||
|
windows W.shiftMaster
|
||||||
|
|
||||||
|
(w:_) -> do toggleWindow w
|
||||||
|
whenX (runQuery isExclusive w) (hideOthers xs n)
|
||||||
|
where
|
||||||
|
toggleWindow w = liftM2 (&&) (runQuery isMaximized w) (onCurrentScreen w) >>= \case
|
||||||
|
True -> whenX (onCurrentScreen w) (minimizeWindow w)
|
||||||
|
False -> do windows (flip W.shiftWin w =<< W.currentTag)
|
||||||
|
maximizeWindowAndFocus w
|
||||||
|
windows W.shiftMaster
|
||||||
|
|
||||||
|
onCurrentScreen w = withWindowSet (return . elem w . currentWindows)
|
||||||
|
|
||||||
|
-- | Hide all 'ExclusiveScratchpads' on the current screen
|
||||||
|
hideAll :: ExclusiveScratchpads -- ^ List of exclusive scratchpads
|
||||||
|
-> X ()
|
||||||
|
hideAll xs = mapWithCurrentScreen q minimizeWindow
|
||||||
|
where q = joinQueries (map query xs) <&&> isExclusive <&&> isMaximized
|
||||||
|
|
||||||
|
-- | If the focused window is a scratchpad, the scratchpad gets reset to the original
|
||||||
|
-- placement specified with the hook and becomes exclusive again
|
||||||
|
resetExclusiveSp :: ExclusiveScratchpads -- ^ List of exclusive scratchpads
|
||||||
|
-> X ()
|
||||||
|
resetExclusiveSp xs = withFocused $ \w -> whenX (isScratchpad xs w) $ do
|
||||||
|
let ys = filterM (flip runQuery w . query) xs
|
||||||
|
|
||||||
|
unlessX (null <$> ys) $ do
|
||||||
|
mh <- (head . map hook) <$> ys -- ys /= [], so `head` is fine
|
||||||
|
n <- (head . map name) <$> ys -- same
|
||||||
|
|
||||||
|
(windows . appEndo <=< runQuery mh) w
|
||||||
|
hideOthers xs n
|
||||||
|
delTag "_XSP_NOEXCLUSIVE" w
|
||||||
|
|
||||||
|
where unlessX = whenX . fmap not
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | Hide the scratchpad of the same family by name if it's on the focused workspace
|
||||||
|
hideOthers :: ExclusiveScratchpads -> String -> X ()
|
||||||
|
hideOthers xs n =
|
||||||
|
let ys = concatMap exclusive $ filter ((n==).name) xs
|
||||||
|
qs = map query $ filter ((`elem` ys).name) xs
|
||||||
|
q = joinQueries qs <&&> isExclusive <&&> isMaximized in
|
||||||
|
|
||||||
|
mapWithCurrentScreen q minimizeWindow
|
||||||
|
|
||||||
|
-- | Conditionally map a function on all windows of the current screen
|
||||||
|
mapWithCurrentScreen :: Query Bool -> (Window -> X ()) -> X ()
|
||||||
|
mapWithCurrentScreen q f = withWindowSet $ \s -> do
|
||||||
|
ws <- filterM (runQuery q) $ currentWindows s
|
||||||
|
mapM_ f ws
|
||||||
|
|
||||||
|
-- | Extract all windows on the current screen from a StackSet
|
||||||
|
currentWindows :: W.StackSet i l a sid sd -> [a]
|
||||||
|
currentWindows = W.integrate' . W.stack . W.workspace . W.current
|
||||||
|
|
||||||
|
-- | Check if given window is a scratchpad
|
||||||
|
isScratchpad :: ExclusiveScratchpads -> Window -> X Bool
|
||||||
|
isScratchpad xs w = withWindowSet $ \s -> do
|
||||||
|
let q = joinQueries $ map query xs
|
||||||
|
|
||||||
|
ws <- filterM (runQuery q) $ W.allWindows s
|
||||||
|
return $ elem w ws
|
||||||
|
|
||||||
|
-- | Build a disjunction from a list of clauses
|
||||||
|
joinQueries :: [Query Bool] -> Query Bool
|
||||||
|
joinQueries = foldl (<||>) (liftX $ return False)
|
||||||
|
|
||||||
|
-- | Useful queries
|
||||||
|
isExclusive, isMaximized :: Query Bool
|
||||||
|
isExclusive = (notElem "_XSP_NOEXCLUSIVE" . words) <$> stringProperty "_XMONAD_TAGS"
|
||||||
|
isMaximized = not <$> isInProperty "_NET_WM_STATE" "_NET_WM_STATE_HIDDEN"
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | Make a window not exclusive anymore
|
||||||
|
setNoexclusive :: ExclusiveScratchpads -- ^ List of exclusive scratchpads
|
||||||
|
-> Window -- ^ Window which should be made not
|
||||||
|
-- exclusive anymore
|
||||||
|
-> X ()
|
||||||
|
setNoexclusive xs w = whenX (isScratchpad xs w) $ addTag "_XSP_NOEXCLUSIVE" w
|
||||||
|
|
||||||
|
-- | Float and drag the window, make it not exclusive anymore
|
||||||
|
floatMoveNoexclusive :: ExclusiveScratchpads -- ^ List of exclusive scratchpads
|
||||||
|
-> Window -- ^ Window which should be moved
|
||||||
|
-> X ()
|
||||||
|
floatMoveNoexclusive xs w = setNoexclusive xs w
|
||||||
|
>> focus w
|
||||||
|
>> mouseMoveWindow w
|
||||||
|
>> windows W.shiftMaster
|
||||||
|
|
||||||
|
-- | Resize window, make it not exclusive anymore
|
||||||
|
resizeNoexclusive :: ExclusiveScratchpads -- ^ List of exclusive scratchpads
|
||||||
|
-> Window -- ^ Window which should be resized
|
||||||
|
-> X ()
|
||||||
|
resizeNoexclusive xs w = setNoexclusive xs w
|
||||||
|
>> focus w
|
||||||
|
>> mouseResizeWindow w
|
||||||
|
>> windows W.shiftMaster
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | Manage hook that makes the window non-floating
|
||||||
|
nonFloating :: ManageHook
|
||||||
|
nonFloating = idHook
|
||||||
|
|
||||||
|
-- | Manage hook that makes the window floating with the default placement
|
||||||
|
defaultFloating :: ManageHook
|
||||||
|
defaultFloating = doFloat
|
||||||
|
|
||||||
|
-- | Manage hook that makes the window floating with custom placement
|
||||||
|
customFloating :: W.RationalRect -- ^ @RationalRect x y w h@ that specifies relative position,
|
||||||
|
-- height and width (see "XMonad.StackSet#RationalRect")
|
||||||
|
-> ManageHook
|
||||||
|
customFloating = doRectFloat
|
@@ -37,7 +37,7 @@ data Placement = OffsetLeft Int Int -- ^ An exact amount of pixels from the up
|
|||||||
-- In the module we suppose that those matrices are represented as [[Bool]],
|
-- In the module we suppose that those matrices are represented as [[Bool]],
|
||||||
-- so the lengths of the inner lists must be the same.
|
-- so the lengths of the inner lists must be the same.
|
||||||
--
|
--
|
||||||
-- See "Xmonad.Layout.Decoration" for usage examples
|
-- See "XMonad.Layout.Decoration" for usage examples
|
||||||
|
|
||||||
-- | Gets the ('width', 'height') of an image
|
-- | Gets the ('width', 'height') of an image
|
||||||
imageDims :: [[Bool]] -> (Int, Int)
|
imageDims :: [[Bool]] -> (Int, Int)
|
||||||
|
@@ -23,6 +23,7 @@ module XMonad.Util.Invisible (
|
|||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
|
import Control.Monad.Fail
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- A wrapper data type to store layout state that shouldn't be persisted across
|
-- A wrapper data type to store layout state that shouldn't be persisted across
|
||||||
@@ -30,10 +31,10 @@ import Control.Applicative
|
|||||||
-- Invisible derives trivial definitions for Read and Show, so the wrapped data
|
-- Invisible derives trivial definitions for Read and Show, so the wrapped data
|
||||||
-- type need not do so.
|
-- type need not do so.
|
||||||
|
|
||||||
newtype Invisible m a = I (m a) deriving (Monad, Applicative, Functor)
|
newtype Invisible m a = I (m a) deriving (Monad, MonadFail, Applicative, Functor)
|
||||||
|
|
||||||
instance (Functor m, Monad m) => Read (Invisible m a) where
|
instance (Functor m, Monad m, MonadFail m) => Read (Invisible m a) where
|
||||||
readsPrec _ s = [(fail "Read Invisible", s)]
|
readsPrec _ s = [(Control.Monad.Fail.fail "Read Invisible", s)]
|
||||||
|
|
||||||
instance Monad m => Show (Invisible m a) where
|
instance Monad m => Show (Invisible m a) where
|
||||||
show _ = ""
|
show _ = ""
|
||||||
|
@@ -19,6 +19,8 @@ module XMonad.Util.Themes
|
|||||||
, ppThemeInfo
|
, ppThemeInfo
|
||||||
, xmonadTheme
|
, xmonadTheme
|
||||||
, smallClean
|
, smallClean
|
||||||
|
, adwaitaTheme
|
||||||
|
, adwaitaDarkTheme
|
||||||
, robertTheme
|
, robertTheme
|
||||||
, darkTheme
|
, darkTheme
|
||||||
, deiflTheme
|
, deiflTheme
|
||||||
@@ -91,6 +93,8 @@ ppThemeInfo t = themeName t <> themeDescription t <> "by" <> themeAuthor t
|
|||||||
listOfThemes :: [ThemeInfo]
|
listOfThemes :: [ThemeInfo]
|
||||||
listOfThemes = [ xmonadTheme
|
listOfThemes = [ xmonadTheme
|
||||||
, smallClean
|
, smallClean
|
||||||
|
, adwaitaTheme
|
||||||
|
, adwaitaDarkTheme
|
||||||
, darkTheme
|
, darkTheme
|
||||||
, deiflTheme
|
, deiflTheme
|
||||||
, oxymor00nTheme
|
, oxymor00nTheme
|
||||||
@@ -132,6 +136,48 @@ smallClean =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- | Matching decorations for Adwaita GTK theme
|
||||||
|
adwaitaTheme :: ThemeInfo
|
||||||
|
adwaitaTheme =
|
||||||
|
newTheme { themeName = "adwaitaTheme"
|
||||||
|
, themeAuthor = "Alex Griffin"
|
||||||
|
, themeDescription = "Matching decorations for Adwaita GTK theme"
|
||||||
|
, theme = def { activeColor = "#dfdcd8"
|
||||||
|
, inactiveColor = "#f6f5f4"
|
||||||
|
, urgentColor = "#3584e4"
|
||||||
|
, activeBorderColor = "#bfb8b1"
|
||||||
|
, inactiveBorderColor = "#cdc7c2"
|
||||||
|
, urgentBorderColor = "#1658a7"
|
||||||
|
, activeTextColor = "#2e3436"
|
||||||
|
, inactiveTextColor = "#929595"
|
||||||
|
, urgentTextColor = "#ffffff"
|
||||||
|
, fontName = "xft:Cantarell:bold:size=11"
|
||||||
|
, decoWidth = 400
|
||||||
|
, decoHeight = 35
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | Matching decorations for Adwaita-dark GTK theme
|
||||||
|
adwaitaDarkTheme :: ThemeInfo
|
||||||
|
adwaitaDarkTheme =
|
||||||
|
newTheme { themeName = "adwaitaDarkTheme"
|
||||||
|
, themeAuthor = "Alex Griffin"
|
||||||
|
, themeDescription = "Matching decorations for Adwaita-dark GTK theme"
|
||||||
|
, theme = def { activeColor = "#2d2d2d"
|
||||||
|
, inactiveColor = "#353535"
|
||||||
|
, urgentColor = "#15539e"
|
||||||
|
, activeBorderColor = "#070707"
|
||||||
|
, inactiveBorderColor = "#1c1c1c"
|
||||||
|
, urgentBorderColor = "#030c17"
|
||||||
|
, activeTextColor = "#eeeeec"
|
||||||
|
, inactiveTextColor = "#929291"
|
||||||
|
, urgentTextColor = "#ffffff"
|
||||||
|
, fontName = "xft:Cantarell:bold:size=11"
|
||||||
|
, decoWidth = 400
|
||||||
|
, decoHeight = 35
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
-- | Don's preferred colors - from DynamicLog...;)
|
-- | Don's preferred colors - from DynamicLog...;)
|
||||||
donaldTheme :: ThemeInfo
|
donaldTheme :: ThemeInfo
|
||||||
donaldTheme =
|
donaldTheme =
|
||||||
|
@@ -1,3 +1,2 @@
|
|||||||
packages: ./
|
packages:
|
||||||
../xmonad/
|
xmonad-contrib.cabal
|
||||||
../x11/
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
name: xmonad-contrib
|
name: xmonad-contrib
|
||||||
version: 0.15
|
version: 0.16
|
||||||
homepage: http://xmonad.org/
|
homepage: http://xmonad.org/
|
||||||
synopsis: Third party extensions for xmonad
|
synopsis: Third party extensions for xmonad
|
||||||
description:
|
description:
|
||||||
@@ -36,7 +36,7 @@ cabal-version: >= 1.6
|
|||||||
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==7.8.4, GHC==7.10.3, GHC==8.0.1, GHC==8.2.2, GHC==8.4.3, GHC==8.6.1
|
tested-with: GHC==8.0.2, GHC==8.2.2, GHC==8.4.4, GHC==8.6.5, GHC==8.8.1
|
||||||
|
|
||||||
source-repository head
|
source-repository head
|
||||||
type: git
|
type: git
|
||||||
@@ -52,7 +52,7 @@ flag testing
|
|||||||
default: False
|
default: False
|
||||||
|
|
||||||
library
|
library
|
||||||
build-depends: base >= 4.5 && < 5,
|
build-depends: base >= 4.9 && < 5,
|
||||||
bytestring >= 0.10 && < 0.11,
|
bytestring >= 0.10 && < 0.11,
|
||||||
containers >= 0.5 && < 0.7,
|
containers >= 0.5 && < 0.7,
|
||||||
directory,
|
directory,
|
||||||
@@ -181,6 +181,7 @@ library
|
|||||||
XMonad.Hooks.Minimize
|
XMonad.Hooks.Minimize
|
||||||
XMonad.Hooks.Place
|
XMonad.Hooks.Place
|
||||||
XMonad.Hooks.PositionStoreHooks
|
XMonad.Hooks.PositionStoreHooks
|
||||||
|
XMonad.Hooks.RefocusLast
|
||||||
XMonad.Hooks.RestoreMinimized
|
XMonad.Hooks.RestoreMinimized
|
||||||
XMonad.Hooks.ScreenCorners
|
XMonad.Hooks.ScreenCorners
|
||||||
XMonad.Hooks.Script
|
XMonad.Hooks.Script
|
||||||
@@ -285,6 +286,7 @@ library
|
|||||||
XMonad.Layout.ToggleLayouts
|
XMonad.Layout.ToggleLayouts
|
||||||
XMonad.Layout.TrackFloating
|
XMonad.Layout.TrackFloating
|
||||||
XMonad.Layout.TwoPane
|
XMonad.Layout.TwoPane
|
||||||
|
XMonad.Layout.TwoPanePersistent
|
||||||
XMonad.Layout.WindowArranger
|
XMonad.Layout.WindowArranger
|
||||||
XMonad.Layout.WindowNavigation
|
XMonad.Layout.WindowNavigation
|
||||||
XMonad.Layout.WindowSwitcherDecoration
|
XMonad.Layout.WindowSwitcherDecoration
|
||||||
@@ -316,6 +318,7 @@ library
|
|||||||
XMonad.Util.Dmenu
|
XMonad.Util.Dmenu
|
||||||
XMonad.Util.Dzen
|
XMonad.Util.Dzen
|
||||||
XMonad.Util.EZConfig
|
XMonad.Util.EZConfig
|
||||||
|
XMonad.Util.ExclusiveScratchpads
|
||||||
XMonad.Util.ExtensibleState
|
XMonad.Util.ExtensibleState
|
||||||
XMonad.Util.Font
|
XMonad.Util.Font
|
||||||
XMonad.Util.Image
|
XMonad.Util.Image
|
||||||
|
Reference in New Issue
Block a user